Elasticsearch,Tire和嵌套查询/与ActiveRecord的关联

时间:2012-07-27 17:17:48

标签: elasticsearch tire

我正在使用ElasticSearch with Tire来索引和搜索一些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对象的例子,那么这似乎是对知识库的一个非常好的补充。

感谢您的任何想法和贡献。

2 个答案:

答案 0 :(得分:51)

对Tire中的ActiveRecord关联的支持正在发挥作用,但需要在应用程序内部进行一些调整。毫无疑问,图书馆应该在这里做得更好,将来肯定会有。

尽管如此,这里有一个完整的Tire配置示例,可以在弹性搜索中使用Rails的关联:active_record_associations.rb

让我在这里强调一些事情。

触摸父母

首先,您必须确保通知关联的父模型关于关联的更改。

鉴于我们有Chapter模型,“属于”Book,我们需要这样做:

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

这样,当我们做类似的事情时:

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

book实例会收到有关添加章节的通知。

回应接触

对此部分进行排序后,我们需要通知 Tire 有关更改,并相应地更新弹性搜索索引:

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

(毫无疑问 Tire 应该自行拦截after_touch通知,而不是强迫你这样做。另一方面,它证明了工作的简单性你在图书馆周围的方式限制,不会伤害你的眼睛。)

Rails中的正确JSON序列化&lt; 3.1

尽管README提到你必须在Rails&lt;中禁用自动“在JSON中添加根密钥”。 3.1,很多人都忘了它,所以你必须将它包含在类定义中:

self.include_root_in_json = false

弹性搜索的正确映射

现在我们工作的重点 - 为我们的文档(模型)定义正确的映射:

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,并从相关模型索引章节文本。所有数据都被有效地“去规范化”为弹性搜索中的单个文档(如果这样的术语有点意义)。

正确的文档JSON序列化

作为最后一步,我们必须在elasticsearch索引中正确序列化文档。请注意我们如何利用 ActiveRecord 中方便的to_json方法:

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

完成所有这些设置后,我们可以在文档的BookChapter部分中搜索属性。

请运行开头链接的active_record_associations.rb Ruby文件以查看完整图片。

有关详细信息,请参阅以下资源:

有关mapping / to_indexed_json相互作用的详情,请参阅StackOverflow回答:ElasticSearch & Tire: Using Mapping and to_indexed_json

请参阅此StackOverflow答案:Index the results of a method in ElasticSearch (Tire + ActiveRecord),了解在为具有关联的模型编制索引时如何对抗n + 1个查询。

答案 1 :(得分:3)

我已经在我的一个应用程序中创建了这个解决方案,它为一组深层嵌套的模型索引

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

更新:我现在发布了一个执行此操作的gem: https://github.com/paulnsorensen/lifesaver