在SunSpot Solr中嵌套搜索

Har*_*tty 1 solr ruby-on-rails sunspot

我正在尝试实现基于Solr的搜索消息线程.每条消息都可以有很多回复(回复只能是一层深度).我想检索内容与搜索关键字匹配的父邮件或回复与搜索关键字匹配的内容.

例如:

Hello Jack
  Hello Janice
  How are you?
  ..

I am Janice
  How are you?

Welcome to the Jungle
  Nothing better to do.
Run Code Online (Sandbox Code Playgroud)

搜索Janice应该返回以下结果集:

Hello Jack # one of the child messages matches the key word
I am Janice # parent message matched the keyword)
Run Code Online (Sandbox Code Playgroud)

我的模型如下:

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
Run Code Online (Sandbox Code Playgroud)

用于指定嵌套子查询类似条件的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
Run Code Online (Sandbox Code Playgroud)

在上面的模型中,我想将文本搜索限制为给定的类别.

Nic*_*zny 8

实现所需内容的最佳方法是将回复的内容 - 以及您希望搜索的任何其他字段 - 反规范化为其父消息.

在太阳黑子中这很简单.您可能在线研究的另一个常见方案是根据其评论内容搜索博客文章.

这里需要注意的一件重要事情是:由于非规范化,您需要一个after_save钩子,以便回复可以在添加或更新时重新索引其父级.

在您的情况下,更改可能看起来像这样......

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
Run Code Online (Sandbox Code Playgroud)

(如果您想保存几行而不是定义新方法,text :replies_content也可以接受内联lambda.这取决于您.)

使用此方法,搜索语法没有真正的变化,因为回复的所有内容都会被归入您的默认关键字搜索.

如果您考虑到更具体的用例,则需要澄清您的问题,但这对我来说似乎是最好和最简单的方法.

最后一点:如果您的消息有很多回复,这种方法可能会有点沉重.确保使用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
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,category_id在太阳黑子中,确定范围非常简单.这可能是你问题的主要部分,我刚刚离开并使它变得比它更复杂:)

从那里,其中一些@messages将是父母,一些将是答复.您的视图当然可以确定哪个是哪个并相应地进行渲染.

<% if message.parent %>
  …
Run Code Online (Sandbox Code Playgroud)

此处还有一些其他方法,具体取决于您的要求的确切性质.以上可能已经足够了,所以我不会在这里详述它们.但是如果你继续追求非规范化,你还可以为所有消息的回复包含一个多值整数列category_id.有点像integer :reply_category_ids, :multi => true.

后一种方法可以提供与消息线程整体更松散的匹配,这可能是也可能不值得非规范化的复杂性,具体取决于您的应用程序.我将把语法留给你,它主要来自我之前的例子.

正如您所看到的,这里有一些排列,具体取决于您希望针对该类别的范围和时间.希望我的上述示例能够让您足够了解应用程序的具体细节.