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.
- checksums.yaml +4 -4
- data/.yardopts +2 -1
- data/README.md +28 -10
- data/Rakefile +56 -0
- data/docs/.nojekyll +0 -0
- data/docs/README.md +147 -0
- data/docs/_coverpage.md +14 -0
- data/docs/_sidebar.md +14 -0
- data/docs/examples/_sidebar.md +15 -0
- data/docs/examples/data_analysis.md +216 -0
- data/docs/examples/semantic_search_with_llm.md +83 -0
- data/docs/examples/simple-ingest-pipeline.md +326 -0
- data/docs/guides/_sidebar.md +14 -0
- data/docs/guides/aggregations.md +142 -0
- data/docs/guides/machine-learning.md +154 -0
- data/docs/guides/models.md +372 -0
- data/docs/guides/pipelines.md +151 -0
- data/docs/guides/querying.md +361 -0
- data/docs/guides/quick-start.md +72 -0
- data/docs/guides/scopes.md +125 -0
- data/docs/index.html +113 -0
- data/docs/stretchy.cover.png +0 -0
- data/docs/stretchy.logo.png +0 -0
- data/docs/styles.css +90 -0
- data/lib/stretchy/attributes/transformers/keyword_transformer.rb +41 -35
- data/lib/stretchy/attributes/type/array.rb +24 -1
- data/lib/stretchy/attributes/type/base.rb +6 -2
- data/lib/stretchy/attributes/type/binary.rb +24 -17
- data/lib/stretchy/attributes/type/boolean.rb +29 -22
- data/lib/stretchy/attributes/type/completion.rb +18 -10
- data/lib/stretchy/attributes/type/constant_keyword.rb +35 -26
- data/lib/stretchy/attributes/type/date_time.rb +28 -17
- data/lib/stretchy/attributes/type/dense_vector.rb +46 -49
- data/lib/stretchy/attributes/type/flattened.rb +28 -19
- data/lib/stretchy/attributes/type/geo_point.rb +21 -12
- data/lib/stretchy/attributes/type/geo_shape.rb +21 -12
- data/lib/stretchy/attributes/type/hash.rb +24 -10
- data/lib/stretchy/attributes/type/histogram.rb +25 -0
- data/lib/stretchy/attributes/type/ip.rb +26 -17
- data/lib/stretchy/attributes/type/join.rb +16 -7
- data/lib/stretchy/attributes/type/keyword.rb +21 -26
- data/lib/stretchy/attributes/type/knn_vector.rb +47 -0
- data/lib/stretchy/attributes/type/match_only_text.rb +22 -1
- data/lib/stretchy/attributes/type/nested.rb +16 -11
- data/lib/stretchy/attributes/type/numeric/base.rb +30 -22
- data/lib/stretchy/attributes/type/numeric/byte.rb +20 -0
- data/lib/stretchy/attributes/type/numeric/double.rb +20 -0
- data/lib/stretchy/attributes/type/numeric/float.rb +20 -0
- data/lib/stretchy/attributes/type/numeric/half_float.rb +20 -0
- data/lib/stretchy/attributes/type/numeric/integer.rb +21 -1
- data/lib/stretchy/attributes/type/numeric/long.rb +20 -0
- data/lib/stretchy/attributes/type/numeric/scaled_float.rb +16 -7
- data/lib/stretchy/attributes/type/numeric/short.rb +20 -0
- data/lib/stretchy/attributes/type/numeric/unsigned_long.rb +21 -1
- data/lib/stretchy/attributes/type/percolator.rb +16 -4
- data/lib/stretchy/attributes/type/point.rb +19 -9
- data/lib/stretchy/attributes/type/range/base.rb +24 -1
- data/lib/stretchy/attributes/type/range/date_range.rb +21 -5
- data/lib/stretchy/attributes/type/range/double_range.rb +20 -4
- data/lib/stretchy/attributes/type/range/float_range.rb +21 -5
- data/lib/stretchy/attributes/type/range/integer_range.rb +20 -4
- data/lib/stretchy/attributes/type/range/ip_range.rb +20 -4
- data/lib/stretchy/attributes/type/range/long_range.rb +20 -4
- data/lib/stretchy/attributes/type/rank_feature.rb +16 -6
- data/lib/stretchy/attributes/type/rank_features.rb +16 -9
- data/lib/stretchy/attributes/type/search_as_you_type.rb +28 -18
- data/lib/stretchy/attributes/type/shape.rb +19 -9
- data/lib/stretchy/attributes/type/sparse_vector.rb +25 -21
- data/lib/stretchy/attributes/type/string.rb +42 -1
- data/lib/stretchy/attributes/type/text.rb +53 -28
- data/lib/stretchy/attributes/type/token_count.rb +21 -11
- data/lib/stretchy/attributes/type/version.rb +16 -6
- data/lib/stretchy/attributes/type/wildcard.rb +36 -25
- data/lib/stretchy/attributes.rb +29 -0
- data/lib/stretchy/delegation/gateway_delegation.rb +78 -0
- data/lib/stretchy/index_setting.rb +94 -0
- data/lib/stretchy/indexing/bulk.rb +75 -3
- data/lib/stretchy/model/callbacks.rb +1 -0
- data/lib/stretchy/model/common.rb +157 -0
- data/lib/stretchy/model/persistence.rb +144 -0
- data/lib/stretchy/model/refreshable.rb +26 -0
- data/lib/stretchy/pipeline.rb +2 -1
- data/lib/stretchy/pipelines/processor.rb +38 -36
- data/lib/stretchy/querying.rb +7 -8
- data/lib/stretchy/record.rb +5 -4
- data/lib/stretchy/relation.rb +229 -28
- data/lib/stretchy/relations/aggregation_methods/aggregation.rb +59 -0
- data/lib/stretchy/relations/aggregation_methods/avg.rb +45 -0
- data/lib/stretchy/relations/aggregation_methods/bucket_script.rb +47 -0
- data/lib/stretchy/relations/aggregation_methods/bucket_selector.rb +47 -0
- data/lib/stretchy/relations/aggregation_methods/bucket_sort.rb +47 -0
- data/lib/stretchy/relations/aggregation_methods/cardinality.rb +47 -0
- data/lib/stretchy/relations/aggregation_methods/children.rb +47 -0
- data/lib/stretchy/relations/aggregation_methods/composite.rb +41 -0
- data/lib/stretchy/relations/aggregation_methods/date_histogram.rb +53 -0
- data/lib/stretchy/relations/aggregation_methods/date_range.rb +53 -0
- data/lib/stretchy/relations/aggregation_methods/extended_stats.rb +48 -0
- data/lib/stretchy/relations/aggregation_methods/filter.rb +47 -0
- data/lib/stretchy/relations/aggregation_methods/filters.rb +47 -0
- data/lib/stretchy/relations/aggregation_methods/geo_bounds.rb +40 -0
- data/lib/stretchy/relations/aggregation_methods/geo_centroid.rb +40 -0
- data/lib/stretchy/relations/aggregation_methods/global.rb +39 -0
- data/lib/stretchy/relations/aggregation_methods/histogram.rb +43 -0
- data/lib/stretchy/relations/aggregation_methods/ip_range.rb +41 -0
- data/lib/stretchy/relations/aggregation_methods/max.rb +40 -0
- data/lib/stretchy/relations/aggregation_methods/min.rb +41 -0
- data/lib/stretchy/relations/aggregation_methods/missing.rb +40 -0
- data/lib/stretchy/relations/aggregation_methods/nested.rb +40 -0
- data/lib/stretchy/relations/aggregation_methods/percentile_ranks.rb +45 -0
- data/lib/stretchy/relations/aggregation_methods/percentiles.rb +45 -0
- data/lib/stretchy/relations/aggregation_methods/range.rb +42 -0
- data/lib/stretchy/relations/aggregation_methods/reverse_nested.rb +40 -0
- data/lib/stretchy/relations/aggregation_methods/sampler.rb +40 -0
- data/lib/stretchy/relations/aggregation_methods/scripted_metric.rb +43 -0
- data/lib/stretchy/relations/aggregation_methods/significant_terms.rb +45 -0
- data/lib/stretchy/relations/aggregation_methods/stats.rb +42 -0
- data/lib/stretchy/relations/aggregation_methods/sum.rb +42 -0
- data/lib/stretchy/relations/aggregation_methods/terms.rb +46 -0
- data/lib/stretchy/relations/aggregation_methods/top_hits.rb +42 -0
- data/lib/stretchy/relations/aggregation_methods/top_metrics.rb +44 -0
- data/lib/stretchy/relations/aggregation_methods/value_count.rb +41 -0
- data/lib/stretchy/relations/aggregation_methods/weighted_avg.rb +42 -0
- data/lib/stretchy/relations/aggregation_methods.rb +20 -749
- data/lib/stretchy/relations/finder_methods.rb +2 -18
- data/lib/stretchy/relations/null_relation.rb +55 -0
- data/lib/stretchy/relations/query_builder.rb +82 -36
- data/lib/stretchy/relations/query_methods/bind.rb +19 -0
- data/lib/stretchy/relations/query_methods/extending.rb +29 -0
- data/lib/stretchy/relations/query_methods/fields.rb +70 -0
- data/lib/stretchy/relations/query_methods/filter_query.rb +53 -0
- data/lib/stretchy/relations/query_methods/has_field.rb +40 -0
- data/lib/stretchy/relations/query_methods/highlight.rb +75 -0
- data/lib/stretchy/relations/query_methods/hybrid.rb +60 -0
- data/lib/stretchy/relations/query_methods/ids.rb +40 -0
- data/lib/stretchy/relations/query_methods/match.rb +52 -0
- data/lib/stretchy/relations/query_methods/must_not.rb +54 -0
- data/lib/stretchy/relations/query_methods/neural.rb +58 -0
- data/lib/stretchy/relations/query_methods/neural_sparse.rb +43 -0
- data/lib/stretchy/relations/query_methods/none.rb +21 -0
- data/lib/stretchy/relations/query_methods/or_filter.rb +21 -0
- data/lib/stretchy/relations/query_methods/order.rb +63 -0
- data/lib/stretchy/relations/query_methods/query_string.rb +44 -0
- data/lib/stretchy/relations/query_methods/regexp.rb +61 -0
- data/lib/stretchy/relations/query_methods/should.rb +51 -0
- data/lib/stretchy/relations/query_methods/size.rb +44 -0
- data/lib/stretchy/relations/query_methods/skip_callbacks.rb +47 -0
- data/lib/stretchy/relations/query_methods/source.rb +59 -0
- data/lib/stretchy/relations/query_methods/where.rb +113 -0
- data/lib/stretchy/relations/query_methods.rb +48 -569
- data/lib/stretchy/relations/scoping/default.rb +136 -0
- data/lib/stretchy/relations/scoping/named.rb +70 -0
- data/lib/stretchy/relations/scoping/scope_registry.rb +36 -0
- data/lib/stretchy/relations/scoping.rb +30 -0
- data/lib/stretchy/relations/search_option_methods.rb +2 -0
- data/lib/stretchy/version.rb +1 -1
- data/lib/stretchy.rb +17 -10
- metadata +111 -17
- data/lib/stretchy/common.rb +0 -38
- data/lib/stretchy/null_relation.rb +0 -53
- data/lib/stretchy/persistence.rb +0 -43
- data/lib/stretchy/refreshable.rb +0 -15
- data/lib/stretchy/scoping/default.rb +0 -134
- data/lib/stretchy/scoping/named.rb +0 -68
- data/lib/stretchy/scoping/scope_registry.rb +0 -34
- 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
|
-
|
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
|
-
:
|
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
|
40
|
-
@values[:#{name}] || []
|
41
|
-
end
|
42
|
-
|
43
|
-
def #{name}_values=(values)
|
44
|
-
raise ImmutableRelation if @loaded
|
45
|
-
@values[:#{name}] = values
|
46
|
-
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
|
53
|
-
@values[:#{name}]
|
54
|
-
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)
|
61
|
-
raise ImmutableRelation if @loaded
|
62
|
-
@values[:#{name}] = value
|
63
|
-
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
|