Подтвердить что ты не робот

Elasticsearch, Tire и вложенные запросы/ассоциации с ActiveRecord

Я использую ElasticSearch с Tire для индексации и поиска некоторых моделей ActiveRecord, и я искал "правильный" способ индексирования и поиска. Я не нашел для себя лучшей практики, поэтому я хотел спросить, есть ли у кого подход, который, по их мнению, работает очень хорошо.

Как пример настройки (это составлено, но иллюстрирует проблему), скажем, у нас есть книга с главами. Каждая книга имеет название и автора, а также множество глав. В каждой главе есть текст. Мы хотим индексировать поля книги и текст глав, чтобы вы могли искать книгу по автору или любую книгу с определенными словами.

class Book < ActiveRecord::Base
  include Tire::Model::Search
  include Tire::Model::Callbacks

  has_many :chapters

  mapping do
    indexes :title, :analyzer => 'snowball', :boost => 100
    indexes :author, :analyzer => 'snowball'
    indexes :chapters, type: 'object', properties: {
      chapter_text: { type: 'string', analyzer: 'snowball' }
    }
  end
end

class Chapter < ActiveRecord::Base
  belongs_to :book
end

Итак, я выполняю поиск с помощью:

s = Book.search do
  query { string query_string }
end

Это не работает, хотя кажется, что это индексирование должно это сделать. Если вместо этого я индексирую:

indexes :chapters, :as => 'chapters.map{|c| c.chapter_text}.join('|'), :analyzer => 'snowball'

Это делает текст доступным для поиска, но, очевидно, это не хороший хак, и он теряет фактический связанный объект. Я пробовал варианты поиска, например:

s = Book.search do
  query do
    boolean do
      should { string query_string }
      should { string "chapters.chapter_text:#{query_string}" }
    end
  end
end

Не повезло и там. Если у кого-то есть хороший, ясный пример индексирования и поиска связанных объектов ActiveRecord с использованием Tire, похоже, это было бы действительно хорошим дополнением к базе знаний здесь.

Спасибо за любые идеи и вклады.

4b9b3361

Ответ 1

Поддержка ассоциаций ActiveRecord в Tire работает, но требует нескольких настроек в вашем приложении. Там нет сомнений, что библиотека должна работать лучше здесь, и в будущем это, безусловно, будет.

Итак, вот полноценный пример конфигурации Tire для работы с ассоциациями Rails в elasticsearch: active_record_associations.rb

Позвольте мне выделить пару вещей здесь.

Прикосновение к родительскому

Во-первых, вы должны убедиться, что вы сообщаете родительскую модель ассоциации об изменениях в ассоциации.

Учитывая, что у нас есть модель Chapter, которая "принадлежит" a Book, нам нужно сделать:

class Chapter < ActiveRecord::Base
  belongs_to :book, touch: true
end

Таким образом, когда мы делаем что-то вроде:

book.chapters.create text: "Lorem ipsum...."

Экземпляр Book уведомляется о добавленной главе.

Отвечая на вопросы

При сортировке этой части нам необходимо уведомить Tire об изменении и обновить индекс elasticsearch соответственно:

class Book < ActiveRecord::Base
  has_many :chapters
  after_touch() { tire.update_index }
end

(Нет сомнений, что Tire должен перехватывать уведомления after_touch сам по себе, а не заставлять вас делать это. С другой стороны, это завещание о том, как легко будет работать с ограничениями библиотеки в манере что не повредит вашим глазам.)

Соответствующая сериализация JSON в Rails < 3.1

Несмотря на упоминания README, вы должны отключить автоматическое "добавление корневого ключа в JSON" в Rails < 3.1, многие забывают об этом, поэтому вы должны включить его также в определение класса:

self.include_root_in_json = false

Надлежащее отображение для elasticsearch

Теперь идет мясо нашей работы - определение правильного отображения для наших документов (моделей):

mapping do
  indexes :title,      type: 'string', boost: 10, analyzer: 'snowball'
  indexes :created_at, type: 'date'

  indexes :chapters do
    indexes :text, analyzer: 'snowball'
  end
end

Обратите внимание, что мы индексируем title с повышением, created_at как "дата" и текст главы из связанной модели. Все данные эффективно "де-нормируются" как единый документ в поиске elastics (если такой термин имеет мало смысла).

Надлежащий документ Сериализация JSON

В качестве последнего шага нам необходимо правильно сериализовать документ в индексе elasticsearch. Обратите внимание, как мы можем использовать удобный метод to_json из ActiveRecord:

def to_indexed_json
  to_json( include: { chapters: { only: [:text] } } )
end

При всей этой настройке мы можем искать в свойствах как части Book, так и Chapter нашего документа.

Запустите active_record_associations.rb Файл Ruby, связанный в начале, чтобы увидеть полную картину.

Для получения дополнительной информации, пожалуйста, обратитесь к этим ресурсам:

См. этот ответ StackOverflow: ElasticSearch и Tire: использование сопоставления и to_indexed_json для получения дополнительной информации о взаимодействии mapping/to_indexed_json.

См. этот ответ StackOverflow: Индексируйте результаты метода в ElasticSearch (Tire + ActiveRecord), чтобы узнать, как бороться с n + 1 запросами при индексировании моделей с ассоциациями.