弹性搜索,轮胎和嵌套查询/与ActiveRecord的关联 [英] Elasticsearch, Tire, and Nested queries / associations with ActiveRecord

查看:275
本文介绍了弹性搜索,轮胎和嵌套查询/与ActiveRecord的关联的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用ElasticSearch和Tire来索引和搜索一些ActiveRecord模型,并且我一直在搜索正确的索引和搜索关联的方式。我没有找到什么似乎是最好的做法,所以我想问是否有人有一种方法,他们认为工作真的很好。



作为一个示例设置(这是补充但说明问题),假设我们有一本书,附有章节。每本书都有一个标题和作者,还有一堆章节。每章都有文字。我们希望为图书的字段和章节的文本索引,以便您可以按作者搜索一本书,或者为任何具有某些单词的书籍。

  class Book< ActiveRecord :: Base 
include Tire :: Model :: Search
include Tire :: Model :: Callbacks

has_many:chapter

mapping do
索引:title,:analyzer => 'snowball',:boost => 100
索引:作者,:analyzer => 'snowball'
索引:章节,类型:'object',属性:{
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

这不起作用,即使这样的索引应该这样做。如果相反,我索引:

 索引:章节:as => chapters.map {| C | c.chapter_text} .join('|'),:analyzer => 'snowball'

这使得文本可搜索,但显然这不是一个好的黑客,它失去了实际关联对象。我尝试了这些搜索的变体,例如:

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

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



谢谢对于任何想法和贡献。

解决方案

在Tire中对ActiveRecord关联的支持正在运行,但需要您的应用程序中的几个调整。图书馆在这里应该做的更好,在这方面毫无疑问,将来肯定会。\\ / p>

说到这里,这里是一个完整的轮胎配置示例弹性搜索中的Rails协会: active_record_associations.rb



让我在这里突出几件事。



触摸父母



首先,你必须确保你通知关联的父模型关联关联的变化。



鉴于我们有一个 Chapter model,属于a Book ,我们需要做:

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

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

  book.chapters.create文本:Lorem ipsum ....

书籍实例被通知有关添加的章节。



响应触摸



有了这部分排序,我们需要通知轮胎关于更改,并相应地更新弹性搜索索引:

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

(毫无疑问,轮胎应该截取 after_touch 通知本身,而不是强迫你这样做,另一方面,证明了以一种不会伤害你的眼睛的方式来围绕图书馆的限制来解决你的方式。)



Rails中正确的JSON序列化< 3.1



尽管有README提到,您必须禁用在Rails中自动添加JSON中的根密钥 3.1,很多人忘记了,所以你必须把它包括在类定义中:

  self.include_root_in_json = false 



弹性搜索的正确映射



现在来我们的工作的肉 - 为我们的文档(模型)定义适当的映射:

 映射do 
索引:标题,类型:'string',boost:10,分析器:'snowball'
索引:created_at,键入:'date'

索引:章节
索引:文本,分析器: 'snowball'
end
end

请注意,我们索引 title with boosting, created_at asdate,以及相关模型的章节文本。所有的数据在弹性搜索(如果这样一个术语会稍微有意义)上被有效地去归一化为一个文档。



正确的文档JSON序列化



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

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

通过所有这些设置,我们可以在 Book



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



有关更多信息,请参考以下资源: / p>



请参阅此StackOverflow答案: ElasticSearch&轮胎:使用Mapping和to_indexed_json 获取有关映射的更多信息 / to_indexed_json interplay。



请参阅此StackOverflow答案:索引ElasticSearch(Tire + ActiveRecord)中的方法的结果,以了解如何在使用关联建立模型时对n + 1查询进行打击。


I'm using ElasticSearch with Tire to index and search some ActiveRecord models, and I've been searching for the "right" way to index and search associations. I haven't found what seems like a best practice for this, so I wanted to ask if anyone has an approach that they think works really well.

As an example setup (this is made up but illustrates the problem), let's say we have a book, with chapters. Each book has a title and author, and a bunch of chapters. Each chapter has text. We want to index the book's fields and the chapters' text so you can search for a book by author, or for any book with certain words in it.

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

So then I do the search with:

s = Book.search do
  query { string query_string }
end

That doesn't work, even though it seems like that indexing should do it. If instead I index:

indexes :chapters, :as => 'chapters.map{|c| c.chapter_text}.join('|'), :analyzer => 'snowball'

That makes the text searchable, but obviously it's not a nice hack and it loses the actual associated object. I've tried variations of the searching, like:

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

With no luck there, either. If anyone has a good, clear example of indexing and searching associated ActiveRecord objects using Tire, it seems like that would be a really good addition to the knowledge base here.

Thanks for any ideas and contributions.

解决方案

The support for ActiveRecord associations in Tire is working, but requires couple of tweaks inside your application. There's no question the library should do better job here, and in the future it certainly will.

That said, here is a full-fledged example of Tire configuration to work with Rails' associations in elasticsearch: active_record_associations.rb

Let me highlight couple of things here.

Touching the parent

First, you have to ensure you notify the parent model of the association about changes in the association.

Given we have a Chapter model, which "belongs to" a Book, we need to do:

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

In this way, when we do something like:

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

The book instance is notified about the added chapter.

Responding to touches

With this part sorted, we need to notify Tire about the change, and update the elasticsearch index accordingly:

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

(There's no question Tire should intercept after_touch notifications by itself, and not force you to do this. It is, on the other hand, a testament of how easy is to work your way around the library limitations in a manner which does not hurt your eyes.)

Proper JSON serialization in Rails < 3.1

Despite the README mentions you have to disable automatic "adding root key in JSON" in Rails < 3.1, many people forget it, so you have to include it in the class definition as well:

self.include_root_in_json = false

Proper mapping for elasticsearch

Now comes the meat of our work -- defining proper mapping for our documents (models):

mapping do
  indexes :title,      type: 'string', boost: 10, analyzer: 'snowball'
  indexes :created_at, type: 'date'

  indexes :chapters do
    indexes :text, analyzer: 'snowball'
  end
end

Notice we index title with boosting, created_at as "date", and chapter text from the associated model. All the data are effectively "de-normalized" as a single document in elasticsearch (if such a term would make slight sense).

Proper document JSON serialization

As the last step, we have to properly serialize the document in the elasticsearch index. Notice how we can leverage the convenient to_json method from ActiveRecord:

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

With all this setup in place, we can search in properties in both the Book and the Chapter parts of our document.

Please run the active_record_associations.rb Ruby file linked at the beginning to see the full picture.

For further information, please refer to these resources:

See this StackOverflow answer: ElasticSearch & Tire: Using Mapping and to_indexed_json for more information about mapping / to_indexed_json interplay.

See this StackOverflow answer: Index the results of a method in ElasticSearch (Tire + ActiveRecord) to see how to fight n+1 queries when indexing models with associations.

这篇关于弹性搜索,轮胎和嵌套查询/与ActiveRecord的关联的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆