Skip to content

Instantly share code, notes, and snippets.

@karmi
Created July 29, 2012 16:57
Show Gist options
  • Select an option

  • Save karmi/3200212 to your computer and use it in GitHub Desktop.

Select an option

Save karmi/3200212 to your computer and use it in GitHub Desktop.

Revisions

  1. karmi created this gist Jul 29, 2012.
    141 changes: 141 additions & 0 deletions active_record_associations.rb
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,141 @@
    # An example of elasticsearch & Tire setup for ActiveRecord associations.
    #
    # A `Book has_many :chapters` scenario, with mapping and JSON serialization
    # for indexing associated models.
    #
    # Demonstrates three important caveats as of now:
    #
    # 1. You you have to use `touch: true` in the `belongs_to` declaration,
    # to automatically notify the parent model about the update.
    #
    # 2. You have to explicitely hook up updating elasticsearch index via
    # the `after_touch` callback in the parent model.
    #
    # 3. You have to disable `include_root_in_json` for proper JSON serialization.
    #
    #
    # Run me with:
    #
    # $ ruby active_record_associations.rb
    #

    require 'logger'
    require 'ansi/core'
    require 'active_record'
    require 'oj'
    require 'tire'

    def _(message=nil); puts '-'*80, ANSI.bold(message.to_s), '-'*80; end

    ActiveRecord::Base.logger = Logger.new(STDERR)
    ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: ":memory:" )

    Tire.configure { logger STDERR }

    _ "Creating ActiveRecord schema..."

    ActiveRecord::Schema.define(version: 1) do
    create_table :books do |t|
    t.string :title
    t.timestamps
    end
    create_table :chapters do |t|
    t.string :text
    t.integer :number, :book_id
    t.timestamps
    end
    add_index(:chapters, :book_id)
    end

    _ "Deleting elasticsearch index..."

    Tire.index('books').delete

    # The Book class
    #
    class Book < ActiveRecord::Base
    has_many :chapters

    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # Enable automatic saving in elasticsearch when associated objects change
    #
    after_touch() { tire.update_index }
    #
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # Properly serialize JSON for elasticsearch
    #
    self.include_root_in_json = false
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

    include Tire::Model::Search
    include Tire::Model::Callbacks

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

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

    # Define the JSON serialization
    #
    def to_indexed_json
    to_json( include: { chapters: { only: [:text] } } )
    end

    end

    # The book Chapter class
    #
    class Chapter < ActiveRecord::Base
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    # Do not forget to automatically `touch` parent object from associations
    belongs_to :book, touch: true
    # !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    end

    _ "Create book instance..."

    book = Book.create title: "How to impress your Rails friends with elasticsearch"
    book.chapters.create number: 1, text: "The world generates more and more information ..."
    book.chapters.create number: 2, text: "After the elasticsearch installation ..."

    p Book.first
    p Book.first.chapters

    _ "Refresh the index for immediate search (there's a 1 second delay by default)..."

    Book.tire.index.refresh

    _ "Search 'elasticsearch' in book titles..."

    results = Book.search('title:elasticsearch')

    puts ANSI.green( "Found: '%s'" % results.first.title )

    _ "Search 'install' in chapter text..."

    results = Book.search('chapters.text:install')

    puts ANSI.green( "Found: '%s'" % results.first.title )

    _ "Search 'elasticsearch' anywhere with highlighting..."

    results = Book.search do
    query { string "elasticsearch" }
    highlight 'title', 'chapters.text'
    end

    puts ANSI.green( "Found: '%s'" % results.first.title ),
    '-'*80,
    "Highlights: ",
    results.first.highlight.to_hash \
    .map { |k,v| ' - ' + k.to_s + ": " + v.first.to_s } \
    .map { |s| s.gsub(/<em>([^<]+)<\/em>/, ANSI.yellow + ANSI.bold + '\1' + ANSI.reset) } \
    .join("\n")