public
Last active

An example of elasticsearch & Tire setup for ActiveRecord associations

  • Download Gist
active_record_associations.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 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")

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.