search_flip 2.3.2 → 3.0.0.beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  # The SearchFlip::Criteria class serves the purpose of chaining various
4
3
  # filtering and aggregation methods. Each chainable method creates a new
@@ -14,15 +13,15 @@ module SearchFlip
14
13
  # CommentIndex.sort("_doc").find_each { |comment| "..." }
15
14
 
16
15
  class Criteria
17
- include SearchFlip::Filterable
18
- include SearchFlip::PostFilterable
19
- include SearchFlip::Aggregatable
16
+ include Filterable
17
+ include PostFilterable
18
+ include Aggregatable
20
19
  extend Forwardable
21
20
 
22
21
  attr_accessor :target, :profile_value, :source_value, :sort_values, :highlight_values, :suggest_values,
23
22
  :offset_value, :limit_value, :includes_values, :eager_load_values, :preload_values, :failsafe_value,
24
- :scroll_args, :custom_value, :terminate_after_value, :timeout_value, :preference_value,
25
- :search_type_value, :routing_value, :track_total_hits_value, :explain_value
23
+ :scroll_args, :custom_value, :terminate_after_value, :timeout_value, :preference_value, :search_type_value,
24
+ :routing_value, :track_total_hits_value, :explain_value
26
25
 
27
26
  # Creates a new criteria while merging the attributes (constraints,
28
27
  # settings, etc) of the current criteria with the attributes of another one
@@ -58,15 +57,11 @@ module SearchFlip
58
57
  criteria.includes_values = (criteria.includes_values || []) + other.includes_values if other.includes_values
59
58
  criteria.preload_values = (criteria.preload_values || []) + other.preload_values if other.preload_values
60
59
  criteria.eager_load_values = (criteria.eager_load_values || []) + other.eager_load_values if other.eager_load_values
61
- criteria.search_values = (criteria.search_values || []) + other.search_values if other.search_values
62
60
  criteria.must_values = (criteria.must_values || []) + other.must_values if other.must_values
63
61
  criteria.must_not_values = (criteria.must_not_values || []) + other.must_not_values if other.must_not_values
64
- criteria.should_values = (criteria.should_values || []) + other.should_values if other.should_values
65
62
  criteria.filter_values = (criteria.filter_values || []) + other.filter_values if other.filter_values
66
- criteria.post_search_values = (criteria.post_search_values || []) + other.post_search_values if other.post_search_values
67
63
  criteria.post_must_values = (criteria.post_must_values || []) + other.post_must_values if other.post_must_values
68
64
  criteria.post_must_not_values = (criteria.post_must_not_values || []) + other.post_must_not_values if other.post_must_not_values
69
- criteria.post_should_values = (criteria.post_should_values || []) + other.post_should_values if other.post_should_values
70
65
  criteria.post_filter_values = (criteria.post_filter_values || []) + other.post_filter_values if other.post_filter_values
71
66
 
72
67
  criteria.highlight_values = (criteria.highlight_values || {}).merge(other.highlight_values) if other.highlight_values
@@ -190,37 +185,6 @@ module SearchFlip
190
185
  end
191
186
  end
192
187
 
193
- # Creates a new criteria while removing all specified scopes. Currently,
194
- # you can unscope :search, :post_search, :sort, :highlight, :suggest, :custom
195
- # and :aggregate.
196
- #
197
- # @example
198
- # CommentIndex.search("hello world").aggregate(:username).unscope(:search, :aggregate)
199
- #
200
- # @param scopes [Symbol] All scopes that you want to remove
201
- #
202
- # @return [SearchFlip::Criteria] A newly created extended criteria
203
-
204
- def unscope(*scopes)
205
- warn "[DEPRECATION] unscope is deprecated and will be removed in search_flip 3"
206
-
207
- unknown = scopes - [:search, :post_search, :sort, :highlight, :suggest, :custom, :aggregate]
208
-
209
- raise(ArgumentError, "Can't unscope #{unknown.join(", ")}") if unknown.size > 0
210
-
211
- scopes = scopes.to_set
212
-
213
- fresh.tap do |criteria|
214
- criteria.search_values = nil if scopes.include?(:search)
215
- criteria.post_search_values = nil if scopes.include?(:post_search)
216
- criteria.sort_values = nil if scopes.include?(:sort)
217
- criteria.hightlight_values = nil if scopes.include?(:highlight)
218
- criteria.suggest_values = nil if scopes.include?(:suggest)
219
- criteria.custom_values = nil if scopes.include?(:custom)
220
- criteria.aggregation_values = nil if scopes.include?(:aggregate)
221
- end
222
- end
223
-
224
188
  # @api private
225
189
  #
226
190
  # Convenience method to have a unified conversion api.
@@ -231,6 +195,8 @@ module SearchFlip
231
195
  self
232
196
  end
233
197
 
198
+ alias_method :all, :criteria
199
+
234
200
  # Creates a new SearchFlip::Criteria.
235
201
  #
236
202
  # @param attributes [Hash] Attributes to initialize the Criteria with
@@ -251,7 +217,7 @@ module SearchFlip
251
217
  #
252
218
  # @return [SearchFlip::Criteria] Simply returns self
253
219
 
254
- ruby2_keywords def with_settings(*args)
220
+ def with_settings(*args)
255
221
  fresh.tap do |criteria|
256
222
  criteria.target = target.with_settings(*args)
257
223
  end
@@ -266,36 +232,17 @@ module SearchFlip
266
232
  def request
267
233
  res = {}
268
234
 
269
- if must_values || search_values || must_not_values || should_values || filter_values
270
- if connection.version.to_i >= 2
271
- res[:query] = {
272
- bool: {}
273
- .merge(must_values || search_values ? { must: (must_values || []) + (search_values || []) } : {})
274
- .merge(must_not_values ? { must_not: must_not_values } : {})
275
- .merge(should_values ? { should: should_values } : {})
276
- .merge(filter_values ? { filter: filter_values } : {})
277
- }
278
- else
279
- filters = (filter_values || []) + (must_not_values || []).map { |must_not_value| { not: must_not_value } }
280
-
281
- queries = {}
282
- .merge(must_values || search_values ? { must: (must_values || []) + (search_values || []) } : {})
283
- .merge(should_values ? { should: should_values } : {})
284
-
285
- res[:query] =
286
- if filters.size > 0
287
- {
288
- filtered: {}
289
- .merge(queries.size > 0 ? { query: { bool: queries } } : {})
290
- .merge(filter: filters.size > 1 ? { and: filters } : filters.first)
291
- }
292
- else
293
- { bool: queries }
294
- end
295
- end
235
+ if must_values || must_not_values || filter_values
236
+ res[:query] = {
237
+ bool: {
238
+ must: must_values.to_a,
239
+ must_not: must_not_values.to_a,
240
+ filter: filter_values.to_a
241
+ }.reject { |_, value| value.empty? }
242
+ }
296
243
  end
297
244
 
298
- res.update from: offset_value_with_default, size: limit_value_with_default
245
+ res.update(from: offset_value_with_default, size: limit_value_with_default)
299
246
 
300
247
  res[:track_total_hits] = track_total_hits_value unless track_total_hits_value.nil?
301
248
  res[:explain] = explain_value unless explain_value.nil?
@@ -306,26 +253,14 @@ module SearchFlip
306
253
  res[:sort] = sort_values if sort_values
307
254
  res[:aggregations] = aggregation_values if aggregation_values
308
255
 
309
- if post_must_values || post_search_values || post_must_not_values || post_should_values || post_filter_values
310
- if connection.version.to_i >= 2
311
- res[:post_filter] = {
312
- bool: {}
313
- .merge(post_must_values || post_search_values ? { must: (post_must_values || []) + (post_search_values || []) } : {})
314
- .merge(post_must_not_values ? { must_not: post_must_not_values } : {})
315
- .merge(post_should_values ? { should: post_should_values } : {})
316
- .merge(post_filter_values ? { filter: post_filter_values } : {})
317
- }
318
- else
319
- post_filters = (post_filter_values || []) + (post_must_not_values || []).map { |post_must_not_value| { not: post_must_not_value } }
320
-
321
- post_queries = {}
322
- .merge(post_must_values || post_search_values ? { must: (post_must_values || []) + (post_search_values || []) } : {})
323
- .merge(post_should_values ? { should: post_should_values } : {})
324
-
325
- post_filters_and_queries = post_filters + (post_queries.size > 0 ? [bool: post_queries] : [])
326
-
327
- res[:post_filter] = post_filters_and_queries.size > 1 ? { and: post_filters_and_queries } : post_filters_and_queries.first
328
- end
256
+ if post_must_values || post_must_not_values || post_filter_values
257
+ res[:post_filter] = {
258
+ bool: {
259
+ must: post_must_values.to_a,
260
+ must_not: post_must_not_values.to_a,
261
+ filter: post_filter_values.to_a
262
+ }.reject { |_, value| value.empty? }
263
+ }
329
264
  end
330
265
 
331
266
  res[:_source] = source_value unless source_value.nil?
@@ -633,6 +568,8 @@ module SearchFlip
633
568
  end
634
569
  end
635
570
 
571
+ # @api private
572
+ #
636
573
  # Returns the offset value or, if not yet set, the default limit value (0).
637
574
  #
638
575
  # @return [Fixnum] The offset value
@@ -658,6 +595,8 @@ module SearchFlip
658
595
  end
659
596
  end
660
597
 
598
+ # @api private
599
+ #
661
600
  # Returns the limit value or, if not yet set, the default limit value (30).
662
601
  #
663
602
  # @return [Fixnum] The limit value
@@ -678,7 +617,7 @@ module SearchFlip
678
617
  #
679
618
  # @return [SearchFlip::Criteria] A newly created extended criteria
680
619
 
681
- def paginate(page:, per_page: limit_value_with_default)
620
+ def paginate(page: 1, per_page: 30)
682
621
  page = [page.to_i, 1].max
683
622
  per_page = per_page.to_i
684
623
 
@@ -686,11 +625,11 @@ module SearchFlip
686
625
  end
687
626
 
688
627
  def page(value)
689
- paginate(page: value)
628
+ paginate(page: value, per_page: limit_value_with_default)
690
629
  end
691
630
 
692
631
  def per(value)
693
- paginate(page: offset_value_with_default / limit_value_with_default + 1, per_page: value)
632
+ paginate(page: 1 + (offset_value_with_default / limit_value_with_default), per_page: value)
694
633
  end
695
634
 
696
635
  # Fetches the records specified by the criteria in batches using the
@@ -811,17 +750,11 @@ module SearchFlip
811
750
 
812
751
  http_response =
813
752
  if scroll_args && scroll_args[:id]
814
- if connection.version.to_i >= 2
815
- http_request.post(
816
- "#{connection.base_url}/_search/scroll",
817
- params: request_params,
818
- json: { scroll: scroll_args[:timeout], scroll_id: scroll_args[:id] }
819
- )
820
- else
821
- http_request
822
- .headers(content_type: "text/plain")
823
- .post("#{connection.base_url}/_search/scroll", params: request_params.merge(scroll: scroll_args[:timeout]), body: scroll_args[:id])
824
- end
753
+ http_request.post(
754
+ "#{connection.base_url}/_search/scroll",
755
+ params: request_params,
756
+ json: { scroll: scroll_args[:timeout], scroll_id: scroll_args[:id] }
757
+ )
825
758
  elsif scroll_args
826
759
  http_request.post(
827
760
  "#{target.type_url}/_search",
@@ -886,7 +819,7 @@ module SearchFlip
886
819
  target.respond_to?(name, *args)
887
820
  end
888
821
 
889
- ruby2_keywords def method_missing(name, *args, &block)
822
+ def method_missing(name, *args, &block)
890
823
  if target.respond_to?(name)
891
824
  merge(target.send(name, *args, &block))
892
825
  else
@@ -1,5 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  class MethodNotImplemented < StandardError; end
4
3
  end
5
-
@@ -1,4 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  # The SearchFlip::Filterable mixin provides chainable methods like
4
3
  # #where, #exists, #range, etc to add search filters to a criteria.
@@ -11,7 +10,7 @@ module SearchFlip
11
10
  module Filterable
12
11
  def self.included(base)
13
12
  base.class_eval do
14
- attr_accessor :search_values, :must_values, :must_not_values, :should_values, :filter_values
13
+ attr_accessor :must_values, :must_not_values, :filter_values
15
14
  end
16
15
  end
17
16
 
@@ -30,11 +29,9 @@ module SearchFlip
30
29
  # @return [SearchFlip::Criteria] A newly created extended criteria
31
30
 
32
31
  def search(q, options = {})
33
- fresh.tap do |criteria|
34
- if q.to_s.strip.length > 0
35
- criteria.search_values = (search_values || []) + [query_string: { query: q, default_operator: :AND }.merge(options)]
36
- end
37
- end
32
+ return self if q.to_s.strip.length.zero?
33
+
34
+ must(query_string: { query: q, default_operator: :AND }.merge(options))
38
35
  end
39
36
 
40
37
  # Adds filters to your criteria for the supplied hash composed of
@@ -57,13 +54,13 @@ module SearchFlip
57
54
  def where(hash)
58
55
  hash.inject(fresh) do |memo, (key, value)|
59
56
  if value.is_a?(Array)
60
- memo.filter terms: { key => value }
57
+ memo.filter(terms: { key => value })
61
58
  elsif value.is_a?(Range)
62
- memo.filter range: { key => { gte: value.min, lte: value.max } }
59
+ memo.filter(range: { key => { gte: value.min, lte: value.max } })
63
60
  elsif value.nil?
64
- memo.exists_not key
61
+ memo.must_not(exists: { field: key })
65
62
  else
66
- memo.filter term: { key => value }
63
+ memo.filter(term: { key => value })
67
64
  end
68
65
  end
69
66
  end
@@ -88,13 +85,13 @@ module SearchFlip
88
85
  def where_not(hash)
89
86
  hash.inject(fresh) do |memo, (key, value)|
90
87
  if value.is_a?(Array)
91
- memo.must_not terms: { key => value }
88
+ memo.must_not(terms: { key => value })
92
89
  elsif value.is_a?(Range)
93
- memo.must_not range: { key => { gte: value.min, lte: value.max } }
90
+ memo.must_not(range: { key => { gte: value.min, lte: value.max } })
94
91
  elsif value.nil?
95
- memo.exists key
92
+ memo.filter(exists: { field: key })
96
93
  else
97
- memo.must_not term: { key => value }
94
+ memo.must_not(term: { key => value })
98
95
  end
99
96
  end
100
97
  end
@@ -109,9 +106,9 @@ module SearchFlip
109
106
  #
110
107
  # @return [SearchFlip::Criteria] A newly created extended criteria
111
108
 
112
- def filter(*args)
109
+ def filter(clause)
113
110
  fresh.tap do |criteria|
114
- criteria.filter_values = (filter_values || []) + args
111
+ criteria.filter_values = (filter_values || []) + Helper.wrap_array(clause)
115
112
  end
116
113
  end
117
114
 
@@ -125,9 +122,9 @@ module SearchFlip
125
122
  #
126
123
  # @return [SearchFlip::Criteria] A newly created extended criteria
127
124
 
128
- def must(*args)
125
+ def must(clause, bool_options = {})
129
126
  fresh.tap do |criteria|
130
- criteria.must_values = (must_values || []) + args
127
+ criteria.must_values = (must_values || []) + Helper.wrap_array(clause)
131
128
  end
132
129
  end
133
130
 
@@ -141,28 +138,45 @@ module SearchFlip
141
138
  #
142
139
  # @return [SearchFlip::Criteria] A newly created extended criteria
143
140
 
144
- def must_not(*args)
141
+ def must_not(clause)
145
142
  fresh.tap do |criteria|
146
- criteria.must_not_values = (must_not_values || []) + args
143
+ criteria.must_not_values = (must_not_values || []) + Helper.wrap_array(clause)
147
144
  end
148
145
  end
149
146
 
150
- # Adds raw should queries to the criteria.
147
+ # Returns all added queries and filters, including post filters, as a raw
148
+ # query.
151
149
  #
152
150
  # @example
153
- # CommentIndex.should(term: { state: "new" })
154
- # CommentIndex.should(range: { created_at: { gt: Time.parse"2016-01-01") }})
151
+ # CommentIndex.where(state: "new").search("text").to_query
152
+ # # => {:bool=>{:filter=>[{:term=>{:state=>"new"}}], :must=>[{:query_string=>{:query=>"text", ...}}]}}
155
153
  #
156
- # @param args [Array, Hash] The raw should query arguments
154
+ # @return [Hash] The raw query
155
+
156
+ def to_query
157
+ {
158
+ bool: {
159
+ must: must_values.to_a,
160
+ must_not: must_not_values.to_a + post_must_not_values.to_a,
161
+ filter: post_must_values.to_a + filter_values.to_a + post_filter_values.to_a
162
+ }.reject { |_, value| value.empty? }
163
+ }
164
+ end
165
+
166
+ # Adds a raw should query to the criteria.
167
+ #
168
+ # @example
169
+ # CommentIndex.should([
170
+ # { term: { state: "new" } },
171
+ # { term: { state: "reviewed" } }
172
+ # ])
173
+ #
174
+ # @param args [Array] The raw should query arguments
157
175
  #
158
176
  # @return [SearchFlip::Criteria] A newly created extended criteria
159
177
 
160
- def should(*args)
161
- warn "[DEPRECATION] should will change in search_flip 3. Please use .must(bool: { should: ... }) until release."
162
-
163
- fresh.tap do |criteria|
164
- criteria.should_values = (should_values || []) + args
165
- end
178
+ def should(clause)
179
+ must(bool: { should: clause })
166
180
  end
167
181
 
168
182
  # Adds a range filter to the criteria without being forced to specify the
@@ -181,10 +195,10 @@ module SearchFlip
181
195
  # @return [SearchFlip::Criteria] A newly created extended criteria
182
196
 
183
197
  def range(field, options = {})
184
- filter range: { field => options }
198
+ filter(range: { field => options })
185
199
  end
186
200
 
187
- # Adds a match all filter/query to the criteria, which simply matches all
201
+ # Adds a match all filter to the criteria, which simply matches all
188
202
  # documents. This can be eg be used within filter aggregations or for
189
203
  # filter chaining. Check out the Elasticsearch docs for further details.
190
204
  #
@@ -209,7 +223,7 @@ module SearchFlip
209
223
  # @return [SearchFlip::Criteria] A newly created extended criteria
210
224
 
211
225
  def match_all(options = {})
212
- filter match_all: options
226
+ filter(match_all: options)
213
227
  end
214
228
 
215
229
  # Adds an exists filter to the criteria, which selects all documents for
@@ -223,10 +237,10 @@ module SearchFlip
223
237
  # @return [SearchFlip::Criteria] A newly created extended criteria
224
238
 
225
239
  def exists(field)
226
- filter exists: { field: field }
240
+ filter(exists: { field: field })
227
241
  end
228
242
 
229
- # Adds an exists not filter to the criteria, which selects all documents
243
+ # Adds an exists not query to the criteria, which selects all documents
230
244
  # for which the specified field's value is null.
231
245
  #
232
246
  # @example
@@ -237,8 +251,7 @@ module SearchFlip
237
251
  # @return [SearchFlip::Criteria] A newly created extended criteria
238
252
 
239
253
  def exists_not(field)
240
- must_not exists: { field: field }
254
+ must_not(exists: { field: field })
241
255
  end
242
256
  end
243
257
  end
244
-
@@ -0,0 +1,13 @@
1
+ module SearchFlip
2
+ module Helper
3
+ def self.wrap_array(object)
4
+ if object.nil?
5
+ []
6
+ elsif object.respond_to?(:to_ary)
7
+ object.to_ary || [object]
8
+ else
9
+ [object]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,4 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  # @api private
4
3
  #
@@ -26,21 +25,17 @@ module SearchFlip
26
25
  client.request = request.send(method, *args)
27
26
  end
28
27
  end
29
-
30
- ruby2_keywords method
31
28
  end
32
29
 
33
30
  [:get, :post, :put, :delete, :head].each do |method|
34
31
  define_method method do |*args|
35
32
  execute(method, *args)
36
33
  end
37
-
38
- ruby2_keywords method
39
34
  end
40
35
 
41
36
  private
42
37
 
43
- ruby2_keywords def execute(method, *args)
38
+ def execute(method, *args)
44
39
  response = request.send(method, *args)
45
40
 
46
41
  raise SearchFlip::ResponseError.new(code: response.code, body: response.body.to_s) unless response.status.success?
@@ -51,4 +46,3 @@ module SearchFlip
51
46
  end
52
47
  end
53
48
  end
54
-
@@ -1,4 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  # The SearchFlip::Index mixin makes your class correspond to an
4
3
  # Elasticsearch index. Your class can then create or delete the index, modify
@@ -248,13 +247,13 @@ module SearchFlip
248
247
  SearchFlip::Criteria.new(target: self)
249
248
  end
250
249
 
251
- def_delegators :criteria, :profile, :where, :where_not, :filter, :range, :match_all, :exists,
250
+ def_delegators :criteria, :all, :profile, :where, :where_not, :filter, :range, :match_all, :exists,
252
251
  :exists_not, :post_where, :post_where_not, :post_range, :post_exists, :post_exists_not,
253
252
  :post_filter, :post_must, :post_must_not, :post_should, :aggregate, :scroll, :source,
254
253
  :includes, :eager_load, :preload, :sort, :resort, :order, :reorder, :offset, :limit, :paginate,
255
254
  :page, :per, :search, :highlight, :suggest, :custom, :find_in_batches, :find_results_in_batches,
256
255
  :find_each, :find_each_result, :failsafe, :total_entries, :total_count, :timeout, :terminate_after,
257
- :records, :results, :should, :should_not, :must, :must_not, :preference, :search_type, :routing,
256
+ :records, :results, :must, :must_not, :should, :preference, :search_type, :routing,
258
257
  :track_total_hits, :explain
259
258
 
260
259
  # Override to specify the type name used within Elasticsearch. Recap,
@@ -354,6 +353,24 @@ module SearchFlip
354
353
  connection.open_index(index_name_with_prefix)
355
354
  end
356
355
 
356
+ # Freezes the index within Elasticsearch. Raises SearchFlip::ResponseError
357
+ # in case any errors occur.
358
+ #
359
+ # @return [Boolean] Returns true or raises SearchFlip::ResponseError
360
+
361
+ def freeze_index
362
+ connection.freeze_index(index_name_with_prefix)
363
+ end
364
+
365
+ # Unfreezes the index within Elasticsearch. Raises SearchFlip::ResponseError
366
+ # in case any errors occur.
367
+ #
368
+ # @return [Boolean] Returns true or raises SearchFlip::ResponseError
369
+
370
+ def unfreeze_index
371
+ connection.unfreeze_index(index_name_with_prefix)
372
+ end
373
+
357
374
  # Updates the index settings within Elasticsearch according to the index
358
375
  # settings specified. Raises SearchFlip::ResponseError in case any
359
376
  # errors occur.
@@ -481,7 +498,7 @@ module SearchFlip
481
498
  #
482
499
  # @see #index See #index for more details
483
500
 
484
- ruby2_keywords def import(*args)
501
+ def import(*args)
485
502
  index(*args)
486
503
  end
487
504
 
@@ -1,4 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  class JSON
4
3
  @default_options = {
@@ -15,4 +14,3 @@ module SearchFlip
15
14
  end
16
15
  end
17
16
  end
18
-
@@ -1,4 +1,3 @@
1
-
2
1
  module SearchFlip
3
2
  module Model
4
3
  def self.included(base)
@@ -18,4 +17,3 @@ module SearchFlip
18
17
  end
19
18
  end
20
19
  end
21
-