28

私はタイヤで ElasticSearch を使用して、いくつかの 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 オブジェクトにインデックスを付けて検索する明確で明確な例を誰かが持っている場合、それはここのナレッジ ベースへの本当に良い追加になるようです。

アイデアや貢献に感謝します。

4

2 に答える 2

52

Tire での ActiveRecord 関連付けのサポートは機能していますが、アプリケーション内でいくつか調整が必要です。図書館がここでより良い仕事をするべきであることは疑いの余地がなく、将来的には確実にそうなるでしょう。

とは言うものの、elasticsearch で Rails のアソシエーションを操作するための Tire 構成の本格的な例を以下に示します: active_record_associations.rb

ここでいくつかのことを強調しましょう。

親に触れる

まず、関連付けの変更について関連付けの親モデルに通知する必要があります。

aChapterに「属する」モデルがあるとするとBook、次のことを行う必要があります。

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

このようにして、次のようなことをすると:

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

bookチャプタが追加されたことをインスタンスに通知します。

タッチへの対応

この部分をソートしたら、変更についてタイヤに通知し、それに応じてelasticsearchインデックスを更新する必要があります。

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

( Tireが自身で通知をインターセプトする必要があり、これを強制してはならないことに疑問の余地after_touchはありません。一方で、目を痛めない方法でライブラリの制限を回避することがいかに簡単かを示す証拠でもあります。 .)

Rails < 3.1 での適切な JSON シリアライゼーション

Rails < 3.1 では自動 "adding root key in JSON" を無効にする必要があると README に記載されていますが、多くの人がそれを忘れているため、クラス定義にも含める必要があります。

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、関連するモデルの章のテキストにインデックスを付けていることに注意してください。すべてのデータは、elasticsearch の単一のドキュメントとして効果的に「非正規化」されます (そのような用語が少し意味をなす場合)。

適切なドキュメントの JSON シリアル化

最後のステップとして、elasticsearch インデックスでドキュメントを適切にシリアル化する必要があります。ActiveRecordの便利to_jsonなメソッドをどのように活用できるかに注目してください。

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

このすべての設定が整ったら、ドキュメントBookの とChapter部分の両方でプロパティを検索できます。

全体像を確認するには、最初にリンクされたactive_record_associations.rb Ruby ファイルを実行してください。

詳細については、次のリソースを参照してください。

/ interplayの詳細については、この StackOverflow の回答: ElasticSearch & Tire: Using Mapping and to_indexed_jsonを参照してください。mappingto_indexed_json

この StackOverflow の回答を参照してください: ElasticSearch (Tire + ActiveRecord) のメソッドの結果にインデックスを付けて、関連付けのあるモデルにインデックスを付けるときに n+1 クエリと戦う方法を確認してください。

于 2012-07-29T17:42:13.160 に答える
3

深くネストされた一連のモデルにインデックスを付ける、アプリケーションの 1 つのソリューションとしてこれを作成しました

https://gist.github.com/paulnsorensen/4744475

更新: これを行う宝石をリリースしました: https://github.com/paulnsorensen/lifesaver

于 2013-02-09T07:26:51.720 に答える