Elasticsearch, Tire, and Nested queries / associations with ActiveRecord

前端 未结 2 567
误落风尘
误落风尘 2020-12-01 00:13

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

相关标签:
2条回答
  • 2020-12-01 00:42

    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:

    • https://github.com/karmi/railscasts-episodes/commit/ee1f6f3
    • https://github.com/karmi/railscasts-episodes/commit/03c45c3
    • https://github.com/karmi/tire/blob/master/test/models/active_record_models.rb#L10-20

    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.

    0 讨论(0)
  • 2020-12-01 01:06

    I have created this as a solution in one of my applications, that indexes a deeply nested set of models

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

    UPDATE: I have now released a gem that does this: https://github.com/paulnsorensen/lifesaver

    0 讨论(0)
提交回复
热议问题