elasticgraph-graphql 0.19.0.0.rc2 → 0.19.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c907a36c44ef987d02202ff39ef9686c3bfc997ce80d9310da0e3364d89de5d0
4
- data.tar.gz: 1a2f992e201097a980b5fa4f16f9721f8f51d2888dc8d12f3267101765b3c7af
3
+ metadata.gz: 5c196b96a85b2a55392c7112b827d9d54a857a6631b173588af6b3ea70ae82ab
4
+ data.tar.gz: 69a8b3e70aa79bf13d205de13fe019f6c276b64c3940fd06745b16ae7e922b80
5
5
  SHA512:
6
- metadata.gz: baba491d690547b7722d7b715af442b691c341abb0c29a25da6678e068d3199a7765829acd8121b756ee464685c7dba92b9beb0f2f34d2431b20e1e1783e347d
7
- data.tar.gz: 289be9c156185ff45b152a504fd6b72f61cba8982b61acf8373e18cf5bc10a70f9978db2c99040bdde0b4824d811b58b812290e3fc08b809940bf998b975022e
6
+ metadata.gz: 34d8f5d423414d1def62504028f54da2a123c8415d2080a0a577a3cc8cf05fcb5d221ead038b57a377a74cc861bba475c62f53448c7b1ccce3776859bc5cc9e8
7
+ data.tar.gz: 8377fae789db02a86b6bdb82af40af08c2b2aac8c3930a7e0fcada9276950dcbb0cfb445a138c70cd413c4c9e18ce22b39b49218d42a2a2a9ca4e6cdae67f10f
@@ -154,7 +154,7 @@ module ElasticGraph
154
154
  # If there are no aggregations, there's nothing to unmerge--just return it as is.
155
155
  return response_from_merged_query unless (aggs = response_from_merged_query["aggregations"])
156
156
 
157
- prefix = @unique_prefix_by_query[original_query]
157
+ prefix = @unique_prefix_by_query[original_query] # : ::String
158
158
  agg_names = original_query.aggregations.keys.map { |name| "#{prefix}#{name}" }.to_set
159
159
 
160
160
  filtered_aggs = aggs
@@ -14,8 +14,13 @@ module ElasticGraph
14
14
  class DatastoreQuery
15
15
  # Responsible for building a search index expression for a specific query based on the filters.
16
16
  class IndexExpressionBuilder
17
- def initialize(schema_names:)
18
- @filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, Support::TimeSet::ALL, Support::TimeSet::EMPTY) do |operator, filter_value|
17
+ def initialize(filter_node_interpreter:, schema_names:)
18
+ @filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(
19
+ filter_node_interpreter,
20
+ schema_names,
21
+ Support::TimeSet::ALL,
22
+ Support::TimeSet::EMPTY
23
+ ) do |operator, filter_value|
19
24
  case operator
20
25
  when :gt, :gte, :lt, :lte
21
26
  if date_string?(filter_value)
@@ -73,6 +78,8 @@ module ElasticGraph
73
78
 
74
79
  time_set = @filter_value_set_extractor.extract_filter_value_set(filter_hashes, [index_def.timestamp_field_path])
75
80
 
81
+ return IndexExpression.only(index_def.index_expression_for_search) if time_set.nil?
82
+
76
83
  if time_set.empty?
77
84
  return require_indices ?
78
85
  # Indices are required. Given the time set is empty, it's impossible for any documents to match our search.
@@ -13,21 +13,19 @@ module ElasticGraph
13
13
  class DatastoreQuery
14
14
  # Responsible for picking routing values for a specific query based on the filters.
15
15
  class RoutingPicker
16
- def initialize(schema_names:)
17
- # @type var all_values_set: _RoutingValueSet
16
+ def initialize(filter_node_interpreter:, schema_names:)
18
17
  all_values_set = RoutingValueSet::ALL
19
18
  empty_set = RoutingValueSet::EMPTY
20
19
 
21
- @filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, all_values_set, empty_set) do |operator, filter_value|
20
+ @filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(
21
+ filter_node_interpreter,
22
+ schema_names,
23
+ all_values_set,
24
+ empty_set
25
+ ) do |operator, filter_value|
22
26
  if operator == :equal_to_any_of
23
27
  # This calls `.compact` to remove `nil` filter_value values
24
28
  RoutingValueSet.of(filter_value.compact)
25
- else # gt, lt, gte, lte, matches
26
- # With one of these inexact/inequality operators, we don't have a way to precisely represent
27
- # the set of values. Instead, we represent it with the special UnboundedWithExclusions
28
- # implementation since when these operators are used the set is unbounded (there's an infinite
29
- # number of values in the set) but it doesn't contain all values (it has some exclusions).
30
- RoutingValueSet::UnboundedWithExclusions
31
29
  end
32
30
  end
33
31
  end
@@ -55,13 +53,11 @@ module ElasticGraph
55
53
  # end
56
54
  # ```
57
55
  def extract_eligible_routing_values(filter_hashes, routing_field_paths)
58
- @filter_value_set_extractor.extract_filter_value_set(filter_hashes, routing_field_paths).to_return_value
56
+ @filter_value_set_extractor.extract_filter_value_set(filter_hashes, routing_field_paths)&.to_return_value
59
57
  end
60
58
  end
61
59
 
62
60
  class RoutingValueSet < Data.define(:type, :routing_values)
63
- # @dynamic ==
64
-
65
61
  def self.of(routing_values)
66
62
  new(:inclusive, routing_values.to_set)
67
63
  end
@@ -73,15 +69,7 @@ module ElasticGraph
73
69
  ALL = of_all_except([])
74
70
  EMPTY = of([])
75
71
 
76
- def intersection(other_set)
77
- # Here we return `self` to preserve the commutative property of `intersection`. Returning `self`
78
- # here matches the behavior of `UnboundedWithExclusions.intersection`. See the comment there for
79
- # rationale.
80
- return self if other_set == UnboundedWithExclusions
81
-
82
- # @type var other: RoutingValueSet
83
- other = _ = other_set
84
-
72
+ def intersection(other)
85
73
  if inclusive? && other.inclusive?
86
74
  # Since both sets are inclusive, we can just delegate to `Set#intersection` here.
87
75
  RoutingValueSet.of(routing_values.intersection(other.routing_values))
@@ -111,15 +99,7 @@ module ElasticGraph
111
99
  end
112
100
  end
113
101
 
114
- def union(other_set)
115
- # Here we return `other` to preserve the commutative property of `union`. Returning `other`
116
- # here matches the behavior of `UnboundedWithExclusions.union`. See the comment there for
117
- # rationale.
118
- return other_set if other_set == UnboundedWithExclusions
119
-
120
- # @type var other: RoutingValueSet
121
- other = _ = other_set
122
-
102
+ def union(other)
123
103
  if inclusive? && other.inclusive?
124
104
  # Since both sets are inclusive, we can just delegate to `Set#union` here.
125
105
  RoutingValueSet.of(routing_values.union(other.routing_values))
@@ -180,52 +160,6 @@ module ElasticGraph
180
160
  def get_included_and_excluded_values(other)
181
161
  inclusive? ? [routing_values, other.routing_values] : [other.routing_values, routing_values]
182
162
  end
183
-
184
- # This `RoutingValueSet` implementation is used for otherwise unrepresentable cases. We use it when
185
- # a filter on one of the `routing_field_paths` uses an inequality like:
186
- #
187
- # {routing_field: {gt: "abc"}}
188
- #
189
- # In a case like that, the set is unbounded (there's an infinite number of values that are greater
190
- # than `"abc"`...), but it's not `RoutingValueSet::ALL`--since it's based on an inequality, there are
191
- # _some_ values that are excluded from the set. But we can't use `RoutingValueSet.of_all_except(...)`
192
- # because the set of exclusions is also unbounded!
193
- #
194
- # When our filter value extraction results in this set, we must search all shards of the index and
195
- # cannot pass any `routing` value to the datastore at all.
196
- module UnboundedWithExclusions
197
- # @dynamic self.==
198
-
199
- def self.intersection(other)
200
- # Technically, the "true" intersection would be `other - values_of(self)` but as we don't have
201
- # any known values from this unbounded set, we just return `other`. It's OK to include extra values
202
- # in the set (we'll search additional shards) but not OK to fail to include necessary values in
203
- # the set (we'd avoid searching a shard that may have matching documents) so we err on the side of
204
- # including more values.
205
- other
206
- end
207
-
208
- def self.union(other)
209
- # Since our set here is unbounded, the resulting union is also unbounded. This errs on the side
210
- # of safety since this set's `to_return_value` returns `nil` to cause the datastore to search
211
- # all shards.
212
- self
213
- end
214
-
215
- def self.negate
216
- # This here is the only difference in behavior of this set implementation vs `RoutingValueSet::ALL`.
217
- # Where as `ALL.negate` returns an empty set, we treat `negate` as a no-op. We do that because the
218
- # negation of an inexact unbounded set is still an inexact unbounded set. While it flips which values
219
- # are in or out of the set, this object is still the representation in our datamodel for that case.
220
- self
221
- end
222
-
223
- def self.to_return_value
224
- # Here we return `nil` to make sure that the datastore searches all shards, since we don't have
225
- # any information we can use to safely limit what shards it searches.
226
- nil
227
- end
228
- end
229
163
  end
230
164
 
231
165
  # `Query::RoutingPicker` exists only for use by `Query` and is effectively private.
@@ -344,17 +344,23 @@ module ElasticGraph
344
344
 
345
345
  # Encapsulates dependencies of `Query`, giving us something we can expose off of `application`
346
346
  # to build queries when desired.
347
- class Builder < Support::MemoizableData.define(:runtime_metadata, :logger, :query_defaults)
348
- def self.with(runtime_metadata:, logger:, **query_defaults)
349
- new(runtime_metadata: runtime_metadata, logger: logger, query_defaults: query_defaults)
347
+ class Builder < Support::MemoizableData.define(:runtime_metadata, :logger, :filter_node_interpreter, :query_defaults)
348
+ def self.with(runtime_metadata:, logger:, filter_node_interpreter:, **query_defaults)
349
+ new(runtime_metadata:, logger:, filter_node_interpreter:, query_defaults:)
350
350
  end
351
351
 
352
352
  def routing_picker
353
- @routing_picker ||= RoutingPicker.new(schema_names: runtime_metadata.schema_element_names)
353
+ @routing_picker ||= RoutingPicker.new(
354
+ filter_node_interpreter: filter_node_interpreter,
355
+ schema_names: runtime_metadata.schema_element_names
356
+ )
354
357
  end
355
358
 
356
359
  def index_expression_builder
357
- @index_expression_builder ||= IndexExpressionBuilder.new(schema_names: runtime_metadata.schema_element_names)
360
+ @index_expression_builder ||= IndexExpressionBuilder.new(
361
+ filter_node_interpreter: filter_node_interpreter,
362
+ schema_names: runtime_metadata.schema_element_names
363
+ )
358
364
  end
359
365
 
360
366
  def new_query(**options)
@@ -26,7 +26,7 @@ module ElasticGraph
26
26
  # vs GraphQL.
27
27
  def translate_filter_args(field:, args:)
28
28
  return nil unless (filter_hash = args[filter_arg_name])
29
- filter_type = field.schema.type_from(field.graphql_field.arguments[filter_arg_name].type)
29
+ filter_type = field.schema.type_from(field.graphql_field.arguments.fetch(filter_arg_name).type)
30
30
  convert(filter_type, filter_hash)
31
31
  end
32
32
 
@@ -118,7 +118,12 @@ module ElasticGraph
118
118
  if sub_filter[:bool].key?(:must_not)
119
119
  # Pull clauses up to current bool_node to remove negation
120
120
  sub_filter[:bool][:must_not].each do |negated_clause|
121
- negated_clause[:bool].each { |k, v| bool_node[k].concat(v) }
121
+ negated_clause_bool = negated_clause[:bool]
122
+ # `minimum_should_match` is not a list like the other clauses, and needs to be handled separately.
123
+ negated_clause_bool.except(:minimum_should_match).each { |k, v| bool_node[k].concat(v) }
124
+ if (min_should_match = negated_clause_bool[:minimum_should_match])
125
+ bool_node[:minimum_should_match] = min_should_match
126
+ end
122
127
  end
123
128
  end
124
129
 
@@ -137,8 +142,6 @@ module ElasticGraph
137
142
  # this because we do not generate `any_satisfy` filters on `object` list fields (instead,
138
143
  # they get generated on their leaf fields).
139
144
  def process_list_any_filter_expression(bool_node, filter, field_path)
140
- return if filter.nil? || filter == {}
141
-
142
145
  if filters_on_sub_fields?(filter)
143
146
  process_any_satisfy_filter_expression_on_nested_object_list(bool_node, filter, field_path)
144
147
  else
@@ -195,8 +198,6 @@ module ElasticGraph
195
198
  # * `filter: {anyOf: [{field: null}]}` -> return all results
196
199
  # * `filter: {anyOf: [{field: null}, {field: ...}]}` -> return all results
197
200
  def process_any_of_expression(bool_node, expressions, field_path)
198
- return if expressions.nil? || expressions == {}
199
-
200
201
  if expressions.empty?
201
202
  # When our `expressions` array is empty, we want to match no documents. However, that's
202
203
  # not the behavior the datastore will give us if we have an empty array in the query under
@@ -218,8 +219,6 @@ module ElasticGraph
218
219
  end
219
220
 
220
221
  def process_all_of_expression(bool_node, expressions, field_path)
221
- return if expressions.nil? || expressions == {}
222
-
223
222
  # `all_of` represents an AND. AND is the default way that `process_filter_hash` combines
224
223
  # filters so we just have to call it for each sub-expression.
225
224
  expressions.each do |sub_expression|
@@ -228,8 +227,6 @@ module ElasticGraph
228
227
  end
229
228
 
230
229
  def process_operator_expression(bool_node, operator, expression, field_path)
231
- return if expression.nil? || expression == {}
232
-
233
230
  # `operator` is a filtering operator, and `expression` is the value the filtering
234
231
  # operator should be applied to. The `op_applicator` lambda, when called, will
235
232
  # return a Clause instance (defined in this module).
@@ -317,8 +314,6 @@ module ElasticGraph
317
314
  end
318
315
 
319
316
  def process_list_count_expression(bool_node, expression, field_path)
320
- return if expression.nil? || expression == {}
321
-
322
317
  # Normally, we don't have to do anything special for list count expressions.
323
318
  # That's the case, for example, for an expression like:
324
319
  #
@@ -25,33 +25,30 @@ module ElasticGraph
25
25
  end
26
26
 
27
27
  def identify_node_type(field_or_op, sub_expression)
28
- identify_by_field_or_op(field_or_op) || identify_by_sub_expr(sub_expression) || :unknown
29
- end
30
-
31
- def filter_operators
32
- @filter_operators ||= build_filter_operators(runtime_metadata)
33
- end
28
+ # `:not` must go before `:empty`, because `not: empty_filter` must be inverted from just `empty_filter`.
29
+ return :not if field_or_op == schema_names.not
34
30
 
35
- private
31
+ # The `:empty` check can go before all other checks; besides `not`, none of the other operators require special
32
+ # handling when the filter is empty, and we want to detect this as early as possible.
33
+ # Note: `any_of: [empty_filter]` does have special handling, but `any_of: empty_filter` does not.
34
+ return :empty if sub_expression.nil? || sub_expression == {}
36
35
 
37
- def identify_by_field_or_op(field_or_op)
38
- return :not if field_or_op == schema_names.not
39
36
  return :list_any_filter if field_or_op == schema_names.any_satisfy
40
37
  return :all_of if field_or_op == schema_names.all_of
41
38
  return :any_of if field_or_op == schema_names.any_of
42
39
  return :operator if filter_operators.key?(field_or_op)
43
40
  return :list_count if field_or_op == LIST_COUNTS_FIELD
41
+ return :sub_field if sub_expression.is_a?(::Hash)
44
42
 
45
- nil
43
+ :unknown
46
44
  end
47
45
 
48
- def identify_by_sub_expr(sub_expression)
49
- return :empty if sub_expression.nil? || sub_expression == {}
50
- return :sub_field if sub_expression.is_a?(::Hash)
51
-
52
- nil
46
+ def filter_operators
47
+ @filter_operators ||= build_filter_operators(runtime_metadata)
53
48
  end
54
49
 
50
+ private
51
+
55
52
  def build_filter_operators(runtime_metadata)
56
53
  filter_by_time_of_day_script_id = runtime_metadata
57
54
  .static_script_ids_by_scoped_name
@@ -12,7 +12,8 @@ module ElasticGraph
12
12
  # Responsible for extracting a set of values from query filters, based on a using a custom
13
13
  # set type that is able to efficiently model the "all values" case.
14
14
  class FilterValueSetExtractor
15
- def initialize(schema_names, all_values_set, empty_set, &build_set_for_filter)
15
+ def initialize(filter_node_interpreter, schema_names, all_values_set, empty_set, &build_set_for_filter)
16
+ @filter_node_interpreter = filter_node_interpreter
16
17
  @schema_names = schema_names
17
18
  @all_values_set = all_values_set
18
19
  @empty_set = empty_set
@@ -41,9 +42,12 @@ module ElasticGraph
41
42
  # routing values causes no adverse behavior (although it may introduce an inefficiency)
42
43
  # but if we fail to route to a shard that contains a matching document, the search results
43
44
  # will be incorrect.
44
- map_reduce_sets(target_field_paths, :union, negate: false) do |target_field_path|
45
+ value_set = map_reduce_sets(target_field_paths, :union, negate: false) do |target_field_path|
45
46
  filter_value_set_for_target_field_path(target_field_path, filter_hashes)
46
47
  end
48
+
49
+ return nil if (_ = value_set) == UnboundedSetWithExclusions
50
+ _ = value_set
47
51
  end
48
52
 
49
53
  private
@@ -83,24 +87,32 @@ module ElasticGraph
83
87
  # the key could identify either a field we are filtering on or a filtering operator to apply
84
88
  # to a particular field.
85
89
  def filter_value_set_for_filter_hash_entry(field_or_op, filter_value, target_field_path_parts, traversed_field_path_parts, negate:)
86
- if filter_value.nil?
87
- # Any filter with a `nil` value is effectively treated as `true` by our filtering logic, so we need
90
+ node_type = @filter_node_interpreter.identify_node_type(field_or_op, filter_value)
91
+ case node_type
92
+ when :empty
93
+ # Any empty filter is effectively treated as `true` by our filtering logic, so we need
88
94
  # to return our `@all_values_set` to indicate this filter matches all documents.
89
95
  @all_values_set
90
- elsif field_or_op == @schema_names.not
91
- filter_value_set_for_filter_hash(filter_value, target_field_path_parts, traversed_field_path_parts, negate: !negate)
92
- elsif filter_value.is_a?(::Hash)
93
- # the only time `value` is a hash is when `field_or_op` is a field name.
94
- # In that case, `value` is a hash of filters that apply to that field.
96
+ when :not
97
+ filter_value_set_for_filter_hash(filter_value || {}, target_field_path_parts, traversed_field_path_parts, negate: !negate)
98
+ when :sub_field
95
99
  filter_value_set_for_filter_hash(filter_value, target_field_path_parts, traversed_field_path_parts + [field_or_op], negate: negate)
96
- elsif field_or_op == @schema_names.any_of
100
+ when :any_of
97
101
  filter_value_set_for_any_of(filter_value, target_field_path_parts, traversed_field_path_parts, negate: negate)
98
- elsif target_field_path_parts == traversed_field_path_parts
102
+ when :operator
103
+ # Check to make sure the operator applies to the target field. If not, we have no information
104
+ # in this clause. The set is unbounded, and may have exclusions.
105
+ return UnboundedSetWithExclusions unless target_field_path_parts == traversed_field_path_parts
106
+
99
107
  set = filter_value_set_for_field_filter(field_or_op, filter_value)
100
108
  negate ? set.negate : set
109
+ when :all_of, :list_any_filter, :list_count, :unknown
110
+ # We have no information in this clause. The set is unbounded, and may have exclusions.
111
+ UnboundedSetWithExclusions
101
112
  else
102
- # Otherwise, we have no information in this clause to limit our filter value set.
103
- @all_values_set
113
+ # :nocov: -- not possible to cover without mocking `@filter_node_interpreter` to return a node type outside the allowed values.
114
+ raise "`FilterValueSetExtractor` must be updated to handle `:#{node_type}` nodes."
115
+ # :nocov:
104
116
  end
105
117
  end
106
118
 
@@ -125,7 +137,7 @@ module ElasticGraph
125
137
  # Determines the set of filter values for a single filter on a single field.
126
138
  def filter_value_set_for_field_filter(filter_op, filter_value)
127
139
  operator_name = @schema_names.canonical_name_for(filter_op)
128
- @build_set_for_filter.call(operator_name, filter_value) || @all_values_set
140
+ @build_set_for_filter.call(operator_name, filter_value) || UnboundedSetWithExclusions
129
141
  end
130
142
 
131
143
  # Maps over the provided `collection` by applying the given `map_transform`
@@ -144,10 +156,49 @@ module ElasticGraph
144
156
  # of each set is the difference between @all_values_set and the given set)--and vice versa.
145
157
  reduction = REDUCTION_INVERSIONS.fetch(reduction) if negate
146
158
 
147
- collection.map(&map_transform).reduce(reduction)
159
+ collection.map(&map_transform).reduce do |s1, s2|
160
+ receiver, argument = ((_ = s2) == UnboundedSetWithExclusions) ? [s2, s1] : [s1, s2]
161
+ (reduction == :union) ? receiver.union(argument) : receiver.intersection(argument)
162
+ end
148
163
  end
149
164
 
150
165
  REDUCTION_INVERSIONS = {union: :intersection, intersection: :union}
166
+
167
+ # This minimal set implementation is used for otherwise unrepresentable cases. We use it when
168
+ # a filter on a `target_field_path` uses an inequality like:
169
+ #
170
+ # {field: {gt: "abc"}}
171
+ #
172
+ # In a case like that, the set is unbounded (there's an infinite number of values that are greater
173
+ # than `"abc"`...), but it's not `@all_values_set`--since it's based on an inequality, there are
174
+ # _some_ values that are excluded from the set. We also can't represent this case with an
175
+ # `all_values_except(...)` set implementation because the set of exclusions is also unbounded!
176
+ #
177
+ # When our filter value extraction results in this set, we cannot limit what shards or indices we must hit based
178
+ # on the filters.
179
+ module UnboundedSetWithExclusions
180
+ def self.intersection(other)
181
+ # Technically, the accurate intersection would be `other - values_of(self)` but as we don't have
182
+ # any known values from this unbounded set, we just return `other`. It's OK to include extra values
183
+ # in the set (we'll search additional shards or indices) but not OK to fail to include necessary values
184
+ # in the set (we'd avoid searching a shard that may have matching documents) so we err on the side of
185
+ # including more values.
186
+ other
187
+ end
188
+
189
+ def self.union(other)
190
+ # Since our set here is unbounded, the resulting union is also unbounded.
191
+ self
192
+ end
193
+
194
+ def self.negate
195
+ # The negation of an `UnboundedSetWithExclusions` is still an `UnboundedSetWithExclusions`. While it would flip
196
+ # which values are in or out of the set, this object is still the representation in our data model for that case.
197
+ self
198
+ end
199
+ end
200
+
201
+ private_constant :UnboundedSetWithExclusions
151
202
  end
152
203
  end
153
204
  end
@@ -197,7 +197,7 @@ module ElasticGraph
197
197
  end
198
198
 
199
199
  def content_type
200
- @content_type ||= normalized_headers["CONTENT-TYPE"]
200
+ normalized_headers["CONTENT-TYPE"]
201
201
  end
202
202
 
203
203
  def self.normalize_header_name(header)
@@ -96,12 +96,18 @@ module ElasticGraph
96
96
  # on `filter_value_set_extractor` to determine it, since it understands the semantics
97
97
  # of `any_of`, `not`, etc.
98
98
  def can_match_nil_values_at?(path, filter)
99
- filter_value_set_extractor.extract_filter_value_set([filter], [path]).includes_nil?
99
+ value_set = filter_value_set_extractor.extract_filter_value_set([filter], [path])
100
+ (_ = value_set).nil? || value_set.includes_nil?
100
101
  end
101
102
 
102
103
  def filter_value_set_extractor
103
104
  @filter_value_set_extractor ||=
104
- Filtering::FilterValueSetExtractor.new(schema_element_names, IncludesNilSet, ExcludesNilSet) do |operator, filter_value|
105
+ Filtering::FilterValueSetExtractor.new(
106
+ filter_node_interpreter,
107
+ schema_element_names,
108
+ IncludesNilSet,
109
+ ExcludesNilSet
110
+ ) do |operator, filter_value|
105
111
  if operator == :equal_to_any_of && filter_value.include?(nil)
106
112
  IncludesNilSet
107
113
  else
@@ -25,7 +25,7 @@ module ElasticGraph
25
25
  def fetch(queries)
26
26
  responses_by_query = @datastore_router.msearch(queries, query_tracker: @query_tracker)
27
27
  @query_tracker.record_datastore_queries_for_single_request(queries)
28
- queries.map { |q| responses_by_query[q] }
28
+ queries.map { |q| responses_by_query.fetch(q) }
29
29
  end
30
30
 
31
31
  def self.execute_many(queries, for_context:)
@@ -129,9 +129,10 @@ module ElasticGraph
129
129
  @datastore_query_builder ||= begin
130
130
  require "elastic_graph/graphql/datastore_query"
131
131
  DatastoreQuery::Builder.with(
132
- filter_interpreter: filter_interpreter,
133
- runtime_metadata: runtime_metadata,
134
- logger: logger,
132
+ filter_interpreter:,
133
+ filter_node_interpreter:,
134
+ runtime_metadata:,
135
+ logger:,
135
136
  default_page_size: @config.default_page_size,
136
137
  max_page_size: @config.max_page_size
137
138
  )
metadata CHANGED
@@ -1,380 +1,130 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elasticgraph-graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0.0.rc2
4
+ version: 0.19.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Myron Marston
8
8
  - Ben VandenBos
9
9
  - Block Engineering
10
- autorequire:
11
- bindir: exe
10
+ autorequire:
11
+ bindir: bin
12
12
  cert_chain: []
13
- date: 2024-12-03 00:00:00.000000000 Z
13
+ date: 2025-02-06 00:00:00.000000000 Z
14
14
  dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: rubocop-factory_bot
17
- requirement: !ruby/object:Gem::Requirement
18
- requirements:
19
- - - "~>"
20
- - !ruby/object:Gem::Version
21
- version: '2.26'
22
- type: :development
23
- prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- requirements:
26
- - - "~>"
27
- - !ruby/object:Gem::Version
28
- version: '2.26'
29
- - !ruby/object:Gem::Dependency
30
- name: rubocop-rake
31
- requirement: !ruby/object:Gem::Requirement
32
- requirements:
33
- - - "~>"
34
- - !ruby/object:Gem::Version
35
- version: '0.6'
36
- type: :development
37
- prerelease: false
38
- version_requirements: !ruby/object:Gem::Requirement
39
- requirements:
40
- - - "~>"
41
- - !ruby/object:Gem::Version
42
- version: '0.6'
43
- - !ruby/object:Gem::Dependency
44
- name: rubocop-rspec
45
- requirement: !ruby/object:Gem::Requirement
46
- requirements:
47
- - - "~>"
48
- - !ruby/object:Gem::Version
49
- version: '3.1'
50
- type: :development
51
- prerelease: false
52
- version_requirements: !ruby/object:Gem::Requirement
53
- requirements:
54
- - - "~>"
55
- - !ruby/object:Gem::Version
56
- version: '3.1'
57
- - !ruby/object:Gem::Dependency
58
- name: standard
59
- requirement: !ruby/object:Gem::Requirement
60
- requirements:
61
- - - "~>"
62
- - !ruby/object:Gem::Version
63
- version: 1.41.0
64
- type: :development
65
- prerelease: false
66
- version_requirements: !ruby/object:Gem::Requirement
67
- requirements:
68
- - - "~>"
69
- - !ruby/object:Gem::Version
70
- version: 1.41.0
71
- - !ruby/object:Gem::Dependency
72
- name: steep
73
- requirement: !ruby/object:Gem::Requirement
74
- requirements:
75
- - - "~>"
76
- - !ruby/object:Gem::Version
77
- version: '1.8'
78
- type: :development
79
- prerelease: false
80
- version_requirements: !ruby/object:Gem::Requirement
81
- requirements:
82
- - - "~>"
83
- - !ruby/object:Gem::Version
84
- version: '1.8'
85
- - !ruby/object:Gem::Dependency
86
- name: coderay
87
- requirement: !ruby/object:Gem::Requirement
88
- requirements:
89
- - - "~>"
90
- - !ruby/object:Gem::Version
91
- version: '1.1'
92
- type: :development
93
- prerelease: false
94
- version_requirements: !ruby/object:Gem::Requirement
95
- requirements:
96
- - - "~>"
97
- - !ruby/object:Gem::Version
98
- version: '1.1'
99
- - !ruby/object:Gem::Dependency
100
- name: flatware-rspec
101
- requirement: !ruby/object:Gem::Requirement
102
- requirements:
103
- - - "~>"
104
- - !ruby/object:Gem::Version
105
- version: '2.3'
106
- - - ">="
107
- - !ruby/object:Gem::Version
108
- version: 2.3.3
109
- type: :development
110
- prerelease: false
111
- version_requirements: !ruby/object:Gem::Requirement
112
- requirements:
113
- - - "~>"
114
- - !ruby/object:Gem::Version
115
- version: '2.3'
116
- - - ">="
117
- - !ruby/object:Gem::Version
118
- version: 2.3.3
119
- - !ruby/object:Gem::Dependency
120
- name: rspec
121
- requirement: !ruby/object:Gem::Requirement
122
- requirements:
123
- - - "~>"
124
- - !ruby/object:Gem::Version
125
- version: '3.13'
126
- type: :development
127
- prerelease: false
128
- version_requirements: !ruby/object:Gem::Requirement
129
- requirements:
130
- - - "~>"
131
- - !ruby/object:Gem::Version
132
- version: '3.13'
133
- - !ruby/object:Gem::Dependency
134
- name: super_diff
135
- requirement: !ruby/object:Gem::Requirement
136
- requirements:
137
- - - "~>"
138
- - !ruby/object:Gem::Version
139
- version: '0.13'
140
- type: :development
141
- prerelease: false
142
- version_requirements: !ruby/object:Gem::Requirement
143
- requirements:
144
- - - "~>"
145
- - !ruby/object:Gem::Version
146
- version: '0.13'
147
- - !ruby/object:Gem::Dependency
148
- name: simplecov
149
- requirement: !ruby/object:Gem::Requirement
150
- requirements:
151
- - - "~>"
152
- - !ruby/object:Gem::Version
153
- version: '0.22'
154
- type: :development
155
- prerelease: false
156
- version_requirements: !ruby/object:Gem::Requirement
157
- requirements:
158
- - - "~>"
159
- - !ruby/object:Gem::Version
160
- version: '0.22'
161
- - !ruby/object:Gem::Dependency
162
- name: simplecov-console
163
- requirement: !ruby/object:Gem::Requirement
164
- requirements:
165
- - - "~>"
166
- - !ruby/object:Gem::Version
167
- version: '0.9'
168
- type: :development
169
- prerelease: false
170
- version_requirements: !ruby/object:Gem::Requirement
171
- requirements:
172
- - - "~>"
173
- - !ruby/object:Gem::Version
174
- version: '0.9'
175
- - !ruby/object:Gem::Dependency
176
- name: httpx
177
- requirement: !ruby/object:Gem::Requirement
178
- requirements:
179
- - - "~>"
180
- - !ruby/object:Gem::Version
181
- version: '1.3'
182
- type: :development
183
- prerelease: false
184
- version_requirements: !ruby/object:Gem::Requirement
185
- requirements:
186
- - - "~>"
187
- - !ruby/object:Gem::Version
188
- version: '1.3'
189
- - !ruby/object:Gem::Dependency
190
- name: method_source
191
- requirement: !ruby/object:Gem::Requirement
192
- requirements:
193
- - - "~>"
194
- - !ruby/object:Gem::Version
195
- version: '1.1'
196
- type: :development
197
- prerelease: false
198
- version_requirements: !ruby/object:Gem::Requirement
199
- requirements:
200
- - - "~>"
201
- - !ruby/object:Gem::Version
202
- version: '1.1'
203
- - !ruby/object:Gem::Dependency
204
- name: rspec-retry
205
- requirement: !ruby/object:Gem::Requirement
206
- requirements:
207
- - - "~>"
208
- - !ruby/object:Gem::Version
209
- version: '0.6'
210
- type: :development
211
- prerelease: false
212
- version_requirements: !ruby/object:Gem::Requirement
213
- requirements:
214
- - - "~>"
215
- - !ruby/object:Gem::Version
216
- version: '0.6'
217
- - !ruby/object:Gem::Dependency
218
- name: vcr
219
- requirement: !ruby/object:Gem::Requirement
220
- requirements:
221
- - - "~>"
222
- - !ruby/object:Gem::Version
223
- version: '6.3'
224
- - - ">="
225
- - !ruby/object:Gem::Version
226
- version: 6.3.1
227
- type: :development
228
- prerelease: false
229
- version_requirements: !ruby/object:Gem::Requirement
230
- requirements:
231
- - - "~>"
232
- - !ruby/object:Gem::Version
233
- version: '6.3'
234
- - - ">="
235
- - !ruby/object:Gem::Version
236
- version: 6.3.1
237
- - !ruby/object:Gem::Dependency
238
- name: factory_bot
239
- requirement: !ruby/object:Gem::Requirement
240
- requirements:
241
- - - "~>"
242
- - !ruby/object:Gem::Version
243
- version: '6.4'
244
- type: :development
245
- prerelease: false
246
- version_requirements: !ruby/object:Gem::Requirement
247
- requirements:
248
- - - "~>"
249
- - !ruby/object:Gem::Version
250
- version: '6.4'
251
- - !ruby/object:Gem::Dependency
252
- name: faker
253
- requirement: !ruby/object:Gem::Requirement
254
- requirements:
255
- - - "~>"
256
- - !ruby/object:Gem::Version
257
- version: '3.5'
258
- type: :development
259
- prerelease: false
260
- version_requirements: !ruby/object:Gem::Requirement
261
- requirements:
262
- - - "~>"
263
- - !ruby/object:Gem::Version
264
- version: '3.5'
265
15
  - !ruby/object:Gem::Dependency
266
16
  name: elasticgraph-datastore_core
267
17
  requirement: !ruby/object:Gem::Requirement
268
18
  requirements:
269
19
  - - '='
270
20
  - !ruby/object:Gem::Version
271
- version: 0.19.0.0.rc2
21
+ version: 0.19.1.0
272
22
  type: :runtime
273
23
  prerelease: false
274
24
  version_requirements: !ruby/object:Gem::Requirement
275
25
  requirements:
276
26
  - - '='
277
27
  - !ruby/object:Gem::Version
278
- version: 0.19.0.0.rc2
28
+ version: 0.19.1.0
279
29
  - !ruby/object:Gem::Dependency
280
30
  name: elasticgraph-schema_artifacts
281
31
  requirement: !ruby/object:Gem::Requirement
282
32
  requirements:
283
33
  - - '='
284
34
  - !ruby/object:Gem::Version
285
- version: 0.19.0.0.rc2
35
+ version: 0.19.1.0
286
36
  type: :runtime
287
37
  prerelease: false
288
38
  version_requirements: !ruby/object:Gem::Requirement
289
39
  requirements:
290
40
  - - '='
291
41
  - !ruby/object:Gem::Version
292
- version: 0.19.0.0.rc2
42
+ version: 0.19.1.0
293
43
  - !ruby/object:Gem::Dependency
294
44
  name: graphql
295
45
  requirement: !ruby/object:Gem::Requirement
296
46
  requirements:
297
47
  - - "~>"
298
48
  - !ruby/object:Gem::Version
299
- version: 2.4.5
49
+ version: 2.4.8
300
50
  type: :runtime
301
51
  prerelease: false
302
52
  version_requirements: !ruby/object:Gem::Requirement
303
53
  requirements:
304
54
  - - "~>"
305
55
  - !ruby/object:Gem::Version
306
- version: 2.4.5
56
+ version: 2.4.8
307
57
  - !ruby/object:Gem::Dependency
308
58
  name: elasticgraph-admin
309
59
  requirement: !ruby/object:Gem::Requirement
310
60
  requirements:
311
61
  - - '='
312
62
  - !ruby/object:Gem::Version
313
- version: 0.19.0.0.rc2
63
+ version: 0.19.1.0
314
64
  type: :development
315
65
  prerelease: false
316
66
  version_requirements: !ruby/object:Gem::Requirement
317
67
  requirements:
318
68
  - - '='
319
69
  - !ruby/object:Gem::Version
320
- version: 0.19.0.0.rc2
70
+ version: 0.19.1.0
321
71
  - !ruby/object:Gem::Dependency
322
72
  name: elasticgraph-elasticsearch
323
73
  requirement: !ruby/object:Gem::Requirement
324
74
  requirements:
325
75
  - - '='
326
76
  - !ruby/object:Gem::Version
327
- version: 0.19.0.0.rc2
77
+ version: 0.19.1.0
328
78
  type: :development
329
79
  prerelease: false
330
80
  version_requirements: !ruby/object:Gem::Requirement
331
81
  requirements:
332
82
  - - '='
333
83
  - !ruby/object:Gem::Version
334
- version: 0.19.0.0.rc2
84
+ version: 0.19.1.0
335
85
  - !ruby/object:Gem::Dependency
336
86
  name: elasticgraph-opensearch
337
87
  requirement: !ruby/object:Gem::Requirement
338
88
  requirements:
339
89
  - - '='
340
90
  - !ruby/object:Gem::Version
341
- version: 0.19.0.0.rc2
91
+ version: 0.19.1.0
342
92
  type: :development
343
93
  prerelease: false
344
94
  version_requirements: !ruby/object:Gem::Requirement
345
95
  requirements:
346
96
  - - '='
347
97
  - !ruby/object:Gem::Version
348
- version: 0.19.0.0.rc2
98
+ version: 0.19.1.0
349
99
  - !ruby/object:Gem::Dependency
350
100
  name: elasticgraph-indexer
351
101
  requirement: !ruby/object:Gem::Requirement
352
102
  requirements:
353
103
  - - '='
354
104
  - !ruby/object:Gem::Version
355
- version: 0.19.0.0.rc2
105
+ version: 0.19.1.0
356
106
  type: :development
357
107
  prerelease: false
358
108
  version_requirements: !ruby/object:Gem::Requirement
359
109
  requirements:
360
110
  - - '='
361
111
  - !ruby/object:Gem::Version
362
- version: 0.19.0.0.rc2
112
+ version: 0.19.1.0
363
113
  - !ruby/object:Gem::Dependency
364
114
  name: elasticgraph-schema_definition
365
115
  requirement: !ruby/object:Gem::Requirement
366
116
  requirements:
367
117
  - - '='
368
118
  - !ruby/object:Gem::Version
369
- version: 0.19.0.0.rc2
119
+ version: 0.19.1.0
370
120
  type: :development
371
121
  prerelease: false
372
122
  version_requirements: !ruby/object:Gem::Requirement
373
123
  requirements:
374
124
  - - '='
375
125
  - !ruby/object:Gem::Version
376
- version: 0.19.0.0.rc2
377
- description:
126
+ version: 0.19.1.0
127
+ description:
378
128
  email:
379
129
  - myron@squareup.com
380
130
  executables: []
@@ -383,7 +133,6 @@ extra_rdoc_files: []
383
133
  files:
384
134
  - LICENSE.txt
385
135
  - README.md
386
- - elasticgraph-graphql.gemspec
387
136
  - lib/elastic_graph/graphql.rb
388
137
  - lib/elastic_graph/graphql/aggregation/composite_grouping_adapter.rb
389
138
  - lib/elastic_graph/graphql/aggregation/computation.rb
@@ -466,28 +215,31 @@ licenses:
466
215
  - MIT
467
216
  metadata:
468
217
  bug_tracker_uri: https://github.com/block/elasticgraph/issues
469
- changelog_uri: https://github.com/block/elasticgraph/releases/tag/v0.19.0.0.rc2
218
+ changelog_uri: https://github.com/block/elasticgraph/releases/tag/v0.19.1.0
470
219
  documentation_uri: https://block.github.io/elasticgraph/docs/main/
471
220
  homepage_uri: https://block.github.io/elasticgraph/
472
- source_code_uri: https://github.com/block/elasticgraph/tree/v0.19.0.0.rc2/elasticgraph-graphql
221
+ source_code_uri: https://github.com/block/elasticgraph/tree/v0.19.1.0/elasticgraph-graphql
473
222
  gem_category: core
474
- post_install_message:
223
+ post_install_message:
475
224
  rdoc_options: []
476
225
  require_paths:
477
226
  - lib
478
227
  required_ruby_version: !ruby/object:Gem::Requirement
479
228
  requirements:
480
- - - "~>"
229
+ - - ">="
481
230
  - !ruby/object:Gem::Version
482
231
  version: '3.2'
232
+ - - "<"
233
+ - !ruby/object:Gem::Version
234
+ version: '3.5'
483
235
  required_rubygems_version: !ruby/object:Gem::Requirement
484
236
  requirements:
485
237
  - - ">="
486
238
  - !ruby/object:Gem::Version
487
239
  version: '0'
488
240
  requirements: []
489
- rubygems_version: 3.5.9
490
- signing_key:
241
+ rubygems_version: 3.5.22
242
+ signing_key:
491
243
  specification_version: 4
492
244
  summary: The ElasticGraph GraphQL query engine.
493
245
  test_files: []
@@ -1,23 +0,0 @@
1
- # Copyright 2024 Block, Inc.
2
- #
3
- # Use of this source code is governed by an MIT-style
4
- # license that can be found in the LICENSE file or at
5
- # https://opensource.org/licenses/MIT.
6
- #
7
- # frozen_string_literal: true
8
-
9
- require_relative "../gemspec_helper"
10
-
11
- ElasticGraphGemspecHelper.define_elasticgraph_gem(gemspec_file: __FILE__, category: :core) do |spec, eg_version|
12
- spec.summary = "The ElasticGraph GraphQL query engine."
13
-
14
- spec.add_dependency "elasticgraph-datastore_core", eg_version
15
- spec.add_dependency "elasticgraph-schema_artifacts", eg_version
16
- spec.add_dependency "graphql", "~> 2.4.5"
17
-
18
- spec.add_development_dependency "elasticgraph-admin", eg_version
19
- spec.add_development_dependency "elasticgraph-elasticsearch", eg_version
20
- spec.add_development_dependency "elasticgraph-opensearch", eg_version
21
- spec.add_development_dependency "elasticgraph-indexer", eg_version
22
- spec.add_development_dependency "elasticgraph-schema_definition", eg_version
23
- end