stretchy-model 0.6.0 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (191) 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/containers/Dockerfile.opensearch +4 -3
  6. data/docker-compose.yml +32 -19
  7. data/docs/.nojekyll +0 -0
  8. data/docs/README.md +147 -0
  9. data/docs/_coverpage.md +14 -0
  10. data/docs/_sidebar.md +14 -0
  11. data/docs/examples/_sidebar.md +15 -0
  12. data/docs/examples/data_analysis.md +216 -0
  13. data/docs/examples/semantic_search_with_llm.md +83 -0
  14. data/docs/examples/simple-ingest-pipeline.md +326 -0
  15. data/docs/guides/_sidebar.md +14 -0
  16. data/docs/guides/aggregations.md +142 -0
  17. data/docs/guides/machine-learning.md +154 -0
  18. data/docs/guides/models.md +372 -0
  19. data/docs/guides/pipelines.md +151 -0
  20. data/docs/guides/querying.md +361 -0
  21. data/docs/guides/quick-start.md +72 -0
  22. data/docs/guides/scopes.md +125 -0
  23. data/docs/index.html +113 -0
  24. data/docs/stretchy.cover.png +0 -0
  25. data/docs/stretchy.logo.png +0 -0
  26. data/docs/styles.css +90 -0
  27. data/lib/elasticsearch/api/actions/machine_learning/models/delete_model.rb +33 -0
  28. data/lib/elasticsearch/api/actions/machine_learning/models/deploy.rb +31 -0
  29. data/lib/elasticsearch/api/actions/machine_learning/models/get_model.rb +43 -0
  30. data/lib/elasticsearch/api/actions/machine_learning/models/get_status.rb +31 -0
  31. data/lib/elasticsearch/api/actions/machine_learning/models/params_registry.rb +45 -0
  32. data/lib/elasticsearch/api/actions/machine_learning/models/register.rb +45 -0
  33. data/lib/elasticsearch/api/actions/machine_learning/models/undeploy.rb +32 -0
  34. data/lib/elasticsearch/api/actions/machine_learning/models/update_model.rb +39 -0
  35. data/lib/elasticsearch/api/namespace/machine_learning/model.rb +27 -0
  36. data/lib/opensearch/api/actions/machine_learning/models/delete_model.rb +33 -0
  37. data/lib/opensearch/api/actions/machine_learning/models/deploy.rb +31 -0
  38. data/lib/opensearch/api/actions/machine_learning/models/get_model.rb +44 -0
  39. data/lib/opensearch/api/actions/machine_learning/models/get_status.rb +31 -0
  40. data/lib/opensearch/api/actions/machine_learning/models/params_registry.rb +45 -0
  41. data/lib/opensearch/api/actions/machine_learning/models/register.rb +45 -0
  42. data/lib/opensearch/api/actions/machine_learning/models/undeploy.rb +31 -0
  43. data/lib/opensearch/api/actions/machine_learning/models/update_model.rb +39 -0
  44. data/lib/opensearch/api/namespace/machine_learning/model.rb +27 -0
  45. data/lib/stretchy/attributes/transformers/keyword_transformer.rb +41 -35
  46. data/lib/stretchy/attributes/type/array.rb +24 -1
  47. data/lib/stretchy/attributes/type/base.rb +6 -2
  48. data/lib/stretchy/attributes/type/binary.rb +24 -17
  49. data/lib/stretchy/attributes/type/boolean.rb +29 -22
  50. data/lib/stretchy/attributes/type/completion.rb +18 -10
  51. data/lib/stretchy/attributes/type/constant_keyword.rb +35 -26
  52. data/lib/stretchy/attributes/type/date_time.rb +81 -20
  53. data/lib/stretchy/attributes/type/dense_vector.rb +46 -49
  54. data/lib/stretchy/attributes/type/flattened.rb +28 -19
  55. data/lib/stretchy/attributes/type/geo_point.rb +21 -12
  56. data/lib/stretchy/attributes/type/geo_shape.rb +21 -12
  57. data/lib/stretchy/attributes/type/hash.rb +24 -10
  58. data/lib/stretchy/attributes/type/histogram.rb +25 -0
  59. data/lib/stretchy/attributes/type/ip.rb +26 -17
  60. data/lib/stretchy/attributes/type/join.rb +16 -7
  61. data/lib/stretchy/attributes/type/keyword.rb +21 -26
  62. data/lib/stretchy/attributes/type/knn_vector.rb +47 -0
  63. data/lib/stretchy/attributes/type/match_only_text.rb +22 -1
  64. data/lib/stretchy/attributes/type/nested.rb +16 -11
  65. data/lib/stretchy/attributes/type/numeric/base.rb +30 -22
  66. data/lib/stretchy/attributes/type/numeric/byte.rb +20 -0
  67. data/lib/stretchy/attributes/type/numeric/double.rb +20 -0
  68. data/lib/stretchy/attributes/type/numeric/float.rb +20 -0
  69. data/lib/stretchy/attributes/type/numeric/half_float.rb +20 -0
  70. data/lib/stretchy/attributes/type/numeric/integer.rb +21 -1
  71. data/lib/stretchy/attributes/type/numeric/long.rb +20 -0
  72. data/lib/stretchy/attributes/type/numeric/scaled_float.rb +16 -7
  73. data/lib/stretchy/attributes/type/numeric/short.rb +20 -0
  74. data/lib/stretchy/attributes/type/numeric/unsigned_long.rb +21 -1
  75. data/lib/stretchy/attributes/type/percolator.rb +16 -4
  76. data/lib/stretchy/attributes/type/point.rb +19 -9
  77. data/lib/stretchy/attributes/type/range/base.rb +24 -1
  78. data/lib/stretchy/attributes/type/range/date_range.rb +21 -5
  79. data/lib/stretchy/attributes/type/range/double_range.rb +20 -4
  80. data/lib/stretchy/attributes/type/range/float_range.rb +21 -5
  81. data/lib/stretchy/attributes/type/range/integer_range.rb +20 -4
  82. data/lib/stretchy/attributes/type/range/ip_range.rb +20 -4
  83. data/lib/stretchy/attributes/type/range/long_range.rb +20 -4
  84. data/lib/stretchy/attributes/type/rank_feature.rb +16 -6
  85. data/lib/stretchy/attributes/type/rank_features.rb +27 -10
  86. data/lib/stretchy/attributes/type/search_as_you_type.rb +28 -18
  87. data/lib/stretchy/attributes/type/shape.rb +19 -9
  88. data/lib/stretchy/attributes/type/sparse_vector.rb +25 -21
  89. data/lib/stretchy/attributes/type/string.rb +42 -1
  90. data/lib/stretchy/attributes/type/text.rb +53 -28
  91. data/lib/stretchy/attributes/type/token_count.rb +21 -11
  92. data/lib/stretchy/attributes/type/version.rb +16 -6
  93. data/lib/stretchy/attributes/type/wildcard.rb +36 -25
  94. data/lib/stretchy/attributes.rb +30 -0
  95. data/lib/stretchy/delegation/gateway_delegation.rb +86 -2
  96. data/lib/stretchy/index_setting.rb +94 -0
  97. data/lib/stretchy/indexing/bulk.rb +75 -3
  98. data/lib/stretchy/machine_learning/model.rb +192 -0
  99. data/lib/stretchy/model/callbacks.rb +1 -0
  100. data/lib/stretchy/model/common.rb +157 -0
  101. data/lib/stretchy/model/persistence.rb +144 -0
  102. data/lib/stretchy/model/refreshable.rb +26 -0
  103. data/lib/stretchy/open_search_compatibility.rb +4 -0
  104. data/lib/stretchy/pipeline.rb +124 -0
  105. data/lib/stretchy/pipelines/processor.rb +57 -0
  106. data/lib/stretchy/querying.rb +7 -7
  107. data/lib/stretchy/rails/instrumentation/publishers.rb +31 -0
  108. data/lib/{rails → stretchy/rails}/instrumentation/railtie.rb +11 -6
  109. data/lib/stretchy/record.rb +5 -4
  110. data/lib/stretchy/relation.rb +230 -28
  111. data/lib/stretchy/relations/aggregation_methods/aggregation.rb +59 -0
  112. data/lib/stretchy/relations/aggregation_methods/avg.rb +45 -0
  113. data/lib/stretchy/relations/aggregation_methods/bucket_script.rb +47 -0
  114. data/lib/stretchy/relations/aggregation_methods/bucket_selector.rb +47 -0
  115. data/lib/stretchy/relations/aggregation_methods/bucket_sort.rb +47 -0
  116. data/lib/stretchy/relations/aggregation_methods/cardinality.rb +47 -0
  117. data/lib/stretchy/relations/aggregation_methods/children.rb +47 -0
  118. data/lib/stretchy/relations/aggregation_methods/composite.rb +41 -0
  119. data/lib/stretchy/relations/aggregation_methods/date_histogram.rb +53 -0
  120. data/lib/stretchy/relations/aggregation_methods/date_range.rb +53 -0
  121. data/lib/stretchy/relations/aggregation_methods/extended_stats.rb +48 -0
  122. data/lib/stretchy/relations/aggregation_methods/filter.rb +47 -0
  123. data/lib/stretchy/relations/aggregation_methods/filters.rb +47 -0
  124. data/lib/stretchy/relations/aggregation_methods/geo_bounds.rb +40 -0
  125. data/lib/stretchy/relations/aggregation_methods/geo_centroid.rb +40 -0
  126. data/lib/stretchy/relations/aggregation_methods/global.rb +39 -0
  127. data/lib/stretchy/relations/aggregation_methods/histogram.rb +43 -0
  128. data/lib/stretchy/relations/aggregation_methods/ip_range.rb +41 -0
  129. data/lib/stretchy/relations/aggregation_methods/max.rb +40 -0
  130. data/lib/stretchy/relations/aggregation_methods/min.rb +41 -0
  131. data/lib/stretchy/relations/aggregation_methods/missing.rb +40 -0
  132. data/lib/stretchy/relations/aggregation_methods/nested.rb +40 -0
  133. data/lib/stretchy/relations/aggregation_methods/percentile_ranks.rb +45 -0
  134. data/lib/stretchy/relations/aggregation_methods/percentiles.rb +45 -0
  135. data/lib/stretchy/relations/aggregation_methods/range.rb +42 -0
  136. data/lib/stretchy/relations/aggregation_methods/reverse_nested.rb +40 -0
  137. data/lib/stretchy/relations/aggregation_methods/sampler.rb +40 -0
  138. data/lib/stretchy/relations/aggregation_methods/scripted_metric.rb +43 -0
  139. data/lib/stretchy/relations/aggregation_methods/significant_terms.rb +45 -0
  140. data/lib/stretchy/relations/aggregation_methods/stats.rb +42 -0
  141. data/lib/stretchy/relations/aggregation_methods/sum.rb +42 -0
  142. data/lib/stretchy/relations/aggregation_methods/terms.rb +46 -0
  143. data/lib/stretchy/relations/aggregation_methods/top_hits.rb +42 -0
  144. data/lib/stretchy/relations/aggregation_methods/top_metrics.rb +44 -0
  145. data/lib/stretchy/relations/aggregation_methods/value_count.rb +41 -0
  146. data/lib/stretchy/relations/aggregation_methods/weighted_avg.rb +42 -0
  147. data/lib/stretchy/relations/aggregation_methods.rb +20 -749
  148. data/lib/stretchy/relations/finder_methods.rb +2 -18
  149. data/lib/stretchy/relations/null_relation.rb +55 -0
  150. data/lib/stretchy/relations/query_builder.rb +139 -23
  151. data/lib/stretchy/relations/query_methods/bind.rb +19 -0
  152. data/lib/stretchy/relations/query_methods/extending.rb +29 -0
  153. data/lib/stretchy/relations/query_methods/fields.rb +70 -0
  154. data/lib/stretchy/relations/query_methods/filter_query.rb +53 -0
  155. data/lib/stretchy/relations/query_methods/has_field.rb +40 -0
  156. data/lib/stretchy/relations/query_methods/highlight.rb +75 -0
  157. data/lib/stretchy/relations/query_methods/hybrid.rb +60 -0
  158. data/lib/stretchy/relations/query_methods/ids.rb +40 -0
  159. data/lib/stretchy/relations/query_methods/match.rb +52 -0
  160. data/lib/stretchy/relations/query_methods/must_not.rb +54 -0
  161. data/lib/stretchy/relations/query_methods/neural.rb +58 -0
  162. data/lib/stretchy/relations/query_methods/neural_sparse.rb +43 -0
  163. data/lib/stretchy/relations/query_methods/none.rb +21 -0
  164. data/lib/stretchy/relations/query_methods/or_filter.rb +21 -0
  165. data/lib/stretchy/relations/query_methods/order.rb +63 -0
  166. data/lib/stretchy/relations/query_methods/query_string.rb +44 -0
  167. data/lib/stretchy/relations/query_methods/regexp.rb +61 -0
  168. data/lib/stretchy/relations/query_methods/should.rb +51 -0
  169. data/lib/stretchy/relations/query_methods/size.rb +44 -0
  170. data/lib/stretchy/relations/query_methods/skip_callbacks.rb +47 -0
  171. data/lib/stretchy/relations/query_methods/source.rb +59 -0
  172. data/lib/stretchy/relations/query_methods/where.rb +113 -0
  173. data/lib/stretchy/relations/query_methods.rb +51 -540
  174. data/lib/stretchy/relations/scoping/default.rb +136 -0
  175. data/lib/stretchy/relations/scoping/named.rb +70 -0
  176. data/lib/stretchy/relations/scoping/scope_registry.rb +36 -0
  177. data/lib/stretchy/relations/scoping.rb +30 -0
  178. data/lib/stretchy/relations/search_option_methods.rb +2 -0
  179. data/lib/stretchy/shared_scopes.rb +6 -1
  180. data/lib/stretchy/version.rb +1 -1
  181. data/lib/stretchy.rb +23 -11
  182. metadata +147 -18
  183. data/lib/rails/instrumentation/publishers.rb +0 -29
  184. data/lib/stretchy/common.rb +0 -33
  185. data/lib/stretchy/null_relation.rb +0 -53
  186. data/lib/stretchy/persistence.rb +0 -43
  187. data/lib/stretchy/refreshable.rb +0 -15
  188. data/lib/stretchy/scoping/default.rb +0 -134
  189. data/lib/stretchy/scoping/named.rb +0 -68
  190. data/lib/stretchy/scoping/scope_registry.rb +0 -34
  191. data/lib/stretchy/scoping.rb +0 -28
@@ -3,6 +3,8 @@ module Stretchy
3
3
 
4
4
  module FinderMethods
5
5
 
6
+ METHODS = [:first, :first!, :last, :last!]
7
+
6
8
  def first
7
9
  return results.first if @loaded
8
10
  spawn.first!.results.first
@@ -39,24 +41,6 @@ module Stretchy
39
41
  self
40
42
  end
41
43
 
42
- # size is not permitted to the count API but queries are.
43
- #
44
- # if a query is supplied with `.count` then the user wants a count of the results
45
- # matching the query
46
- #
47
- # however, the default_size is used to limit the number of results returned
48
- # so we remove the size from the query and then call `.count` API
49
- #
50
- # but if the user supplies a limit, then we should assume they want a count of the results
51
- # which could lead to some confusion
52
- # suppose the user calls `.size(100).count` and the default_size is 100
53
- # if we remove size from the query, then the count could be greater than 100
54
- #
55
- # I think the best way to handle this is to remove the size from the query only if it was
56
- # applied by the default_size
57
- # If the user supplies a limit, then we should assume they want a count of the results
58
- #
59
- # if size is called with a limit,
60
44
  def count
61
45
  return results.count if @loaded || @values[:size].present?
62
46
  spawn.count!
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Stretchy
4
+ module Relations
5
+ module NullRelation # :nodoc:
6
+ def pluck(*column_names)
7
+ []
8
+ end
9
+
10
+ def delete_all
11
+ 0
12
+ end
13
+
14
+ def update_all(_updates)
15
+ 0
16
+ end
17
+
18
+ def delete(_id_or_array)
19
+ 0
20
+ end
21
+
22
+ def empty?
23
+ true
24
+ end
25
+
26
+ def none?
27
+ true
28
+ end
29
+
30
+ def any?
31
+ false
32
+ end
33
+
34
+ def one?
35
+ false
36
+ end
37
+
38
+ def many?
39
+ false
40
+ end
41
+
42
+ def exists?(_conditions = :none)
43
+ false
44
+ end
45
+
46
+ def or(other)
47
+ other.spawn
48
+ end
49
+
50
+ def exec_queries
51
+ @records = OpenStruct.new(klass: NullRelation, total: 0, results: []).freeze
52
+ end
53
+ end
54
+ end
55
+ end
@@ -27,10 +27,26 @@ module Stretchy
27
27
  @query ||= compact_where(values[:where])
28
28
  end
29
29
 
30
+ def match_query
31
+ @match_query ||= values[:match]
32
+ end
33
+
30
34
  def query_strings
31
35
  @query_string ||= compact_where(values[:query_string], bool: false)
32
36
  end
33
37
 
38
+ def neural_sparse
39
+ @neural_sparse ||= values[:neural_sparse]
40
+ end
41
+
42
+ def neural
43
+ @neural ||= values[:neural]
44
+ end
45
+
46
+ def hybrid
47
+ @hybrid ||= values[:hybrid]
48
+ end
49
+
34
50
  def must_nots
35
51
  @must_nots ||= compact_where(values[:must_not])
36
52
  end
@@ -39,12 +55,16 @@ module Stretchy
39
55
  @shoulds ||= compact_where(values[:should])
40
56
  end
41
57
 
58
+ def ids
59
+ @ids ||= values[:ids]
60
+ end
61
+
42
62
  def regexes
43
63
  @regexes ||= values[:regexp]
44
64
  end
45
65
 
46
66
  def fields
47
- values[:field]
67
+ values[:fields]
48
68
  end
49
69
 
50
70
  def source
@@ -86,14 +106,14 @@ module Stretchy
86
106
  build_highlights unless highlights.blank?
87
107
  build_fields unless fields.blank?
88
108
  build_source unless source.blank?
89
- build_aggregations unless aggregations.blank?
109
+ build_aggregations(aggregations, structure) unless aggregations.blank?
90
110
  structure.attributes!.with_indifferent_access
91
111
  end
92
112
 
93
113
  private
94
114
 
95
115
  def missing_bool_query?
96
- query.nil? && must_nots.nil? && shoulds.nil? && regexes.nil?
116
+ query.blank? && must_nots.nil? && shoulds.nil? && regexes.nil?
97
117
  end
98
118
 
99
119
  def missing_query_string?
@@ -104,12 +124,80 @@ module Stretchy
104
124
  query_filters.nil? && or_filters.nil?
105
125
  end
106
126
 
127
+ def missing_neural?
128
+ neural_sparse.nil? && neural.nil? && hybrid.nil?
129
+ end
130
+
131
+ def no_query?
132
+ missing_bool_query? && missing_query_string? && missing_query_filter? && missing_neural? && ids.nil? && match_query.nil?
133
+ end
134
+
107
135
  def build_query
108
- return if missing_bool_query? && missing_query_string? && missing_query_filter?
136
+ return if no_query?
109
137
  structure.query do
138
+ structure.ids do
139
+ structure.values ids.flatten.compact.uniq
140
+ end unless ids.nil?
141
+
142
+ structure.match do
143
+ mq = match_query.dup
144
+ field, value = mq.first.shift
145
+ structure.set! field do
146
+ structure.query value
147
+ structure.extract! mq.last, *mq.last.keys
148
+ end
149
+ end unless match_query.nil?
150
+
151
+ structure.hybrid do
152
+ structure.queries do
153
+ structure.child! do
154
+ params = hybrid[:neural].dup
155
+ field_name, query_text = params.shift
156
+ structure.neural do
157
+ structure.set! field_name do
158
+ structure.query_text query_text
159
+ structure.extract! params, *params.keys
160
+ end
161
+ end
162
+ end
163
+
164
+ structure.child! do
165
+ hybrid_query = hybrid[:query].dup
166
+ structure.extract! hybrid_query, *hybrid_query.keys
167
+ end
168
+
169
+ end
170
+ end unless hybrid.nil?
171
+
172
+ structure.neural_sparse do
173
+ neural_sparse.each do |args|
174
+ params = args.dup
175
+ field_name, query_text = params.shift
176
+ structure.set! field_name do
177
+ structure.query_text query_text
178
+ structure.extract! params, *params.keys
179
+ end
180
+ end
181
+ end unless neural_sparse.blank?
182
+
183
+ structure.neural do
184
+ neural.each do |args|
185
+ params = args.dup
186
+ field_name, query = params.shift
187
+ structure.set! field_name do
188
+ if query.is_a?(Hash)
189
+ structure.extract! query, *query.keys
190
+ else
191
+ structure.query_text query
192
+ end
193
+ structure.extract! params, *params.keys
194
+ end
195
+ end
196
+ end unless neural.blank?
197
+
110
198
  structure.regexp do
111
- build_regexp unless regexes.nil?
112
- end
199
+ build_regexp
200
+ end unless regexes.nil?
113
201
 
114
202
  structure.bool do
115
203
 
@@ -121,18 +209,17 @@ module Stretchy
121
209
 
122
210
  end unless missing_bool_query? && missing_query_filter?
123
211
 
124
-
125
-
126
212
  structure.query_string do
127
213
  structure.extract! query_string_options, *query_string_options.keys
128
214
  structure.query query_strings
129
215
  end unless query_strings.nil?
216
+
130
217
  end.with_indifferent_access
131
218
  end
132
219
 
133
220
  def build_regexp
134
221
  regexes.each do |args|
135
- target_field = args.first.keys.first
222
+ target_field = keyword_transformer.transform(args.first.keys.first.to_s)
136
223
  value_field = args.first.values.first
137
224
  structure.set! target_field, args.last.merge(value: value_field)
138
225
  end
@@ -187,10 +274,10 @@ module Stretchy
187
274
  end
188
275
  end
189
276
 
190
- def build_aggregations
191
- structure.aggregations do
192
- aggregations.each do |agg|
193
- structure.set! agg[:name], aggregation(agg[:name], keyword_transformer.transform(agg[:args], :name))
277
+ def build_aggregations(aggregation_args, aggregation_structure)
278
+ aggregation_structure.aggregations do
279
+ aggregation_args.each do |agg|
280
+ aggregation_structure.set! agg[:name], aggregation(agg[:name], keyword_transformer.transform(agg[:args], :aggs, :aggregations))
194
281
  end
195
282
  end
196
283
  end
@@ -210,7 +297,7 @@ module Stretchy
210
297
 
211
298
  def extra_search_options
212
299
  unless self.count?
213
- values[:size] = size.present? ? size : values[:default_size]
300
+ values[:size] = size.present? ? size : default_size
214
301
  else
215
302
  values[:size] = nil
216
303
  end
@@ -220,7 +307,7 @@ module Stretchy
220
307
  def compact_where(q, opts = {bool:true})
221
308
  return if q.nil?
222
309
  if opts.delete(:bool)
223
- as_must(q)
310
+ as_must([merge_and_append(q)])
224
311
  else
225
312
  as_query_string(q.flatten)
226
313
  end
@@ -231,10 +318,16 @@ module Stretchy
231
318
  q.each do |arg|
232
319
  case arg
233
320
  when Hash
234
- arg = keyword_transformer.transform(arg)
321
+ arg = keyword_transformer.transform(arg, :match)
235
322
  arg.each_pair do |k,v|
236
- # If v is an array, we build a terms query otherwise a term query
237
- _must << (v.is_a?(Array) ? {terms: Hash[k,v]} : {term: Hash[k,v]})
323
+ if k == :match
324
+ v.each do |field, value|
325
+ _must << (field.is_a?(Hash) ? { k => field} : { k => {field => value}})
326
+ end
327
+ else
328
+ # If v is an array, we build a terms query otherwise a term query
329
+ _must << (v.is_a?(Array) ? {terms: Hash[k,v]} : {term: Hash[k,v]})
330
+ end
238
331
  end
239
332
  when String
240
333
  _must << {term: Hash[[arg.split(/:/).collect(&:strip)]]}
@@ -253,12 +346,30 @@ module Stretchy
253
346
 
254
347
  q.each do |arg|
255
348
  arg.each_pair { |k,v| _and << "(#{k}:#{v})" } if arg.class == Hash
256
- _and << "(#{arg})" if arg.class == String
349
+ if q.length == 1
350
+ _and << "#{arg}" if arg.class == String
351
+ else
352
+ _and << "(#{arg})" if arg.class == String
353
+ end
257
354
  end
258
355
  _and.join(" AND ")
259
356
  end
260
357
 
261
-
358
+ def merge_and_append(queries)
359
+ result = {}
360
+ queries.each do |hash|
361
+ hash.each do |key, value|
362
+ if result[key].is_a?(Array)
363
+ result[key] << value
364
+ elsif result.key?(key)
365
+ result[key] = [result[key], value]
366
+ else
367
+ result[key] = value
368
+ end
369
+ end
370
+ end
371
+ result
372
+ end
262
373
 
263
374
  def extract_highlighter(highlighter)
264
375
  Jbuilder.new do |highlight|
@@ -280,12 +391,17 @@ module Stretchy
280
391
  end
281
392
 
282
393
  def aggregation(name, opts = {})
283
- Jbuilder.new do |agg|
394
+ Jbuilder.new do |agg_structure|
284
395
  case
285
396
  when opts.is_a?(Hash)
286
- agg.extract! opts, *opts.keys
397
+ nested_agg = opts.delete(:aggs) || opts.delete(:aggregations)
398
+
399
+ agg_structure.extract! opts, *opts.keys
400
+
401
+ build_aggregations(nested_agg.map {|d| {:name => d.first, :args => d.last } }, agg_structure) if nested_agg
402
+
287
403
  when opts.is_a?(Array)
288
- extract_filter_arguments_from_array(agg, opts)
404
+ extract_filter_arguments_from_array(agg_structure, opts)
289
405
  else
290
406
  raise "#aggregation only accepts Hash or Array"
291
407
  end
@@ -0,0 +1,19 @@
1
+ module Stretchy
2
+ module Relations
3
+ module QueryMethods
4
+ module Bind
5
+ extend ActiveSupport::Concern
6
+ def bind(value)
7
+ spawn.bind!(value)
8
+ end
9
+
10
+ def bind!(value) # :nodoc:
11
+ self.bind_values += [value]
12
+ self
13
+ end
14
+
15
+ QueryMethods.register!(:bind)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,29 @@
1
+ module Stretchy
2
+ module Relations
3
+ module QueryMethods
4
+ module Extending
5
+ extend ActiveSupport::Concern
6
+ def extending(*modules, &block)
7
+ if modules.any? || block
8
+ spawn.extending!(*modules, &block)
9
+ else
10
+ self
11
+ end
12
+ end
13
+
14
+ def extending!(*modules, &block) # :nodoc:
15
+ modules << Module.new(&block) if block
16
+ modules.flatten!
17
+
18
+ self.extending_values += modules
19
+ extend(*extending_values) if extending_values.any?
20
+
21
+ self
22
+ end
23
+
24
+ QueryMethods.register!(:extending)
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,70 @@
1
+ module Stretchy
2
+ module Relations
3
+ module QueryMethods
4
+ module Fields
5
+ extend ActiveSupport::Concern
6
+
7
+ # Specify the fields to be returned by the Elasticsearch query.
8
+ #
9
+ # This method accepts a variable number of arguments, each of which is the name of a field to be returned.
10
+ # If no arguments are provided, all fields are returned.
11
+ #
12
+ # To retrieve specific fields in the search response, use the fields parameter.
13
+ # Because it consults the index mappings, the fields parameter provides several advantages over referencing
14
+ # the `_source` directly. Specifically, the fields parameter:
15
+ # Returns each value in a standardized way that matches its mapping type
16
+ # Accepts multi-fields and field aliases
17
+ # Formats dates and spatial data types
18
+ # Retrieves runtime field values
19
+ # Returns fields calculated by a script at index time
20
+ # Returns fields from related indices using lookup runtime fields
21
+ #
22
+ # ### Parameters
23
+ # - `args:` The Array of field names to be returned by the query (default: []).
24
+ #
25
+ # ### Returns
26
+ # Returns a new Stretchy::Relation with the specified fields to be returned.
27
+ #
28
+ # ---
29
+ #
30
+ # ### Examples
31
+ #
32
+ # #### Single field
33
+ # ```ruby
34
+ # Model.fields(:title)
35
+ # ```
36
+ #
37
+ # #### Multiple fields
38
+ # ```ruby
39
+ # Model.fields(:title, :author)
40
+ # ```
41
+ #
42
+ # #### Nested fields
43
+ # ```ruby
44
+ # Model.fields('author.name', 'author.age')
45
+ # ```
46
+ #
47
+ # #### Wildcard
48
+ # ```ruby
49
+ # Model.fields('books.*')
50
+ # ```
51
+ #
52
+ def fields(*args)
53
+ spawn.field!(*args)
54
+ end
55
+
56
+ # Alias for {#field}
57
+ # @see #field
58
+ alias :field :fields
59
+
60
+ def field!(*args) # :nodoc:
61
+ self.fields_values += args
62
+ self
63
+ end
64
+
65
+ QueryMethods.register!(:field, :fields)
66
+
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,53 @@
1
+ module Stretchy
2
+ module Relations
3
+ module QueryMethods
4
+ module FilterQuery
5
+ extend ActiveSupport::Concern
6
+
7
+ # Adds a filter to the Elasticsearch query.
8
+ #
9
+ # This method is used to filter the results of a query without affecting the score. It accepts a type and a condition.
10
+ # The type can be any valid Elasticsearch filter type, such as `:term`, `:range`, or `:bool`. The condition is a hash
11
+ # that specifies the filter conditions.
12
+ #
13
+ # ### Parameters
14
+ # - `type:` The Symbol representing the filter type.
15
+ # - `condition:` The Hash containing the filter conditions.
16
+ #
17
+ # ### Returns
18
+ # Returns a new Stretchy::Relation with the specified filter applied.
19
+ #
20
+ # ---
21
+ #
22
+ # ### Examples
23
+ #
24
+ # #### Term filter
25
+ # ```ruby
26
+ # Model.filter_query(:term, color: 'blue')
27
+ # ```
28
+ #
29
+ # #### Range filter
30
+ # ```ruby
31
+ # Model.filter_query(:range, age: { gte: 21 })
32
+ # ```
33
+ #
34
+ # #### Bool filter
35
+ # ```ruby
36
+ # Model.filter_query(:bool, must: [{ term: { color: 'blue' } }, { range: { age: { gte: 21 } } }])
37
+ # ```
38
+ #
39
+ def filter_query(name, options = {}, &block)
40
+ spawn.filter_query!(name, options, &block)
41
+ end
42
+
43
+ def filter_query!(name, options = {}, &block) # :nodoc:
44
+ self.filter_query_values += [{name: name, args: options}]
45
+ self
46
+ end
47
+
48
+ QueryMethods.register!(:filter_query)
49
+
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ module Stretchy
2
+ module Relations
3
+ module QueryMethods
4
+ module HasField
5
+ extend ActiveSupport::Concern
6
+
7
+ # Checks if a field exists in the Elasticsearch document.
8
+ #
9
+ # This method is used to filter the results of a query based on whether a field exists or not in the document.
10
+ # It accepts a field name as an argument and adds an `exists` filter to the query.
11
+ #
12
+ # ### Parameters
13
+ # - `field:` The Symbol or String representing the field name.
14
+ #
15
+ # ### Returns
16
+ # Returns a new Stretchy::Relation with the `exists` filter applied.
17
+ #
18
+ # ---
19
+ #
20
+ # ### Examples
21
+ #
22
+ # #### Has field
23
+ # ```ruby
24
+ # Model.has_field(:title)
25
+ # ```
26
+ #
27
+ # #### Nested field exists
28
+ # ```ruby
29
+ # Model.has_field('author.name')
30
+ # ```
31
+ #
32
+ def has_field(field)
33
+ spawn.filter_query(:exists, {field: field})
34
+ end
35
+
36
+ QueryMethods.register!(:has_field)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,75 @@
1
+ module Stretchy
2
+ module Relations
3
+ module QueryMethods
4
+ module Highlight
5
+ extend ActiveSupport::Concern
6
+ # Highlights search results on one or more fields.
7
+ #
8
+ # This method is used to highlight search results on one or more fields. The fields that match the user query get
9
+ # highlighted. It accepts a variable number of arguments, each of which is the name of a field to be highlighted.
10
+ #
11
+ # ### Parameters
12
+ #
13
+ # - `field:` The Symbol of a field name to be highlighted by the query or a series of keyword hashes with the field name as the key and the highlight options as the value
14
+ # - `options:` The Hash of highlight options
15
+ # - `boundary_chars:` The String of the boundary characters (default: ".,!? \t\n")
16
+ # - `boundary_max_scan:` The Integer of the maximum number of characters to scan to find the boundary characters (default: 20)
17
+ # - `boundary_scanner:` The String of the boundary scanner type (default: "sentence")
18
+ # - "sentence" Breaks text into sentences
19
+ # - "word" Breaks text into words
20
+ # - "char" Breaks text into characters
21
+ # - `boundary_scanner_locale:` The String of the locale for the boundary scanner (default: "en-US")
22
+ # - `encoder:` The String of the encoder type (default: "default").
23
+ # - `fragmenter:` The String of the fragmenter type (default: "simple").
24
+ # - `force_source:` The Boolean to force highlighting on the source (default: false).
25
+ # - `fragment_offset:` The Integer of the number of characters to offset the fragment (default: 0).
26
+ # - `fragment_size:` The Integer of the number of characters in a fragment (default: 100).
27
+ # - `highlight_query:` The Hash of highlight query options (default: {}).
28
+ # - `matched_fields:` The Array of fields to be highlighted (default: []).
29
+ # - `no_match_size:` The Integer of the number of characters to return if no matches are found (default: 150).
30
+ # - `number_of_fragments:` The Integer of the number of fragments to return (default: 5).
31
+ # - `order:` The String of the order of the highlighted fragments (default: "score").
32
+ # - `phrase_limit:` The Integer of the maximum number of phrases to highlight (default: 256).
33
+ # - `pre_tags:` The String or Array of Strings to be used as the pre-highlight tags (default: "<em>").
34
+ # - `post_tags:` The String or Array of Strings to be used as the post-highlight tags (default: "</em>").
35
+ # - `require_field_match:` The Boolean to require field matching (default: true).
36
+ # - `max_analyzed_offset:` The Integer of the maximum number of characters to analyze (default: 1000000).
37
+ # - `tags_schema:` The String of the tags schema (default: "styled").
38
+ # - `type:` The String of the highlight type (default: "unified").
39
+ # * "unified" - Highlights fields based on the Unified Highlighter.
40
+ # * "plain" - Highlights fields based on the Plain Highlighter.
41
+ # * "fvh" - Highlights fields based on the Fast Vector Highlighter.
42
+ #
43
+ # ### Returns
44
+ # Returns a new Stretchy::Relation with the specified fields to be highlighted.
45
+ #
46
+ # ---
47
+ #
48
+ # ### Examples
49
+ #
50
+ # #### Single field
51
+ #
52
+ # ```
53
+ # Model.query_string("body:Cat OR Lion").highlight(:body)
54
+ # ```
55
+ #
56
+ # ### Custom highlight options
57
+ #
58
+ # ```ruby
59
+ # Model.query_string("name: Soph*").highlight(name: {pre_tags: "__", post_tags: "__"})
60
+ # ```
61
+ #
62
+ def highlight(*args)
63
+ spawn.highlight!(*args)
64
+ end
65
+
66
+ def highlight!(*args) # :nodoc:
67
+ self.highlight_values += args
68
+ self
69
+ end
70
+
71
+ QueryMethods.register!(:highlight)
72
+ end
73
+ end
74
+ end
75
+ end