stretchy-model 0.6.5 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (165) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +2 -1
  3. data/README.md +28 -10
  4. data/Rakefile +56 -0
  5. data/docs/.nojekyll +0 -0
  6. data/docs/README.md +147 -0
  7. data/docs/_coverpage.md +14 -0
  8. data/docs/_sidebar.md +14 -0
  9. data/docs/examples/_sidebar.md +15 -0
  10. data/docs/examples/data_analysis.md +216 -0
  11. data/docs/examples/semantic_search_with_llm.md +83 -0
  12. data/docs/examples/simple-ingest-pipeline.md +326 -0
  13. data/docs/guides/_sidebar.md +14 -0
  14. data/docs/guides/aggregations.md +142 -0
  15. data/docs/guides/machine-learning.md +154 -0
  16. data/docs/guides/models.md +372 -0
  17. data/docs/guides/pipelines.md +151 -0
  18. data/docs/guides/querying.md +361 -0
  19. data/docs/guides/quick-start.md +72 -0
  20. data/docs/guides/scopes.md +125 -0
  21. data/docs/index.html +113 -0
  22. data/docs/stretchy.cover.png +0 -0
  23. data/docs/stretchy.logo.png +0 -0
  24. data/docs/styles.css +90 -0
  25. data/lib/stretchy/attributes/transformers/keyword_transformer.rb +41 -35
  26. data/lib/stretchy/attributes/type/array.rb +24 -1
  27. data/lib/stretchy/attributes/type/base.rb +6 -2
  28. data/lib/stretchy/attributes/type/binary.rb +24 -17
  29. data/lib/stretchy/attributes/type/boolean.rb +29 -22
  30. data/lib/stretchy/attributes/type/completion.rb +18 -10
  31. data/lib/stretchy/attributes/type/constant_keyword.rb +35 -26
  32. data/lib/stretchy/attributes/type/date_time.rb +28 -17
  33. data/lib/stretchy/attributes/type/dense_vector.rb +46 -49
  34. data/lib/stretchy/attributes/type/flattened.rb +28 -19
  35. data/lib/stretchy/attributes/type/geo_point.rb +21 -12
  36. data/lib/stretchy/attributes/type/geo_shape.rb +21 -12
  37. data/lib/stretchy/attributes/type/hash.rb +24 -10
  38. data/lib/stretchy/attributes/type/histogram.rb +25 -0
  39. data/lib/stretchy/attributes/type/ip.rb +26 -17
  40. data/lib/stretchy/attributes/type/join.rb +16 -7
  41. data/lib/stretchy/attributes/type/keyword.rb +21 -26
  42. data/lib/stretchy/attributes/type/knn_vector.rb +47 -0
  43. data/lib/stretchy/attributes/type/match_only_text.rb +22 -1
  44. data/lib/stretchy/attributes/type/nested.rb +16 -11
  45. data/lib/stretchy/attributes/type/numeric/base.rb +30 -22
  46. data/lib/stretchy/attributes/type/numeric/byte.rb +20 -0
  47. data/lib/stretchy/attributes/type/numeric/double.rb +20 -0
  48. data/lib/stretchy/attributes/type/numeric/float.rb +20 -0
  49. data/lib/stretchy/attributes/type/numeric/half_float.rb +20 -0
  50. data/lib/stretchy/attributes/type/numeric/integer.rb +21 -1
  51. data/lib/stretchy/attributes/type/numeric/long.rb +20 -0
  52. data/lib/stretchy/attributes/type/numeric/scaled_float.rb +16 -7
  53. data/lib/stretchy/attributes/type/numeric/short.rb +20 -0
  54. data/lib/stretchy/attributes/type/numeric/unsigned_long.rb +21 -1
  55. data/lib/stretchy/attributes/type/percolator.rb +16 -4
  56. data/lib/stretchy/attributes/type/point.rb +19 -9
  57. data/lib/stretchy/attributes/type/range/base.rb +24 -1
  58. data/lib/stretchy/attributes/type/range/date_range.rb +21 -5
  59. data/lib/stretchy/attributes/type/range/double_range.rb +20 -4
  60. data/lib/stretchy/attributes/type/range/float_range.rb +21 -5
  61. data/lib/stretchy/attributes/type/range/integer_range.rb +20 -4
  62. data/lib/stretchy/attributes/type/range/ip_range.rb +20 -4
  63. data/lib/stretchy/attributes/type/range/long_range.rb +20 -4
  64. data/lib/stretchy/attributes/type/rank_feature.rb +16 -6
  65. data/lib/stretchy/attributes/type/rank_features.rb +16 -9
  66. data/lib/stretchy/attributes/type/search_as_you_type.rb +28 -18
  67. data/lib/stretchy/attributes/type/shape.rb +19 -9
  68. data/lib/stretchy/attributes/type/sparse_vector.rb +25 -21
  69. data/lib/stretchy/attributes/type/string.rb +42 -1
  70. data/lib/stretchy/attributes/type/text.rb +53 -28
  71. data/lib/stretchy/attributes/type/token_count.rb +21 -11
  72. data/lib/stretchy/attributes/type/version.rb +16 -6
  73. data/lib/stretchy/attributes/type/wildcard.rb +36 -25
  74. data/lib/stretchy/attributes.rb +29 -0
  75. data/lib/stretchy/delegation/gateway_delegation.rb +78 -0
  76. data/lib/stretchy/index_setting.rb +94 -0
  77. data/lib/stretchy/indexing/bulk.rb +75 -3
  78. data/lib/stretchy/model/callbacks.rb +1 -0
  79. data/lib/stretchy/model/common.rb +157 -0
  80. data/lib/stretchy/model/persistence.rb +144 -0
  81. data/lib/stretchy/model/refreshable.rb +26 -0
  82. data/lib/stretchy/pipeline.rb +2 -1
  83. data/lib/stretchy/pipelines/processor.rb +38 -36
  84. data/lib/stretchy/querying.rb +7 -8
  85. data/lib/stretchy/record.rb +5 -4
  86. data/lib/stretchy/relation.rb +229 -28
  87. data/lib/stretchy/relations/aggregation_methods/aggregation.rb +59 -0
  88. data/lib/stretchy/relations/aggregation_methods/avg.rb +45 -0
  89. data/lib/stretchy/relations/aggregation_methods/bucket_script.rb +47 -0
  90. data/lib/stretchy/relations/aggregation_methods/bucket_selector.rb +47 -0
  91. data/lib/stretchy/relations/aggregation_methods/bucket_sort.rb +47 -0
  92. data/lib/stretchy/relations/aggregation_methods/cardinality.rb +47 -0
  93. data/lib/stretchy/relations/aggregation_methods/children.rb +47 -0
  94. data/lib/stretchy/relations/aggregation_methods/composite.rb +41 -0
  95. data/lib/stretchy/relations/aggregation_methods/date_histogram.rb +53 -0
  96. data/lib/stretchy/relations/aggregation_methods/date_range.rb +53 -0
  97. data/lib/stretchy/relations/aggregation_methods/extended_stats.rb +48 -0
  98. data/lib/stretchy/relations/aggregation_methods/filter.rb +47 -0
  99. data/lib/stretchy/relations/aggregation_methods/filters.rb +47 -0
  100. data/lib/stretchy/relations/aggregation_methods/geo_bounds.rb +40 -0
  101. data/lib/stretchy/relations/aggregation_methods/geo_centroid.rb +40 -0
  102. data/lib/stretchy/relations/aggregation_methods/global.rb +39 -0
  103. data/lib/stretchy/relations/aggregation_methods/histogram.rb +43 -0
  104. data/lib/stretchy/relations/aggregation_methods/ip_range.rb +41 -0
  105. data/lib/stretchy/relations/aggregation_methods/max.rb +40 -0
  106. data/lib/stretchy/relations/aggregation_methods/min.rb +41 -0
  107. data/lib/stretchy/relations/aggregation_methods/missing.rb +40 -0
  108. data/lib/stretchy/relations/aggregation_methods/nested.rb +40 -0
  109. data/lib/stretchy/relations/aggregation_methods/percentile_ranks.rb +45 -0
  110. data/lib/stretchy/relations/aggregation_methods/percentiles.rb +45 -0
  111. data/lib/stretchy/relations/aggregation_methods/range.rb +42 -0
  112. data/lib/stretchy/relations/aggregation_methods/reverse_nested.rb +40 -0
  113. data/lib/stretchy/relations/aggregation_methods/sampler.rb +40 -0
  114. data/lib/stretchy/relations/aggregation_methods/scripted_metric.rb +43 -0
  115. data/lib/stretchy/relations/aggregation_methods/significant_terms.rb +45 -0
  116. data/lib/stretchy/relations/aggregation_methods/stats.rb +42 -0
  117. data/lib/stretchy/relations/aggregation_methods/sum.rb +42 -0
  118. data/lib/stretchy/relations/aggregation_methods/terms.rb +46 -0
  119. data/lib/stretchy/relations/aggregation_methods/top_hits.rb +42 -0
  120. data/lib/stretchy/relations/aggregation_methods/top_metrics.rb +44 -0
  121. data/lib/stretchy/relations/aggregation_methods/value_count.rb +41 -0
  122. data/lib/stretchy/relations/aggregation_methods/weighted_avg.rb +42 -0
  123. data/lib/stretchy/relations/aggregation_methods.rb +20 -749
  124. data/lib/stretchy/relations/finder_methods.rb +2 -18
  125. data/lib/stretchy/relations/null_relation.rb +55 -0
  126. data/lib/stretchy/relations/query_builder.rb +82 -36
  127. data/lib/stretchy/relations/query_methods/bind.rb +19 -0
  128. data/lib/stretchy/relations/query_methods/extending.rb +29 -0
  129. data/lib/stretchy/relations/query_methods/fields.rb +70 -0
  130. data/lib/stretchy/relations/query_methods/filter_query.rb +53 -0
  131. data/lib/stretchy/relations/query_methods/has_field.rb +40 -0
  132. data/lib/stretchy/relations/query_methods/highlight.rb +75 -0
  133. data/lib/stretchy/relations/query_methods/hybrid.rb +60 -0
  134. data/lib/stretchy/relations/query_methods/ids.rb +40 -0
  135. data/lib/stretchy/relations/query_methods/match.rb +52 -0
  136. data/lib/stretchy/relations/query_methods/must_not.rb +54 -0
  137. data/lib/stretchy/relations/query_methods/neural.rb +58 -0
  138. data/lib/stretchy/relations/query_methods/neural_sparse.rb +43 -0
  139. data/lib/stretchy/relations/query_methods/none.rb +21 -0
  140. data/lib/stretchy/relations/query_methods/or_filter.rb +21 -0
  141. data/lib/stretchy/relations/query_methods/order.rb +63 -0
  142. data/lib/stretchy/relations/query_methods/query_string.rb +44 -0
  143. data/lib/stretchy/relations/query_methods/regexp.rb +61 -0
  144. data/lib/stretchy/relations/query_methods/should.rb +51 -0
  145. data/lib/stretchy/relations/query_methods/size.rb +44 -0
  146. data/lib/stretchy/relations/query_methods/skip_callbacks.rb +47 -0
  147. data/lib/stretchy/relations/query_methods/source.rb +59 -0
  148. data/lib/stretchy/relations/query_methods/where.rb +113 -0
  149. data/lib/stretchy/relations/query_methods.rb +48 -569
  150. data/lib/stretchy/relations/scoping/default.rb +136 -0
  151. data/lib/stretchy/relations/scoping/named.rb +70 -0
  152. data/lib/stretchy/relations/scoping/scope_registry.rb +36 -0
  153. data/lib/stretchy/relations/scoping.rb +30 -0
  154. data/lib/stretchy/relations/search_option_methods.rb +2 -0
  155. data/lib/stretchy/version.rb +1 -1
  156. data/lib/stretchy.rb +17 -10
  157. metadata +111 -17
  158. data/lib/stretchy/common.rb +0 -38
  159. data/lib/stretchy/null_relation.rb +0 -53
  160. data/lib/stretchy/persistence.rb +0 -43
  161. data/lib/stretchy/refreshable.rb +0 -15
  162. data/lib/stretchy/scoping/default.rb +0 -134
  163. data/lib/stretchy/scoping/named.rb +0 -68
  164. data/lib/stretchy/scoping/scope_registry.rb +0 -34
  165. data/lib/stretchy/scoping.rb +0 -28
@@ -3,11 +3,31 @@ module Stretchy
3
3
  module QueryMethods
4
4
  extend ActiveSupport::Concern
5
5
 
6
+ @_registry = []
6
7
 
7
- MULTI_VALUE_METHODS = [
8
+ class << self
9
+ # Define the register! method
10
+ def register!(*methods)
11
+ @_registry += methods
12
+ end
13
+
14
+ # Define a method to access the registry
15
+ def registry
16
+ @_registry.flatten.compact.uniq
17
+ end
18
+ end
19
+ # Load all the query methods
20
+ Dir["#{File.dirname(__FILE__)}/query_methods/*.rb"].each do |file|
21
+ basename = File.basename(file, '.rb')
22
+ module_name = basename.split('_').collect(&:capitalize).join
23
+ mod = const_get(module_name)
24
+ include mod
25
+ end
26
+
27
+ MULTI_VALUE_METHODS = [
8
28
  :where,
9
29
  :order,
10
- :field,
30
+ :fields,
11
31
  :highlight,
12
32
  :source,
13
33
  :must_not,
@@ -19,556 +39,51 @@ module Stretchy
19
39
  :or_filter,
20
40
  :extending,
21
41
  :skip_callbacks,
42
+ :match,
22
43
  :neural_sparse,
23
44
  :neural,
24
45
  :hybrid,
25
- :regexp
46
+ :regexp,
47
+ :ids
26
48
  ]
27
49
 
28
- SINGLE_VALUE_METHODS = [:size]
29
-
30
- class WhereChain
31
- def initialize(scope)
32
- @scope = scope
33
- end
34
- end
35
-
36
-
37
50
  MULTI_VALUE_METHODS.each do |name|
38
51
  class_eval <<-CODE, __FILE__, __LINE__ + 1
39
- def #{name}_values # def select_values
40
- @values[:#{name}] || [] # @values[:select] || []
41
- end # end
42
- #
43
- def #{name}_values=(values) # def select_values=(values)
44
- raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
45
- @values[:#{name}] = values # @values[:select] = values
46
- end # end
52
+ def #{name}_values
53
+ @values[:#{name}] || []
54
+ end
55
+
56
+ def #{name}_values=(values)
57
+ raise ImmutableRelation if @loaded
58
+ @values[:#{name}] = values
59
+ end
47
60
  CODE
48
61
  end
49
62
 
63
+ SINGLE_VALUE_METHODS = [:size]
64
+
50
65
  SINGLE_VALUE_METHODS.each do |name|
51
66
  class_eval <<-CODE, __FILE__, __LINE__ + 1
52
- def #{name}_value # def readonly_value
53
- @values[:#{name}] # @values[:readonly]
54
- end # end
67
+ def #{name}_value
68
+ @values[:#{name}]
69
+ end
55
70
  CODE
56
71
  end
57
72
 
73
+ # def readonly_value=(value)
74
+ # raise ImmutableRelation if @loaded
75
+ # @values[:readonly] = value
76
+ # end
58
77
  SINGLE_VALUE_METHODS.each do |name|
59
78
  class_eval <<-CODE, __FILE__, __LINE__ + 1
60
- def #{name}_value=(value) # def readonly_value=(value)
61
- raise ImmutableRelation if @loaded # raise ImmutableRelation if @loaded
62
- @values[:#{name}] = value # @values[:readonly] = value
63
- end # end
79
+ def #{name}_value=(value)
80
+ raise ImmutableRelation if @loaded
81
+ @values[:#{name}] = value
82
+ end
64
83
  CODE
65
84
  end
66
85
 
67
86
 
68
- # Allows you to add one or more sorts on specified fields.
69
- #
70
- # @overload order(attribute: direction, ...)
71
- # @param attribute [Symbol] the attribute to sort by
72
- # @param direction [Symbol] the direction to sort in (:asc or :desc)
73
- #
74
- # @overload order(attribute: {order: direction, mode: mode, ...}, ...)
75
- # @param params [Hash] attributes to sort by
76
- # @param params [Symbol] :attribute the attribute name as key to sort by
77
- # @param options [Hash] a hash containing possible sorting options
78
- # @option options [Symbol] :order the direction to sort in (:asc or :desc)
79
- # @option options [Symbol] :mode the mode to use for sorting (:avg, :min, :max, :sum, :median)
80
- # @option options [Symbol] :numeric_type the numeric type to use for sorting (:double, :long, :date, :date_nanos)
81
- # @option options [Symbol] :missing the value to use for documents without the field
82
- # @option options [Hash] :nested the nested sorting options
83
- # @option nested [String] :path the path to the nested object
84
- # @option nested [Hash] :filter the filter to apply to the nested object
85
- # @option nested [Hash] :max_children the maximum number of children to consider per root document when picking the sort value. Defaults to unlimited
86
- #
87
- # @example
88
- # Model.order(created_at: :asc)
89
- # # Elasticsearch equivalent
90
- # #=> "sort" : [{"created_at" : "asc"}]
91
- #
92
- # Model.order(age: :desc, name: :asc, price: {order: :desc, mode: :avg})
93
- #
94
- # # Elasticsearch equivalent
95
- # #=> "sort" : [
96
- # { "price" : {"order" : "desc", "mode": "avg"}},
97
- # { "name" : "asc" },
98
- # { "age" : "desc" }
99
- # ]
100
- #
101
- # @return [Stretchy::Relation] a new relation with the specified order
102
- # @see #sort
103
- def order(*args)
104
- check_if_method_has_arguments!(:order, args)
105
- spawn.order!(*args)
106
- end
107
-
108
- def order!(*args) # :nodoc:
109
- self.order_values += args.first.zip.map(&:to_h)
110
- self
111
- end
112
-
113
- # Alias for {#order}
114
- # @see #order
115
- alias :sort :order
116
-
117
-
118
- # Allows you to skip callbacks for the specified fields that are added by query_must_have for
119
- # the current query.
120
- #
121
- # @example
122
- # Model.skip_callbacks(:routing)
123
- def skip_callbacks(*args)
124
- spawn.skip_callbacks!(*args)
125
- end
126
-
127
- def skip_callbacks!(*args) # :nodoc:
128
- self.skip_callbacks_values += args
129
- self
130
- end
131
-
132
- alias :sort :order
133
-
134
-
135
- # Sets the maximum number of records to be retrieved.
136
- #
137
- # @param args [Integer] the maximum number of records to retrieve
138
- #
139
- # @example
140
- # Model.size(10)
141
- #
142
- # @return [ActiveRecord::Relation] a new relation, which reflects the limit
143
- # @see #limit
144
- def size(args)
145
- spawn.size!(args)
146
- end
147
-
148
- def size!(args) # :nodoc:
149
- self.size_value = args
150
- self
151
- end
152
-
153
- # Alias for {#size}
154
- # @see #size
155
- alias :limit :size
156
-
157
-
158
-
159
-
160
- # Adds conditions to the query.
161
- #
162
- # Each argument is a hash where the key is the attribute to filter by and the value is the value to match.
163
- #
164
- # @overload where(*rest)
165
- # @param rest [Array<Hash>] keywords containing attribute-value pairs to match
166
- #
167
- # @example
168
- # Model.where(price: 10, color: :green)
169
- #
170
- # # Elasticsearch equivalent
171
- # # => "query" : {
172
- # "bool" : {
173
- # "must" : [
174
- # { "term" : { "price" : 10 } },
175
- # { "term" : { "color" : "green" } }
176
- # ]
177
- # }
178
- # }
179
- #
180
- # .where acts as a convienence method for adding conditions to the query. It can also be used to add
181
- # range , regex, terms, and id queries through shorthand parameters.
182
- #
183
- # @example
184
- # Model.where(price: {gte: 10, lte: 20})
185
- # Model.where(age: 19..33)
186
- # Model.where(color: /gr(a|e)y/)
187
- # Model.where(id: [10, 22, 18])
188
- # Model.where(names: ['John', 'Jane'])
189
- #
190
- # @return [ActiveRecord::Relation, WhereChain] a new relation, which reflects the conditions, or a WhereChain if opts is :chain
191
- # @see #must
192
- def where(opts = :chain, *rest)
193
- if opts == :chain
194
- WhereChain.new(spawn)
195
- elsif opts.blank?
196
- self
197
- else
198
- opts.each do |key, value|
199
- case value
200
- when Range
201
- between(value, key)
202
- when Hash
203
- filter_query(:range, key => value) if value.keys.any? { |k| [:gte, :lte, :gt, :lt].include?(k) }
204
- when Regexp
205
- regexp(Hash[key, value])
206
- when Array
207
- # handle ID queries
208
- # if [:id, :_id].include?(key)
209
-
210
- # else
211
- spawn.where!(opts, *rest)
212
- # end
213
- else
214
- spawn.where!(opts, *rest)
215
- end
216
- end
217
-
218
- self
219
-
220
- end
221
- end
222
-
223
-
224
- def where!(opts, *rest) # :nodoc:
225
- if opts == :chain
226
- WhereChain.new(self)
227
- else
228
- self.where_values += build_where(opts, rest)
229
- self
230
- end
231
- end
232
-
233
- # Alias for {#where}
234
- # @see #where
235
- alias :must :where
236
-
237
-
238
- concerning :Neural do
239
- def neural_sparse(opts)
240
- spawn.neural_sparse!(opts)
241
- end
242
-
243
- def neural_sparse!(opts) # :nodoc:
244
- self.neural_sparse_values += [opts]
245
- self
246
- end
247
-
248
- def neural(opts)
249
- spawn.neural!(opts)
250
- end
251
-
252
- def neural!(opts) # :nodoc:
253
- self.neural_values += [opts]
254
- self
255
- end
256
-
257
- def hybrid(opts)
258
- spawn.hybrid!(opts)
259
- end
260
-
261
- def hybrid!(opts) # :nodoc:
262
- self.hybrid_values += [opts]
263
- self
264
- end
265
- end
266
- # Adds a regexp condition to the query.
267
- #
268
- # @param field [Hash] the field to filter by and the Regexp to match
269
- # @param opts [Hash] additional options for the regexp query
270
- # - :flags [String] the flags to use for the regexp query (e.g. 'ALL')
271
- # - :use_keyword [Boolean] whether to use the .keyword field for the regexp query. Default: true
272
- # - :case_insensitive [Boolean] whether to use case insensitive matching. If the regexp has ignore case flag `/regex/i`, this is automatically set to true
273
- # - :max_determinized_states [Integer] the maximum number of states that the regexp query can produce
274
- # - :rewrite [String] the rewrite method to use for the regexp query
275
- #
276
- #
277
- # @example
278
- # Model.regexp(:name, /john|jane/)
279
- # Model.regexp(:name, /john|jane/i)
280
- # Model.regexp(:name, /john|jane/i, flags: 'ALL')
281
- #
282
- # @return [Stretchy::Relation] a new relation, which reflects the regexp condition
283
- # @see #where
284
- def regexp(args)
285
- spawn.regexp!(args)
286
- end
287
-
288
- def regexp!(args) # :nodoc:
289
- args = args.to_a
290
- target_field, regex = args.shift
291
- opts = args.to_h
292
- opts.reverse_merge!(use_keyword: true)
293
- target_field = "#{target_field}.keyword" if opts.delete(:use_keyword)
294
- opts.merge!(case_insensitive: true) if regex.casefold?
295
- self.regexp_values += [[Hash[target_field, regex.source], opts]]
296
- self
297
- end
298
-
299
-
300
-
301
- # Adds a query string to the search.
302
- #
303
- # The query string uses Elasticsearch's Query String Query syntax, which includes a series of terms and operators.
304
- # Terms can be single words or phrases. Operators include AND, OR, and NOT, among others.
305
- # Field names can be included in the query string to search for specific values in specific fields. (e.g. "eye_color: green")
306
- # The default operator between terms are treated as OR operators.
307
- #
308
- # @param query [String] the query string
309
- # @param rest [Array] additional arguments (not normally used)
310
- #
311
- # @example
312
- # Model.query_string("((big cat) OR (domestic cat)) AND NOT panther eye_color: green")
313
- #
314
- # @return [Stretchy::Relation] a new relation, which reflects the query string
315
- def query_string(opts = :chain, *rest)
316
- if opts == :chain
317
- WhereChain.new(spawn)
318
- elsif opts.blank?
319
- self
320
- else
321
- spawn.query_string!(opts, *rest)
322
- end
323
- end
324
-
325
- def query_string!(opts, *rest) # :nodoc:
326
- if opts == :chain
327
- WhereChain.new(self)
328
- else
329
- self.query_string_values += build_where(opts, rest)
330
- self
331
- end
332
- end
333
-
334
-
335
-
336
- # Adds negated conditions to the query.
337
- #
338
- # Each argument is a hash where the key is the attribute to filter by and the value is the value to exclude.
339
- #
340
- # @overload must_not(*rest)
341
- # @param rest [Array<Hash>] a hash containing attribute-value pairs to exclude
342
- #
343
- # @example
344
- # Model.must_not(color: 'blue', size: :large)
345
- #
346
- # @return [Stretchy::Relation] a new relation, which reflects the negated conditions
347
- # @see #where_not
348
- def must_not(opts = :chain, *rest)
349
- if opts == :chain
350
- WhereChain.new(spawn)
351
- elsif opts.blank?
352
- self
353
- else
354
- spawn.must_not!(opts, *rest)
355
- end
356
- end
357
-
358
-
359
- def must_not!(opts, *rest) # :nodoc:
360
- if opts == :chain
361
- WhereChain.new(self)
362
- else
363
- self.must_not_values += build_where(opts, rest)
364
- self
365
- end
366
- end
367
-
368
- # Alias for {#must_not}
369
- # @see #must_not
370
- alias :where_not :must_not
371
-
372
-
373
-
374
- # Adds optional conditions to the query.
375
- #
376
- # Each argument is a hash where the key is the attribute to filter by and the value is the value to match optionally.
377
- #
378
- # @overload should(*rest)
379
- # @param rest [Array<Hash>] additional keywords containing attribute-value pairs to match optionally
380
- #
381
- # @example
382
- # Model.should(color: :pink, size: :medium)
383
- #
384
- # @return [Stretchy::Relation] a new relation, which reflects the optional conditions
385
- def should(opts = :chain, *rest)
386
- if opts == :chain
387
- WhereChain.new(spawn)
388
- elsif opts.blank?
389
- self
390
- else
391
- spawn.should!(opts, *rest)
392
- end
393
- end
394
-
395
- def should!(opts, *rest) # :nodoc:
396
- if opts == :chain
397
- WhereChain.new(self)
398
- else
399
- self.should_values += build_where(opts, rest)
400
- self
401
- end
402
- end
403
-
404
-
405
-
406
-
407
- # @deprecated in elasticsearch 7.x+ use {#filter_query} instead
408
- def or_filter(name, options = {}, &block)
409
- spawn.or_filter!(name, options, &block)
410
- end
411
-
412
- def or_filter!(name, options = {}, &block) # :nodoc:
413
- self.or_filter_values += [{name: name, args: options}]
414
- self
415
- end
416
-
417
- # Adds a filter to the query.
418
- #
419
- # This method supports all filters supported by Elasticsearch.
420
- #
421
- # @overload filter_query(type, opts)
422
- # @param type [Symbol] the type of filter to add (:range, :term, etc.)
423
- # @param opts [Hash] a hash containing the attribute and value to filter by
424
- #
425
- # @example
426
- # Model.filter_query(:range, age: {gte: 30})
427
- # Model.filter_query(:term, color: :blue)
428
- #
429
- # @return [Stretchy::Relation] a new relation, which reflects the filter
430
- def filter_query(name, options = {}, &block)
431
- spawn.filter_query!(name, options, &block)
432
- end
433
-
434
- def filter_query!(name, options = {}, &block) # :nodoc:
435
- self.filter_query_values += [{name: name, args: options}]
436
- self
437
- end
438
-
439
-
440
-
441
-
442
-
443
-
444
-
445
-
446
- def field(*args)
447
- spawn.field!(*args)
448
- end
449
- alias :fields :field
450
-
451
- def field!(*args) # :nodoc:
452
- self.field_values += args
453
- self
454
- end
455
-
456
-
457
-
458
- # Controls which fields of the source are returned.
459
- #
460
- # This method supports source filtering, which allows you to include or exclude fields from the source.
461
- # You can specify fields directly, use wildcard patterns, or use an object containing arrays
462
- # of includes and excludes patterns.
463
- #
464
- # If the includes property is specified, only source fields that match one of its patterns are returned.
465
- # You can exclude fields from this subset using the excludes property.
466
- #
467
- # If the includes property is not specified, the entire document source is returned, excluding any
468
- # fields that match a pattern in the excludes property.
469
- #
470
- # @overload source(opts)
471
- # @param opts [Hash, Boolean] a hash containing :includes and/or :excludes arrays, or a boolean indicating whether
472
- # to include the source
473
- #
474
- # @example
475
- # Model.source(includes: [:name, :email])
476
- # Model.source(excludes: [:name, :email])
477
- # Model.source(false) # don't include source
478
- #
479
- # @return [Stretchy::Relation] a new relation, which reflects the source filtering
480
- def source(*args)
481
- spawn.source!(*args)
482
- end
483
-
484
- def source!(*args) # :nodoc:
485
- self.source_values += args
486
- self
487
- end
488
-
489
-
490
-
491
- # Checks if a field exists in the documents.
492
- #
493
- # This is a helper for the exists filter in Elasticsearch, which returns documents
494
- # that have at least one non-null value in the specified field.
495
- #
496
- # @param field [Symbol, String] the field to check for existence
497
- #
498
- # @example
499
- # Model.has_field(:name)
500
- #
501
- # @return [ActiveRecord::Relation] a new relation, which reflects the exists filter
502
- def has_field(field)
503
- spawn.filter_query(:exists, {field: field})
504
- end
505
-
506
-
507
-
508
-
509
- def bind(value)
510
- spawn.bind!(value)
511
- end
512
-
513
- def bind!(value) # :nodoc:
514
- self.bind_values += [value]
515
- self
516
- end
517
-
518
-
519
-
520
-
521
-
522
- # Highlights the specified fields in the search results.
523
- #
524
- # @example
525
- # Model.where(body: "turkey").highlight(:body)
526
- #
527
- # @param [Hash] args The fields to highlight. Each field is a key in the hash,
528
- # and the value is another hash specifying the type of highlighting.
529
- # For example, `{body: {type: :plain}}` will highlight the 'body' field
530
- # with plain type highlighting.
531
- #
532
- # @return [Stretchy::Relation] Returns a Stretchy::Relation object, which can be used
533
- # for chaining further query methods.
534
- def highlight(*args)
535
- spawn.highlight!(*args)
536
- end
537
-
538
- def highlight!(*args) # :nodoc:
539
- self.highlight_values += args
540
- self
541
- end
542
-
543
-
544
- # Returns a chainable relation with zero records.
545
- def none
546
- extending(NullRelation)
547
- end
548
-
549
- def none! # :nodoc:
550
- extending!(NullRelation)
551
- end
552
-
553
-
554
- def extending(*modules, &block)
555
- if modules.any? || block
556
- spawn.extending!(*modules, &block)
557
- else
558
- self
559
- end
560
- end
561
-
562
- def extending!(*modules, &block) # :nodoc:
563
- modules << Module.new(&block) if block
564
- modules.flatten!
565
-
566
- self.extending_values += modules
567
- extend(*extending_values) if extending_values.any?
568
-
569
- self
570
- end
571
-
572
87
  def build_where(opts, other = [])
573
88
  case opts
574
89
  when String, Array
@@ -589,37 +104,13 @@ module Stretchy
589
104
 
590
105
  private
591
106
 
592
- # If terms are used, we assume that the field is a keyword field
593
- # and append .keyword to the field name
594
- # {terms: {field: 'gender'}}
595
- # or nested aggs
596
- # {terms: {field: 'gender'}, aggs: {name: {terms: {field: 'position.name'}}}}
597
- # should be converted to
598
- # {terms: {field: 'gender.keyword'}, aggs: {name: {terms: {field: 'position.name.keyword'}}}}
599
- # {date_histogram: {field: 'created_at', interval: 'day'}}
600
- # TODO: There may be cases where we don't want to add .keyword to the field and there should be a way to override this
601
- KEYWORD_AGGREGATION_FIELDS = [:terms, :rare_terms, :significant_terms, :cardinality, :string_stats]
602
- def assume_keyword_field(args={}, parent_match=false)
603
- if args.is_a?(Hash)
604
- args.each do |k, v|
605
- if v.is_a?(Hash)
606
- assume_keyword_field(v, KEYWORD_AGGREGATION_FIELDS.include?(k))
607
- else
608
- next unless v.is_a?(String) || v.is_a?(Symbol)
609
- args[k] = ([:field, :fields].include?(k.to_sym) && v !~ /\.keyword$/ && parent_match) ? "#{v}.keyword" : v.to_s
610
- end
611
- end
612
- end
613
- end
614
-
615
107
  def check_if_method_has_arguments!(method_name, args)
616
108
  if args.blank?
617
109
  raise ArgumentError, "The method .#{method_name}() must contain arguments."
618
110
  end
619
111
  end
620
112
 
621
- VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC,
622
- 'asc', 'desc', 'ASC', 'DESC'] # :nodoc:
113
+ VALID_DIRECTIONS = [:asc, :desc, :ASC, :DESC]
623
114
 
624
115
  def validate_order_args(args)
625
116
  args.each do |arg|
@@ -631,18 +122,6 @@ module Stretchy
631
122
  end
632
123
  end
633
124
 
634
- def add_relations_to_bind_values(attributes)
635
- if attributes.is_a?(Hash)
636
- attributes.each_value do |value|
637
- if value.is_a?(ActiveRecord::Relation)
638
- self.bind_values += value.bind_values
639
- else
640
- add_relations_to_bind_values(value)
641
- end
642
- end
643
- end
644
- end
645
- end
646
-
647
125
  end
126
+ end
648
127
  end