Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save randika/de2303d53393e9b3923c to your computer and use it in GitHub Desktop.
Save randika/de2303d53393e9b3923c to your computer and use it in GitHub Desktop.
# 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")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment