In this code snippet I’ll show how to integrate a Rails 4 model with Elasticsearch and find related models via matching tags.
Create a new Rails project:
# create directory and RVM files
mkdir rails-elasticsearch-related
echo ruby-2.2.2 > rails-elasticsearch-related/.ruby-version
echo rails-elasticsearch-related > rails-elasticsearch-related/.ruby-gemset
cd rails-elasticsearch-related
# install Rails gem
gem install rails
# create new Rails project
rails new . --database = postgresql --skip-javascript --skip-turbolinks --skip-test-unit
# setup postgresql database
rake db:create
rake db:migrate
# create Rails model (ex: User)
rails g model User first_name:string last_name:string
rake db:migrate
Add the acts-as-taggable-on gem for tagging models. edit file: Gemfile, add: gem 'acts-as-taggable-on'
# install gem and migrate database
bundle install
rake acts_as_taggable_on_engine:install:migrations
rake db:migrate
Add (interests) tags to User model. edit file: app/models/user.rb
class User < ActiveRecord :: Base
acts_as_taggable_on :interests
end
Add Elasticsearch gems. Edit file: Gemfile, add the following, and execute: bundle install
.
gem 'elasticsearch-model' , git: 'git://github.com/elasticsearch/elasticsearch-rails.git'
gem 'elasticsearch-rails' , git: 'git://github.com/elasticsearch/elasticsearch-rails.git'
Integrate User model with Elasticsearch. edit file: app/models/user.rb
class User < ActiveRecord :: Base
acts_as_taggable_on :interests
#
# Elasticsearch integration - start
#
include Elasticsearch :: Model
include Elasticsearch :: Model :: Callbacks
include Elasticsearch :: Model :: Indexing
# determine how to index model as JSON, and add tag list for interests
def as_indexed_json ( _options = {})
as_json . merge (
'interests' => interest_list
)
end
# instance method to search elasticsearch and execute more_like_this query:
def search_more_like_this ( how_many = nil )
how_many = 5 unless how_many . is_a? ( Integer )
search_definition = {
query: {
more_like_this: {
fields: tag_types ,
docs: [
{
_index: self . class . index_name ,
_type: self . class . document_type ,
_id: id
}
],
min_term_freq: 1
}
},
size: how_many
}
self . class . __elasticsearch__ . search ( search_definition )
end
#
# Elasticsearch integration - end
#
end
Time to populate the user model. For this tutorial I used the faker gem. Edit file: Gemfile, add: gem 'faker'
. Execute: bundle install
to install.
Edit file: db/seeds.rb, add:
# select random content from faker (via i18n translation)
creatures = I18n . t ( 'faker.team.creature' ). sample ( 25 )
# add 100 users
100 . times do | i |
user = User . create! ({
first_name: Faker :: Name . first_name ,
last_name: Faker :: Name . last_name ,
interest_list: creatures . sample ( 10 ), # select 10 random interests
})
end
Via rails console, create the Elasticsearch index for the User model.
rails c
> User.__elasticsearch__.create_index! force: true
> exit
Populate the User model by executing: rake db:seed
.
Check data structure in Elasticsearch via cURL:
curl 'http://127.0.0.1:9200/users/_search?size=1&sort=id' 2>/dev/null | python -m json.tool
{
"_shards" : {
"failed" : 0,
"successful" : 5,
"total" : 5
} ,
"hits" : {
"hits" : [
{
"_id" : "1" ,
"_index" : "users" ,
"_score" : null,
"_source" : {
"created_at" : "2015-07-16T01:22:27.882Z" ,
"first_name" : "Lavon" ,
"id" : 1,
"interests" : [
"vampires" ,
"sons" ,
"fishes" ,
"goats" ,
"zebras" ,
"dogs" ,
"horses" ,
"spirits" ,
"giants" ,
"sorcerors"
] ,
"last_name" : "Wuckert" ,
"updated_at" : "2015-07-16T01:22:27.882Z"
} ,
"_type" : "user" ,
"sort" : [
1
]
}
] ,
"max_score" : null,
"total" : 100
} ,
"timed_out" : false ,
"took" : 1
}
Via Rails console, find related Users via tags:
rails c
# get first user's interests
pry ( main ) > User . first . interest_list
User Load ( 0.6 ms ) SELECT "users" . * FROM "users" ORDER BY "users" . "id" ASC LIMIT 1
ActsAsTaggableOn :: Tag Load ( 1.0 ms ) SELECT "tags" . * FROM "tags" INNER JOIN "taggings" ON "tags" . "id" = "taggings" . "tag_id" WHERE "taggings" . "taggable_id" = $1 AND "taggings" . "taggable_type" = $2 AND ( taggings . context = 'interests' AND taggings . tagger_id IS NULL ) [[ "taggable_id" , 1 ], [ "taggable_type" , "User" ]]
[
[ 0 ] "vampires" ,
[ 1 ] "sons" ,
[ 2 ] "fishes" ,
[ 3 ] "goats" ,
[ 4 ] "zebras" ,
[ 5 ] "dogs" ,
[ 6 ] "horses" ,
[ 7 ] "spirits" ,
[ 8 ] "giants" ,
[ 9 ] "sorcerors"
]
# search for related users by matching tags
pry ( main ) > User . first . search_more_like_this . first
User Load ( 0.4 ms ) SELECT "users" . * FROM "users" ORDER BY "users" . "id" ASC LIMIT 1
{
"_index" => "users" ,
"_type" => "user" ,
"_id" => "5" ,
"_score" => 0.63467145 ,
"_source" => {
"id" => 5 ,
"first_name" => "Jacynthe" ,
"last_name" => "Russel" ,
"created_at" => "2015-07-16T01:22:28.232Z" ,
"updated_at" => "2015-07-16T01:22:28.232Z" ,
"interests" => [
[ 0 ] "zebras" ,
[ 1 ] "giants" ,
[ 2 ] "werewolves" ,
[ 3 ] "spirits" ,
[ 4 ] "horses" ,
[ 5 ] "witches" ,
[ 6 ] "dogs" ,
[ 7 ] "fishes" ,
[ 8 ] "elves" ,
[ 9 ] "penguins"
]
}
}
Source code on GitHub