Module: SearchFlip::Filterable

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

Overview

The SearchFlip::Filterable mixin provides chainable methods like #where, #exists, #range, etc to add search filters to a criteria.

Examples:

CommentIndex.where(public: true)
CommentIndex.exists(:user_id)
CommentIndex.range(:created_at, gt: Date.today - 7)

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(base) ⇒ Object



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

def self.included(base)
  base.class_eval do
    attr_accessor :must_values, :must_not_values, :filter_values
  end
end

Instance Method Details

#exists(field) ⇒ SearchFlip::Criteria

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

Examples:

CommentIndex.exists(:notified_at)

Parameters:

  • field (Symbol, String)

    The field that should have a non-null value

Returns:



264
265
266
# File 'lib/search_flip/filterable.rb', line 264

def exists(field)
  filter(exists: { field: field })
end

#exists_not(field) ⇒ SearchFlip::Criteria

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

Examples:

CommentIndex.exists_not(:notified_at)

Parameters:

  • field (Symbol, String)

    The field that should have a null value

Returns:



278
279
280
# File 'lib/search_flip/filterable.rb', line 278

def exists_not(field)
  must_not(exists: { field: field })
end

#filter(clause) ⇒ SearchFlip::Criteria

Adds raw filter queries to the criteria.

Examples:

CommentIndex.filter(term: { state: "new" })
CommentIndex.filter(range: { created_at: { gte: Time.parse("2016-01-01") }})

Parameters:

  • args (Array, Hash)

    The raw filter query arguments

Returns:



109
110
111
112
113
# File 'lib/search_flip/filterable.rb', line 109

def filter(clause)
  fresh.tap do |criteria|
    criteria.filter_values = (filter_values || []) + Helper.wrap_array(clause)
  end
end

#match_all(options = {}) ⇒ SearchFlip::Criteria

Adds a match all filter to the criteria, which simply matches all documents. This can be eg be used within filter aggregations or for filter chaining. Check out the Elasticsearch docs for further details.

Examples:

Basic usage

CommentIndex.match_all

Filter chaining

query = CommentIndex.match_all
query = query.where(public: true) unless current_user.admin?

Filter aggregation

query = CommentIndex.aggregate(filtered_tags: {}) do |aggregation|
  aggregation = aggregation.match_all
  aggregation = aggregation.where(user_id: current_user.id) if current_user
  aggregation = aggregation.aggregate(:tags)
end

query.aggregations(:filtered_tags).tags.buckets.each { ... }

Parameters:

  • options (Hash) (defaults to: {})

    Options for the match_all filter, like eg boost

Returns:



234
235
236
# File 'lib/search_flip/filterable.rb', line 234

def match_all(options = {})
  filter(match_all: options)
end

#match_noneSearchFlip::Criteria

Adds a match none filter to the criteria, which simply matches none documents at all. Check out the Elasticsearch docs for further details.

Examples:

Basic usage

CommentIndex.match_none

Filter chaining

query = CommentIndex.search("...")
query = query.match_none unless current_user.admin?

Returns:



250
251
252
# File 'lib/search_flip/filterable.rb', line 250

def match_none
  filter(match_none: {})
end

#must(clause, bool_options = {}) ⇒ SearchFlip::Criteria

Adds raw must queries to the criteria.

Examples:

CommentIndex.must(term: { state: "new" })
CommentIndex.must(range: { created_at: { gt: Time.parse("2016-01-01") }})

Parameters:

  • args (Array, Hash)

    The raw must query arguments

Returns:



125
126
127
128
129
# File 'lib/search_flip/filterable.rb', line 125

def must(clause, bool_options = {})
  fresh.tap do |criteria|
    criteria.must_values = (must_values || []) + Helper.wrap_array(clause)
  end
end

#must_not(clause) ⇒ SearchFlip::Criteria

Adds raw must_not queries to the criteria.

Examples:

CommentIndex.must_not(term: { state: "new" })
CommentIndex.must_not(range: { created_at: { gt: Time.parse"2016-01-01") }})

Parameters:

  • args (Array, Hash)

    The raw must_not query arguments

Returns:



141
142
143
144
145
# File 'lib/search_flip/filterable.rb', line 141

def must_not(clause)
  fresh.tap do |criteria|
    criteria.must_not_values = (must_not_values || []) + Helper.wrap_array(clause)
  end
end

#range(field, options = {}) ⇒ SearchFlip::Criteria

Adds a 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 as well use #where, etc. Check out the Elasticsearch docs for further details regarding the range filter.

Examples:

CommentIndex.range(:created_at, gte: Time.parse("2016-01-01"))
CommentIndex.range(:likes_count, gt: 10, lt: 100)

Parameters:

  • field (Symbol, String)

    The field name to specify the range for

  • options (Hash) (defaults to: {})

    The range filter specification, like lt, lte, etc

Returns:



206
207
208
# File 'lib/search_flip/filterable.rb', line 206

def range(field, options = {})
  filter(range: { field => options })
end

#search(q, options = {}) ⇒ SearchFlip::Criteria

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

Examples:

CommentIndex.search("message:hello OR message:worl*")

Parameters:

  • q (String)

    The query string query

  • options (Hash) (defaults to: {})

    Additional options for the query string query, like eg default_operator, default_field, etc.

Returns:



31
32
33
34
35
# File 'lib/search_flip/filterable.rb', line 31

def search(q, options = {})
  return self if q.to_s.strip.length.zero?

  must(query_string: { query: q, default_operator: :AND }.merge(options))
end

#should(clause) ⇒ SearchFlip::Criteria

Adds a raw should query to the criteria.

Examples:

CommentIndex.should([
  { term: { state: "new" } },
  { term: { state: "reviewed" } }
])

Parameters:

  • args (Array)

    The raw should query arguments

Returns:



187
188
189
# File 'lib/search_flip/filterable.rb', line 187

def should(clause)
  must(bool: { should: clause })
end

#to_queryHash

Returns all added queries and filters, including post filters, as a raw query.

Examples:

CommentIndex.where(state: "new").search("text").to_query
# => {:bool=>{:filter=>[{:term=>{:state=>"new"}}], :must=>[{:query_string=>{:query=>"text", ...}}]}}

CommentIndex.must(term: { state: "new" }).to_query
# => {:term=>{:state=>"new"}}

Returns:

  • (Hash)

    The raw query



159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/search_flip/filterable.rb', line 159

def to_query
  must_clauses = must_values.to_a
  must_not_clauses = must_not_values.to_a + post_must_not_values.to_a
  filter_clauses = post_must_values.to_a + filter_values.to_a + post_filter_values.to_a

  return must_clauses.first if must_clauses.size == 1 && must_not_clauses.empty? && filter_clauses.empty?

  {
    bool: {
      must: must_clauses,
      must_not: must_not_clauses,
      filter: filter_clauses
    }.reject { |_, value| value.empty? }
  }
end

#where(hash) ⇒ SearchFlip::Criteria

Adds 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.

Examples:

CommentIndex.where(id: [1, 2, 3], state: ["approved", "declined"])
CommentIndex.where(id: 1 .. 100)
CommentIndex.where(created_at: Time.parse("2016-01-01") .. Time.parse("2017-01-01"))
CommentIndex.where(id: 1, message: "hello")
CommentIndex.where(state: nil)

Parameters:

  • hash (Hash)

    A field-to-filter mapping specifying filter values for the respective fields

Returns:



54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/search_flip/filterable.rb', line 54

def where(hash)
  hash.inject(fresh) do |memo, (key, value)|
    if value.is_a?(Array)
      memo.filter(terms: { key => value })
    elsif value.is_a?(Range)
      memo.filter(range: { key => { gte: value.min, lte: value.max } })
    elsif value.nil?
      memo.must_not(exists: { field: key })
    else
      memo.filter(term: { key => value })
    end
  end
end

#where_not(hash) ⇒ SearchFlip::Criteria

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

Examples:

CommentIndex.where_not(state: "approved")
CommentIndex.where_not(created_at: Time.parse("2016-01-01") .. Time.parse("2017-01-01"))
CommentIndex.where_not(id: [1, 2, 3], state: "new")
CommentIndex.where_not(state: nil)

Parameters:

  • hash (Hash)

    A field-to-filter mapping specifying filter values for the respective fields

Returns:

See Also:



85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/search_flip/filterable.rb', line 85

def where_not(hash)
  hash.inject(fresh) do |memo, (key, value)|
    if value.is_a?(Array)
      memo.must_not(terms: { key => value })
    elsif value.is_a?(Range)
      memo.must_not(range: { key => { gte: value.min, lte: value.max } })
    elsif value.nil?
      memo.filter(exists: { field: key })
    else
      memo.must_not(term: { key => value })
    end
  end
end