【问题标题】:Elasticsearch, Tire, and Nested queries / associations with ActiveRecordElasticsearch、Tire 和嵌套查询/与 ActiveRecord 的关联
【发布时间】:2012-07-26 09:54:24
【问题描述】:

我正在使用 ElasticSearch 和 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'

这使得文本可搜索,但显然它不是一个很好的 hack,它会丢失实际的关联对象。我已经尝试过搜索的变体,例如:

s = Book.search do
  query do
    boolean do
      should { string query_string }
      should { string "chapters.chapter_text:#{query_string}" }
    end
  end
end

那里也没有运气。如果有人有一个使用 Tire 索引和搜索关联 ActiveRecord 对象的良好、清晰的示例,那么这似乎是对这里知识库的一个非常好的补充。

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

【问题讨论】:

  • 如果答案对您有帮助,请考虑将其标记为已接受。
  • 我花了一些时间来测试和确认(被拉到另一个项目中)但是是的,你的回答很好,谢谢 - 我相信它会对很多人有所帮助开始吧。

标签: elasticsearch tire


【解决方案1】:

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

也就是说,这里是一个完整的轮胎配置示例,可与 Rails 在 elasticsearch 中的关联一起使用:active_record_associations.rb

让我在这里强调几件事。

触摸父母

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

鉴于我们有一个“属于”BookChapter 模型,我们需要这样做:

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

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

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

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

响应触摸

这部分排序完成后,我们需要通知Tire有关变化,并相应地更新elasticsearch索引:

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

(毫无疑问,Tire 应该自行拦截 after_touch 通知,而不是强迫您这样做。另一方面,这证明了按自己的方式工作是多么容易以不伤眼睛的方式绕过图书馆的限制。)

Rails 中正确的 JSON 序列化

尽管 README 提到你必须在 Rails

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

请注意,我们使用 boosting 索引 title,将 created_at 索引为“日期”,以及来自关联模型的章节文本。在 elasticsearch 中,所有数据都被有效地“反规范化”为单个文档(如果这样的术语有点意义的话)。

正确的文档 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 个查询。

【讨论】:

  • 我有机会尝试一下,它有效;非常感谢您回答的完整性(当然还有关于轮胎的出色工作)。与此同时,我通过创建一个执行 chapters.map() 来收集文本然后在 to_indexed_json() 中调用该方法的方法来让它工作,但这显然有点像 hack。这种方法更清洁。再次感谢。
  • 我也在寻找一种“更干净”的关联方式。感谢您的回答的彻底性。轮胎宝石很酷。感谢您的辛勤工作。
  • 这很有趣——想详细说明“更清洁”的方式吗?在这里或在 Github 问题、邮件等处?
  • 这是否受到多对多关系的影响,使用 :through => ?我有资源、标签和资源标签。如此混乱:S。我不确定我应该添加什么:触摸到
  • 老实说,我什至不需要索引这些东西,我只是想让它工作,所以我不必使用 load: true。
【解决方案2】:

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

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

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

【讨论】:

  • 你的gem还能用吗,我看很久没更新了
  • 自从 elasticsearch 发布了自己的 rails gem 以来,我已经很久没有接触过它了。我建议为每个要索引的文档制作一个特定的作业,并在模型/服务回调中明确地将作业排入队列。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-07-26
  • 1970-01-01
  • 2011-10-25
  • 2012-04-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多