在SunSpot Solr中嵌套搜索

时间:2010-11-04 17:47:17

标签: ruby-on-rails solr sunspot

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

E.g:

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

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

2 个答案:

答案 0 :(得分: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

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

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

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

<% if message.parent %>
  …

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

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

正如您所看到的,这里有一些排列,具体取决于您希望针对该类别的范围和时间。希望我的上述示例能够为您提供足够的信息来确定应用的具体细节。

答案 1 :(得分:0)

非常感谢Nick,当我启用跨所有表的任意子字符串的全局搜索时,您的提示帮助我解决了我的问题。就我而言,我必须使用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