class Trample::Search

Attributes

_aggs[RW]
_conditions[RW]
_models[R]

Public Class Methods

aggregation(name, attrs = {}) { |_aggs| ... } click to toggle source
# File lib/trample/search.rb, line 31
def self.aggregation(name, attrs = {})
  attrs.merge!(name: name)
  attrs[:order] = @_aggs.keys.length
  @_aggs[name] = Aggregation.new(attrs)
  yield @_aggs[name] if block_given?
end
condition(name, attrs = {}) click to toggle source
# File lib/trample/search.rb, line 25
def self.condition(name, attrs = {})
  attrs.merge!(name: name, search_klass: self)

  @_conditions[name] = Condition.new(attrs)
end
inherited(klass) click to toggle source
Calls superclass method
# File lib/trample/search.rb, line 19
def self.inherited(klass)
  super
  klass._conditions = self._conditions.dup
  klass._aggs = self._aggs.dup
end
model(*klasses) click to toggle source
# File lib/trample/search.rb, line 38
def self.model(*klasses)
  @_models = klasses
end
paginate(page_params) click to toggle source
# File lib/trample/search.rb, line 42
def self.paginate(page_params)
  instance = new
  instance.paginate(page_params)
end

Public Instance Methods

agg(*names_or_payloads) click to toggle source

todo refactor…

# File lib/trample/search.rb, line 88
def agg(*names_or_payloads)
  names_or_payloads.each do |name_or_payload|
    name = name_or_payload
    selections = []
    if name_or_payload.is_a?(Hash)
      name = name_or_payload.keys.first if name_or_payload.is_a?(Hash)
      selections = Array(name_or_payload.values.first)
    end
    template = self.class._aggs[name.to_sym]
    raise AggregationNotDefinedError.new(self, name) unless template
    agg = self.aggregations.find { |a| a.name.to_sym == name.to_sym }

    if agg.nil?
      # N.B. deep dup so buckets don't mutate
      agg = Aggregation.new(deep_dup(template.attributes).merge(name: name.to_sym))
      agg.bucket_sort = template.bucket_sort
      self.aggregations << agg
    end

    selections.each do |key|
      bucket = agg.find_or_initialize_bucket(key)
      bucket.selected = true
    end
  end

  self
end
aggregations() click to toggle source
# File lib/trample/search.rb, line 131
def aggregations
  @aggregations.sort! { |a, b| a.order <=> b.order }
  @aggregations
end
aggregations=(aggregation_array) click to toggle source

N.B rails may send nil here instead of empty array

Calls superclass method
# File lib/trample/search.rb, line 117
def aggregations=(aggregation_array)
  aggregation_array ||= []
  super([])

  aggregation_array.each do |aggregation_hash|
    if aggregation_hash[:buckets] # rails converting [] to nil
      selections = aggregation_hash[:buckets].select { |b| !!b[:selected] }.map { |b| b[:key] }
      agg(aggregation_hash[:name].to_sym => selections)
    else
      agg(aggregation_hash[:name].to_sym => [])
    end
  end
end
backend() click to toggle source
# File lib/trample/search.rb, line 143
def backend
  @backend ||= Backend::Searchkick.new(metadata, self.class._models)
end
condition(name) click to toggle source
# File lib/trample/search.rb, line 79
def condition(name)
  ConditionProxy.new(name, self)
end
conditions=(hash) click to toggle source
Calls superclass method
# File lib/trample/search.rb, line 136
def conditions=(hash)
  super({})
  hash.each_pair do |name, value|
    condition(name).set(value)
  end
end
find_in_batches(batch_size: 10_000) { |results| ... } click to toggle source

Uses elasticsearch scroll, as ES will blow up > 10_000 results, even if you are paginating.

  1. Execute a search, telling ES we are scrolling

  2. metadata.scroll_id is set on response

  3. Use the scroll_id to fetch the next resultset

  4. Repeat until results are exhausted

# File lib/trample/search.rb, line 191
def find_in_batches(batch_size: 10_000)
  metadata.scroll = true
  metadata.pagination.per_page = batch_size
  query!
  while !self.results.empty?
    yield self.results
    query!
  end
  self.metadata = Metadata.new
end
includes(includes) click to toggle source
# File lib/trample/search.rb, line 83
def includes(includes)
  self.metadata.records[:includes] = includes
end
page(current_page) click to toggle source
# File lib/trample/search.rb, line 54
def page(current_page)
  metadata.pagination.current_page = current_page
  self
end
paginate(page_params) click to toggle source
# File lib/trample/search.rb, line 47
def paginate(page_params)
  page_params ||= {}
  metadata.pagination.current_page = page_params[:number] if page_params[:number]
  metadata.pagination.per_page = page_params[:size] if page_params[:size]
  self
end
per(per_page) click to toggle source
# File lib/trample/search.rb, line 59
def per(per_page)
  metadata.pagination.per_page = per_page
  self
end
query!(options = { lookup: true }) click to toggle source
# File lib/trample/search.rb, line 147
def query!(options = { lookup: true })
  @records = nil
  hash = backend.query!(conditions, aggregations)

  load_autocompletes if options[:lookup]

  self.metadata.took = hash[:took]
  self.metadata.scroll_id = hash[:scroll_id]
  self.metadata.pagination.total = hash[:total]
  self.results = hash[:results]
  if !!metadata.records[:load]
    records!
  else
    self.results
  end
end
records() click to toggle source

Todo only works for single-model search atm N.B. preserves sorting

# File lib/trample/search.rb, line 166
def records
  @records ||= begin
                 queried = self.class._models.first.where(id: results.map(&:_id))
                 queried = queried.includes(metadata.records[:includes])
                 [].tap do |sorted|
                   results.each do |result|
                     model = queried.find { |m| m.id.to_s == result.id.to_s }
                     sorted << model
                   end
                 end
               end
end
records!() click to toggle source
# File lib/trample/search.rb, line 179
def records!
  @records = nil
  records
end
sort(*fields) click to toggle source
# File lib/trample/search.rb, line 64
def sort(*fields)
  return self if fields.empty?

  sorts = fields.map do |f|
    if f.to_s.starts_with?('-')
      f.sub!('-','')
      {att: f, dir: :desc}
    else
      {att: f, dir: :asc}
    end
  end
  self.metadata.sort = sorts
  self
end

Private Instance Methods

deep_dup(o) click to toggle source
# File lib/trample/search.rb, line 204
def deep_dup(o)
  Marshal.load(Marshal.dump(o))
end
load_autocompletes() click to toggle source
# File lib/trample/search.rb, line 208
def load_autocompletes
  self.conditions.values.each(&:lookup_autocomplete)
end