Skip to content

ActiveRecord Elasticsearch

Spencer Markowski edited this page Nov 1, 2023 · 1 revision

Query Interface

where(field: value)

Model.where(color: :red)
# query => {"query":{"bool":{"must":{"term":{"color":"red"}}}}}

not(field: value)

Model.where_not(color: :red)
# query => {"query":{"bool":{"must_not":{"term":{"color":"red"}}}}}

first

Model.first
# query => {"sort":{"created_at":"asc"}}

last

Model.last
# query => {"sort":{"created_at":"desc"}}

count

Model.where(color: :blue).count
#=> 60

Model.count
#=> {"count"=>624, "_shards"=>{"total"=>5, "successful"=>5, "skipped"=>0, "failed"=>0}}

source(include: [...fields...])

Model.source(include: [:body, :color])

filter(:filter_method, arguments)

Model.filter(:terms, color: [:blue, :red])
# query => {"query":{"bool":{"filter":{"bool":{"must":[{"terms":{"color":["blue","red"]}}]}}}}}
Model.filter(:term, color: :red)
# query => {"query":{"bool":{"filter":{"bool":{"must":[{"term":{"color":"red"}}]}}}}}

size

Model.size(10000)

routing(:field_name)

Model.routing(:id)
# query => models/_search?routing=id&size=10000

Chaining

Model.where_not(color: :red).where(in_stock: true)
# query => {"query":{"bool":{"must":{"term":{"in_stock":true}},"must_not":{"term":{"color":"red"}}}}}
Model.where_not(color: :red).where(in_stock: true, local: true).filter(:term, soft: true)
# query => {"query":{"bool":{"must":[{"term":{"in_stock":true}},{"term":{"local":true}}],"must_not":{"term":{"color":"red"}},"filter":{"bool":{"must":[{"term":{"soft":true}}]}}}}}

Aggregations

aggregation :bucket_name, aggregation_type: { args... }

Terms

aggregation(:colors, terms: {field: :color})

Nested

aggregation(:colors, 
    terms: {
      field: :color
    }, 
    aggs: { 
      total: { 
        sum: {
          field: :price
        } 
      }
    }
)

Aggregations are accessible on the result object.

result = Model.filter(:terms, color: :blue).aggregation(:totals, sum: {field: :quantity})
result.aggregations.totals #=> 10

Scopes

  scope :colored, ->(colors) { aggregation(:colors, filters: { colors: { terms: { color: colors} } ) }
  scope :available, -> { where(available: true) }
  scope :between, lambda { |range| filter(:range, date: {gte: range.begin, lte: range.end}) }

Associations

Magic Fields

(:created_at, :updated_at)

Default Sort

default_sort_key :created_at

Index name

Automatically derived from class name as ClassName.to_s.tableize

index_name 'items'

Attributes

attribute :color,       String,     default: :blue, index: 'not_analyzed'
attribute :guid,        Keyword
attribute :sizes,       Array,      default: [], index: :not_analyzed
attribute :available,   Boolean,    default: false
attribute :quantity,    Integer,    default: 0
attribute :date         DateTime

Relation

Bulk Operations

to_bulk(:index) # default
to_bulk(:delete)
to_bulk(:update)
blankets = [{"color": "blue", "quantity": 100}, ...]
blankets.each_slice(100) do |batch|
    blankets_batch = batch.collect do |item|
      Blanket.new( color: item.color, quantity: item.quantity).to_bulk
    end
    Blanket.gateway.client.bulk body: blankets_batch
end

Instrumentation

Queries are automatically logged via ActiveSupport::Instrumentation via the to_elastic method.

Blanket (6.322ms) curl -X GET 'http://localhost:9200/blankets/_search?size=1' -d '{"sort":{"date":"desc"}}'
Model.to_elastic