1

メッセージスレッドのSolrベースの検索を実装しようとしています。各メッセージには多くの返信を含めることができます(返信の深さは1レベルのみです)。検索キーに一致するコンテンツを含む親メッセージを取得するか、検索キーに一致する返信を取得したい。

例えば:

Hello Jack
  Hello Janice
  How are you?
  ..

I am Janice
  How are you?

Welcome to the Jungle
  Nothing better to do.

検索するJaniceと、次の結果セットが返されます。

Hello Jack # one of the child messages matches the key word
I am Janice # parent message matched the keyword)

私のモデルは次のとおりです。

class Message < ActiveRecord::Base    
  belongs_to :parent, :class_name => "Message"
  has_many   :replies, :class_name => "Message", :foreign_key => :parent_id      
  # content      
  searchable do
    text :content
    integer :parent_id
  end     
end

ネストされたサブクエリのような条件を指定するためのDSL構文は何ですか?

編集1

すべてのインデックスを保持するための複合テキストインデックスフィールドを作成することを検討しました。しかし、返信が特定の追加基準に一致することを確認する必要があるため、このアプローチは私のシナリオでは実行可能ではありません。

class Message < ActiveRecord::Base    
  belongs_to :parent, :class_name => "Message"
  has_many   :replies, :class_name => "Message", :foreign_key => :parent_id      
  belongs_to :category
  # content      
  searchable do
    text :content
    integer :category_id
    integer :parent_id
  end     
end

上記のモデルでは、テキスト検索を特定のカテゴリに制限したいと思います。

4

2 に答える 2

8

探していることを達成するための最良の方法は、返信の内容(および検索可能にしたいその他のフィールド)を親メッセージに非正規化することです。

これはSunspotで行うのは非常に簡単です。オンラインで調査する可能性のあるもう1つの一般的なシナリオは、コメントの内容に基づいてブログ投稿を検索することです。

ここで注意すべき重要な点の1つは、非正規化のafter_saveため、返信が追加または更新されたときに親のインデックスを再作成できるようにフックが必要になることです。

あなたの場合、変更は次のようになります…</ p>

class Message < ActiveRecord::Base    
  # …

  after_save :reindex_parent

  searchable do
    # …
    text :replies_content
  end

  def replies_content
    replies.collect(&:content).join(" ")
  end

  def reindex_parent
    parent.solr_index!
  end

end

(新しいメソッドを定義する代わりに数行を保存したい場合text :replies_contentは、インラインを受け入れることもできます。それはあなた次第です。)lambda

返信のすべてのコンテンツがデフォルトのキーワード検索にまとめられるため、このアプローチでは検索構文に実際の変更はありません。

より具体的なユースケースを念頭に置いている場合は、質問を明確にする必要がありますが、これは私にとって最良かつ最も単純なアプローチのようです。

最後にもう1つ注意してください。たとえば、メッセージに多くの返信がある場合、このアプローチは少し重くなる可能性があります。DelayedJobまたはResqueを使用して非同期的にインデックスを作成していることを確認することをお勧めします。しかし、それは別の会話です。

更新1:特定のcategory_idを使用したスコープ

まず第一に、私は各返信がcategory_idその親とは異なる可能性があると想定しています。また、言い換えると、親または返信テキストコンテンツに対してキーワードマッチングを実行し、カテゴリ別にスコープを設定する必要があります。

私が見るいくつかのオプションがあります。最も単純なものから始めて、次にいくつかの可能性のある組み合わせについて説明します。最も簡単なアプローチは、かなり基本的な検索を実行し(非正規化などについて心配する必要はありません)、ActiveRecordの関連付けを使用して親子メッセージを再構築することです。

@search = Message.search do
  keywords params[:q]
  with(:category_id, params[:category_id])
end
@messages = @search.results

ご覧のとおりcategory_id、Sunspotではスコーピングは非常に簡単です。これがあなたの質問の大部分である可能性があり、私はちょうど行って、それを必要以上に複雑にしました:)

そこから、それらのいくつか@messagesは親になり、いくつかは返信になります。どちらがどれであるかを判断し、それに応じてレンダリングすることは、確かにビューの能力の範囲内です。

<% if message.parent %>
  …

要件の正確な性質に応じて、ここには他にもいくつかのアプローチがあります。上記で十分かもしれないので、ここでは詳しく説明しません。ただし、非正規化を引き続き実行する場合は、メッセージのすべての応答に複数値の整数列を含めることもできますcategory_id。のようなものinteger :reply_category_ids, :multi => true

この後者のアプローチは、メッセージスレッド全体に対してより緩い一致を提供します。これは、アプリによっては、非正規化の複雑さの価値がある場合とない場合があります。構文はあなたにお任せします。ほとんどの場合、前の例から流れています。

ご覧のとおり、そのカテゴリに対してスコープを設定する時期と場所に応じて、ここにはいくつかの順列があります。うまくいけば、上記の例で、アプリの正確な詳細を理解するのに十分です。

于 2010-11-08T01:35:12.037 に答える
0

ニック、ありがとうございました。すべてのテーブルで任意のサブ文字列を使用してグローバル検索を有効にすると、問題を解決するのにヒントが役立ちました。私の場合、FKを使用して親レコードの属性を取得し、子テーブルで検索できるようにする必要があります。

searchable do
  ...
  text :ip_address,  as: :ip_address_textp # nested searching
  ...
end

private

def ip_address
  Address.find(address_id).ip # retrieve attribute from parent record with FK
end
于 2019-03-07T08:48:47.797 に答える