Module: SearchFlip::Aggregatable

Included in:
Aggregation, Criteria
Defined in:
lib/search_flip/aggregatable.rb

Overview

The SearchFlip::Aggregatable mixin provides handy methods for using the Elasticsearch aggregation framework, which can be chained with each other, all other criteria methods and even nested.

Examples:

ProductIndex.where(available: true).aggregate(:tags, size: 50)
OrderIndex.aggregate(revenue: { sum: { field: "price" }})

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



11
12
13
14
15
# File 'lib/search_flip/aggregatable.rb', line 11

def self.included(base)
  base.class_eval do
    attr_accessor :aggregation_values
  end
end

Instance Method Details

#aggregate(field_or_hash, options = {}, &block) ⇒ Object

Adds an arbitrary aggregation to the request which can be chained as well as nested. Check out the examples and Elasticsearch docs for further details.

Examples:

Basic usage with optons

query = CommentIndex.where(public: true).aggregate(:user_id, size: 100)

query.aggregations(:user_id)
# => { 4 => #<SearchFlip::Result ...>, 7 => #<SearchFlip::Result ...>, ... }

Simple range aggregation

ranges = [{ to: 50 }, { from: 50, to: 100 }, { from: 100 }]

ProductIndex.aggregate(price_range: { range: { field: "price", ranges: ranges }})

Basic nested aggregation

# When nesting aggregations, the return value of the aggregate block is
# used.

OrderIndex.aggregate(:user_id, order: { revenue: "desc" }) do |aggregation|
  aggregation.aggregate(revenue: { sum: { field: "price" }})
end

Nested histogram aggregation

OrderIndex.aggregate(histogram: { date_histogram: { field: "price", interval: "month" }}) do |aggregation|
  aggregation.aggregate(:user_id)
end

Nested aggregation with filters

OrderIndex.aggregate(average_price: {}) do |aggregation|
  aggregation = aggregation.match_all
  aggregation = aggregation.where(user_id: current_user.id) if current_user

  aggregation.aggregate(average_price: { avg: { field: "price" }})
end


53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/search_flip/aggregatable.rb', line 53

def aggregate(field_or_hash, options = {}, &block)
  fresh.tap do |criteria|
    hash = field_or_hash.is_a?(Hash) ? field_or_hash : { field_or_hash => { terms: { field: field_or_hash }.merge(options) } }

    if block
      aggregation = yield(SearchFlip::Aggregation.new(target: target))

      if field_or_hash.is_a?(Hash)
        aggregation_hash = field_or_hash.values.first
        aggregation_hash = aggregation_hash[:top_hits] if aggregation_hash.is_a?(Hash) && aggregation_hash.key?(:top_hits)
        aggregation_hash = aggregation_hash["top_hits"] if aggregation_hash.is_a?(Hash) && aggregation_hash.key?("top_hits")

        aggregation_hash.merge!(aggregation.to_hash)
      else
        hash[field_or_hash].merge!(aggregation.to_hash)
      end
    end

    criteria.aggregation_values = (aggregation_values || {}).merge(hash)
  end
end