1

我正在尝试实现基于 Solr 的消息线程搜索。每条消息可以有许多回复(回复只能是一个级别的深度。)。我想检索内容与搜索键匹配的父消息或与搜索键匹配的回复。

例如:

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 中非常简单。您可能会在线研究的另一个常见场景是根据评论内容搜索博客文章。

这里要注意一件重要的事情:由于非规范化,您需要一个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

使用这种方法,搜索语法没有真正的变化,因为回复的所有内容都将集中到您的默认关键字搜索中。

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

最后一点:例如,如果您的消息有很多回复,这种方法可能会有点繁重。确保使用 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 回答