module SearchFlip::PostFilterable

The SearchFlip::PostFilterable mixin provides chainable methods like post_where, post_exists, post_range, etc to add and apply search filters after aggregations have already been calculated.

@example

query = ProductIndex.search("harry potter")

query = query.aggregate(price_ranges: {
  range: {
    field: "price",
    ranges: [
      { key: "range1", from: 0,  to: 20 },
      { key: "range2", from: 20, to: 50 },
      { key: "range3", from: 50, to: 100 }
    ]
  }
})

query = query.post_where(price: 20 ... 50)

Public Class Methods

included(base) click to toggle source
# File lib/search_flip/post_filterable.rb, line 23
def self.included(base)
  base.class_eval do
    attr_accessor :post_must_values, :post_must_not_values, :post_filter_values
  end
end

Public Instance Methods

post_exists(field) click to toggle source

Adds a post exists filter to the criteria, which selects all documents for which the specified field has a non-null value.

@example

query = CommentIndex.aggregate("...")
query = query.post_exists(:notified_at)

@param field [Symbol, String] The field that should have a non-null value

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 230
def post_exists(field)
  post_filter(exists: { field: field })
end
post_exists_not(field) click to toggle source

Adds a post exists not query to the criteria, which selects all documents for which the specified field's value is null.

@example

query = CommentIndex.aggregate("...")
query = query.post_exists_not(:notified_at)

@param field [Symbol, String] The field that should have a null value

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 245
def post_exists_not(field)
  post_must_not(exists: { field: field })
end
post_filter(clause) click to toggle source

Adds raw post filter queries to the criteria.

@example Raw post term filter query

query = CommentIndex.aggregate("...")
query = query.post_filter(term: { state: "new" })

@example Raw post range filter query

query = CommentIndex.aggregate("...")
query = query.post_filter(range: { created_at: { gte: Time.parse("2016-01-01") }})

@param args [Array, Hash] The raw filter query arguments

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 134
def post_filter(clause)
  fresh.tap do |criteria|
    criteria.post_filter_values = (post_filter_values || []) + Helper.wrap_array(clause)
  end
end
post_must(clause) click to toggle source

Adds raw post must queries to the criteria.

@example Raw post term must query

query = CommentIndex.aggregate("...")
query = query.post_must(term: { state: "new" })

@example Raw post range must query

query = CommentIndex.aggregate("...")
query = query.post_must(range: { created_at: { gte: Time.parse("2016-01-01") }})

@param args [Array, Hash] The raw must query arguments

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 154
def post_must(clause)
  fresh.tap do |criteria|
    criteria.post_must_values = (post_must_values || []) + Helper.wrap_array(clause)
  end
end
post_must_not(clause) click to toggle source

Adds raw post must_not queries to the criteria.

@example Raw post term must_not query

query = CommentIndex.aggregate("...")
query = query.post_must_not(term: { state: "new" })

@example Raw post range must_not query

query = CommentIndex.aggregate("...")
query = query.post_must_not(range: { created_at: { gte: Time.parse("2016-01-01") }})

@param args [Array, Hash] The raw must_not query arguments

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 174
def post_must_not(clause)
  fresh.tap do |criteria|
    criteria.post_must_not_values = (post_must_not_values || []) + Helper.wrap_array(clause)
  end
end
post_range(field, options = {}) click to toggle source

Adds a post range filter to the criteria without being forced to specify the left and right end of the range, such that you can eg simply specify lt, lte, gt and gte. For fully specified ranges, you can easily use post_where, etc. Check out the Elasticsearch docs for further details regarding the range filter.

@example

query = CommentIndex.aggregate("...")
query = query.post_range(:created_at, gte: Time.parse("2016-01-01"))

query = CommentIndex.aggregate("...")
query = query.post_range(:likes_count, gt: 10, lt: 100)

@param field [Symbol, String] The field name to specify the range for @param options [Hash] The range filter specification, like lt, lte, etc

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 215
def post_range(field, options = {})
  post_filter(range: { field => options })
end
post_search(q, options = {}) click to toggle source

Adds a post query string query to the criteria while using AND as the default operator unless otherwise specified. Check out the Elasticsearch docs for further details.

@example

CommentIndex.aggregate(:user_id).post_search("message:hello OR message:worl*")

@param q [String] The query string query

@param options [Hash] Additional options for the query string query, like

eg default_operator, default_field, etc.

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 43
def post_search(q, options = {})
  return self if q.to_s.strip.length.zero?

  post_must(query_string: { query: q, default_operator: :AND }.merge(options))
end
post_should(clause) click to toggle source

Adds a raw post should query to the criteria.

@example Raw post term should query

query = CommentIndex.aggregate("...")
query = query.post_should([
  { term: { state: "new" } },
  { term: { state: "approved" } }
])

@param clauses [Array] The raw should query arguments

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 193
def post_should(clause)
  post_must(bool: { should: clause })
end
post_where(hash) click to toggle source

Adds post filters to your criteria for the supplied hash composed of field-to-filter mappings which specify terms, term or range filters, depending on the type of the respective hash value, namely array, range or scalar type like Fixnum, String, etc.

@example Array values

query = CommentIndex.aggregate("...")
query = query.post_where(id: [1, 2, 3], state: ["approved", "declined"])

@example Range values

query = CommentIndex.aggregate("...")
query = query.post_where(created_at: Time.parse("2016-01-01") .. Time.parse("2017-01-01"))

@example Scalar types

query = CommentIndex.aggregate("...")
query = query.post_where(id: 1, message: "hello world")

@param hash [Hash] A field-to-filter mapping specifying filter values for

the respective fields

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 71
def post_where(hash)
  hash.inject(fresh) do |memo, (key, value)|
    if value.is_a?(Array)
      memo.post_filter(terms: { key => value })
    elsif value.is_a?(Range)
      memo.post_filter(range: { key => { gte: value.min, lte: value.max } })
    elsif value.nil?
      memo.post_must_not(exists: { field: key })
    else
      memo.post_filter(term: { key => value })
    end
  end
end
post_where_not(hash) click to toggle source

Adds post filters to exclude documents in accordance to the supplied hash composed of field-to-filter mappings. Check out post_where for further details.

@example Array values

query = CommentIndex.aggregate("...")
query = query.post_where_not(id: [1, 2, 3])

@example Range values

query = CommentIndex.aggregate("...")
query = query.post_where_not(created_at: Time.parse("2016-01-01") .. Time.parse("2017-01-01"))

@example

query = CommentIndex.aggregate("...")
query = query.post_where_not(state: "approved")

@param hash [Hash] A field-to-filter mapping specifying filter values for the

respective fields

@return [SearchFlip::Criteria] A newly created extended criteria

# File lib/search_flip/post_filterable.rb, line 106
def post_where_not(hash)
  hash.inject(fresh) do |memo, (key, value)|
    if value.is_a?(Array)
      memo.post_must_not(terms: { key => value })
    elsif value.is_a?(Range)
      memo.post_must_not(range: { key => { gte: value.min, lte: value.max } })
    elsif value.nil?
      memo.post_filter(exists: { field: key })
    else
      memo.post_must_not(term: { key => value })
    end
  end
end