ElasticSearch with 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
そこにも運がない。 Tireを使用して関連するActiveRecordオブジェクトのインデックス作成と検索の優れた明確な例を誰かが持っている場合、それはここのナレッジベースへの本当に良い追加になると思われます。
アイデアや貢献をありがとう。
TireでのActiveRecordアソシエーションのサポートは機能していますが、アプリケーション内でいくつかの調整が必要です。図書館がここでより良い仕事をするべきであることは間違いありません、そして将来それは確かにそうなるでしょう。
そうは言っても、elasticsearchでRailsのアソシエーションを操作するためのタイヤ構成の本格的な例を次に示します。 active_record_associations.rb
ここでいくつか強調しておきます。
まず、関連付けの変更について、関連付けの親モデルに通知する必要があります。
Chapter
に「属する」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
通知をそれ自体でインターセプトする必要があり、これを強制することはできません。一方、目を傷つけない方法でライブラリの制限を回避するのがいかに簡単であるかの証拠。)
READMEは、Rails <3.1で自動「JSONにルートキーを追加する」を無効にする必要があると述べていますが、多くの人はそれを忘れているので、含める必要がありますクラス定義でも:
self.include_root_in_json = false
今、私たちの仕事の要点が来ます-私たちのドキュメント(モデル)の適切なマッピングを定義します:
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
を「日付」、関連モデルの章テキストをインデックス付けしていることに注意してください。すべてのデータは、elasticsearchで単一のドキュメントとして効果的に「非正規化」されます(そのような用語がわずかに意味がある場合)。
最後のステップとして、elasticsearchインデックスでドキュメントを適切にシリアル化する必要があります。 ActiveRecordの便利なto_json
メソッドをどのように活用できるかに注目してください。
def to_indexed_json
to_json( include: { chapters: { only: [:text] } } )
end
このすべての設定が整ったら、ドキュメントのBook
とChapter
の両方の部分のプロパティを検索できます。
active_record_associations.rb Rubyファイルを最初にリンクして実行して、全体像を確認してください。
詳細については、次のリソースを参照してください。
mapping
/to_indexed_json
の相互作用の詳細については、次のStackOverflowの回答を参照してください: ElasticSearch&Tire:Mappingとto_indexed_jsonの使用 。
このStackOverflowの回答を参照してください: ElasticSearch(Tire + ActiveRecord)のメソッドの結果にインデックスを付ける 関連のあるモデルにインデックスを付けるときにn +1クエリと戦う方法を確認してください。
私はこれを私のアプリケーションの1つでソリューションとして作成しました。これは、深くネストされたモデルのセットにインデックスを付けます。
https://Gist.github.com/paulnsorensen/4744475
更新:これを行うgemをリリースしました: https://github.com/paulnsorensen/lifesaver