es_searchable

es_searchable implemented an ActiveRecord like search api for Elasticsearch.

Install

gem 'es_searchable', git: '[email protected]:rongchain/es_searchable.git'

or

gem install es_searchable

And then execute:

bundle

Configure


# config/initializers/es_searchable.rb

EsSearchable.configure do |config|
  config.logger = Logger.new("log/es_searchable.log")
  config.hosts = ["localhost:9200"]
end

Use es_searchable

class User
  include EsSearchable
end

search_collection = User.es_where(id: 1)
pp search_collection
=> #<EsSearchable::SearchCollection:0x007fa64361be08
 @conditions=
  {:query=>{:filtered=>{:filter=>{:bool=>{:must=>[{:term=>{:id=>1}}]}}}}},
 @klass=User>

search_collection.class
 => EsSearchable::SearchCollection

search_params = search_collection.search_params
 => {:query=>{:filtered=>{:filter=>{:bool=>{:must=>[{:term=>{:id=>1}}]}}}}}

call load on search_collection to perform the search

search_response = search_collection.load     #perform search
pp search_response
=> #<EsSearchable::SearchCollection:0x007fa64288bf18
 @collections=
  [{"id"=>1,
    "name"=>"Arnaldo Powlowski PhD",
    "email"=>"[email protected]",
    "created_at"=>"2016-03-04T06:47:58.999Z",
    "updated_at"=>"2016-03-04T06:47:58.999Z",
    "is_admin"=>true}],
 @conditions=
  {:query=>{:filtered=>{:filter=>{:bool=>{:must=>[{:term=>{:id=>1}}]}}}},
   :fields=>{}},
 @count=1,
 @klass=User,
 @response=
  {"took"=>1,
   "timed_out"=>false,
   "_shards"=>{"total"=>1, "successful"=>1, "failed"=>0},
   "hits"=>
    {"total"=>1,
     "max_score"=>1.0,
     "hits"=>
      [{"_index"=>"users",
        "_type"=>"user",
        "_id"=>"1",
        "_score"=>1.0,
        "_source"=>
         {"id"=>1,
          "name"=>"Arnaldo Powlowski PhD",
          "email"=>"[email protected]",
          "created_at"=>"2016-03-04T06:47:58.999Z",
          "updated_at"=>"2016-03-04T06:47:58.999Z",
          "is_admin"=>true}}]}},
 @time=1>

pp search_response.collections     #return search results as an Array of hash
=> [{"id"=>1,
  "name"=>"Arnaldo Powlowski PhD",
  "email"=>"[email protected]",
  "created_at"=>"2016-03-04T06:47:58.999Z",
  "updated_at"=>"2016-03-04T06:47:58.999Z",
  "is_admin"=>true}}]

search_response.time      #time consumed by the search
 => 1

search_response.count      #count of search results
 => 1

pp search_response.response       #response hash from elasticsearch
=> {"took"=>1,
 "timed_out"=>false,
 "_shards"=>{"total"=>1, "successful"=>1, "failed"=>0},
 "hits"=>
  {"total"=>1,
   "max_score"=>1.0,
   "hits"=>
    [{"_index"=>"users",
      "_type"=>"user",
      "_id"=>"1",
      "_score"=>1.0,
      "_source"=>
       {"id"=>1,
        "name"=>"Arnaldo Powlowski PhD",
        "email"=>"[email protected]",
        "created_at"=>"2016-03-04T06:47:58.999Z",
        "updated_at"=>"2016-03-04T06:47:58.999Z",
        "is_admin"=>true}}}]}}

#es_where

# Like ActiveRecord, es_searchable use es_where for exact matching, 
pp User.es_where(id: 1).load.collections
=> [{"id"=>1,
  "name"=>"Arnaldo Powlowski PhD",
  "email"=>"[email protected]",
  "created_at"=>"2016-03-04T06:47:58.999Z",
  "updated_at"=>"2016-03-04T06:47:58.999Z",
  "is_admin"=>true}}]

pp User.es_where(id: [1,2]).load.collections
=> [{"id"=>1,
  "name"=>"Arnaldo Powlowski PhD",
  "email"=>"[email protected]",
  "created_at"=>"2016-03-04T06:47:58.999Z",
  "updated_at"=>"2016-03-04T06:47:58.999Z",
  "is_admin"=>true}},
 {"id"=>2,
  "name"=>"Chanel Robel",
  "email"=>"[email protected]",
  "created_at"=>"2016-03-04T06:47:59.008Z",
  "updated_at"=>"2016-03-04T06:47:59.008Z",
  "is_admin"=>false}}]

# #es_where with multiple search conditions
User.es_where(id: 1, is_admin: false).load.collections
=> []

pp User.es_where(id: 1, is_admin: true).load.collections
=> [{"id"=>1,
  "name"=>"Arnaldo Powlowski PhD",
  "email"=>"[email protected]",
  "created_at"=>"2016-03-04T06:47:58.999Z",
  "updated_at"=>"2016-03-04T10:03:39.102Z",
  "is_admin"=>true}]

#es_select

# use es_select to select the attribtues you want
pp User.es_where(id: 1).es_select(:id,:name).load.collections
=> [{"id"=>1, "name"=>"Arnaldo Powlowski PhD"},
 {"id"=>2, "name"=>"Chanel Robel"}]

#es_like

# use es_like to perform keyword matching
pp User.es_like(name: 'Mr. Su').select(:name).load.collections
=> [{"name"=>"Mr. Marquise Goodwin"},
 {"name"=>"Mr. Stephanie Carter"},
 {"name"=>"Mr. Susana Jerde"},
 {"name"=>"Mrs. Earline Thompson"},
 {"name"=>"Mrs. Eunice Bergnaum"}]
pp User.es_like(name: 'Mr. Su').select(:name).search_params
=> {:query=>
  {:filtered=>{:query=>{:bool=>{:must=>[{:match=>{:name=>"Mr. Su"}}]}}}},
 :fields=>[:name]}

pp User.es_like(name: {or: 'Mr. Su'}).select(:name).load.collections
=> [{"name"=>"Mr. Marquise Goodwin"},
 {"name"=>"Mr. Stephanie Carter"},
 {"name"=>"Mr. Susana Jerde"},
 {"name"=>"Mrs. Earline Thompson"},
 {"name"=>"Mrs. Eunice Bergnaum"}]
pp User.es_like(name: {or: 'Mr. Su'}).select(:name).search_params
=> {:query=>
  {:filtered=>
    {:query=>
      {:bool=>
        {:must=>[{:match=>{:name=>{:operator=>:or, :query=>"Mr. Su"}}}]}}}},
 :fields=>[:name]}

pp User.es_like(name: {and: 'Mr. Su'}).select(:name).load.collections
=> []
pp User.es_like(name: {and: 'Mr. Su'}).select(:name).search_params
=> {:query=>
  {:filtered=>
    {:query=>
      {:bool=>
        {:must=>[{:match=>{:name=>{:operator=>:and, :query=>"Mr. Su"}}}]}}}},
 :fields=>[:name]}

#use :like in #es_where

# the following search are equal
User.es_like(name: "test").es_where(id:1)
User.es_where(id:1, name: {like: "test"})

pp User.es_where(id: 1, email: {like: 'charley_walsh'}).select(:id,:email).load.collections
=> [{"id"=>1, "email"=>"[email protected]"}]
pp User.es_where(id: 1).es_like(email: 'charley_walsh').select(:id,:email).load.collections
=> [{"id"=>1, "email"=>"[email protected]"}]

#es_or, #es_and

# use es_or to perform OR condition search
User.es_or(id:2, is_admin: true).select(:id,:is_admin).load.collections
=> [{"id"=>1, "is_admin"=>true}, {"id"=>2, "is_admin"=>false}]

# use es_and to perform AND condition search
User.es_and(id:2, is_admin: true).select(:id,:is_admin).load.collections
=> []
User.es_and(id:1, is_admin: true).select(:id,:is_admin).load.collections
=> [{"id"=>1, "is_admin"=>true}]

# es_and is alias of es_where
User.es_and(id: 1, or: {email: {like: 'brennon'}, is_admin: true})
# the condictions of the search above is  
tel=18510705036 and ( email like 'edaixi.com' or is_admin=true)

#es_not

#use es_not to perform not equal condition search
User.es_where({}).load.count
=> 100
User.es_not({id: 1}).load.count
=> 99
# use the following syntax to perform range search
User.es_where(id: {lt: 3, gt:1  }).select(:id).load.collections
 => [{"id"=>2}]

User.es_where(id: {lte: 2, gte: 1 }).select(:id).load.collections
 => [{"id"=>1}, {"id"=>2}]

#   lt   =>    less than   <
#   gt  =>    greater than >
#   lte =>    less than equal  <= 
#   gte =>   greater than equal >=

### #es_limit and #es_offset

# es_limit and es_offset work the same as limit and offset methods of ActiveRecor

User.es_where({}).select(:id).limit(3).load.collections
 => [{"id"=>1}, {"id"=>2}, {"id"=>3}]

User.es_where({}).select(:id).limit(3).offset(2).load.collections
 => [{"id"=>3}, {"id"=>4}, {"id"=>5}]

*_like, *_between, *_gt, *_lt, *_gte, *_lte

User.name_like('test')   <=>    User.like(name: 'test')
User.id_lte(10)          <=>    User.lte(id: 10)
User.id_between(1,3)     <=>    User.where(id: {gte: 1, lte: 2})