elasticgraph-graphql 0.18.0.5 → 0.19.0.0.rc2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/elasticgraph-graphql.gemspec +1 -1
- data/lib/elastic_graph/graphql/aggregation/non_composite_grouping_adapter.rb +11 -2
- data/lib/elastic_graph/graphql/datastore_query/index_expression_builder.rb +1 -1
- data/lib/elastic_graph/graphql/datastore_query/routing_picker.rb +3 -1
- data/lib/elastic_graph/graphql/datastore_search_router.rb +18 -1
- data/lib/elastic_graph/graphql/filtering/boolean_query.rb +1 -6
- data/lib/elastic_graph/graphql/filtering/filter_interpreter.rb +58 -30
- data/lib/elastic_graph/graphql/filtering/filter_node_interpreter.rb +17 -7
- data/lib/elastic_graph/graphql/filtering/filter_value_set_extractor.rb +9 -3
- data/lib/elastic_graph/graphql/http_endpoint.rb +4 -0
- data/lib/elastic_graph/graphql/query_adapter/filters.rb +1 -1
- data/lib/elastic_graph/graphql/resolvers/query_source.rb +1 -1
- data/lib/elastic_graph/graphql/resolvers/resolvable_value.rb +0 -1
- data/lib/elastic_graph/graphql/schema.rb +15 -18
- data/lib/elastic_graph/graphql.rb +10 -1
- metadata +35 -30
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c907a36c44ef987d02202ff39ef9686c3bfc997ce80d9310da0e3364d89de5d0
|
4
|
+
data.tar.gz: 1a2f992e201097a980b5fa4f16f9721f8f51d2888dc8d12f3267101765b3c7af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: baba491d690547b7722d7b715af442b691c341abb0c29a25da6678e068d3199a7765829acd8121b756ee464685c7dba92b9beb0f2f34d2431b20e1e1783e347d
|
7
|
+
data.tar.gz: 289be9c156185ff45b152a504fd6b72f61cba8982b61acf8373e18cf5bc10a70f9978db2c99040bdde0b4824d811b58b812290e3fc08b809940bf998b975022e
|
@@ -13,7 +13,7 @@ ElasticGraphGemspecHelper.define_elasticgraph_gem(gemspec_file: __FILE__, catego
|
|
13
13
|
|
14
14
|
spec.add_dependency "elasticgraph-datastore_core", eg_version
|
15
15
|
spec.add_dependency "elasticgraph-schema_artifacts", eg_version
|
16
|
-
spec.add_dependency "graphql", "~> 2.
|
16
|
+
spec.add_dependency "graphql", "~> 2.4.5"
|
17
17
|
|
18
18
|
spec.add_development_dependency "elasticgraph-admin", eg_version
|
19
19
|
spec.add_development_dependency "elasticgraph-elasticsearch", eg_version
|
@@ -114,8 +114,17 @@ module ElasticGraph
|
|
114
114
|
# (by doc count descending, then by the key values ascending), and then take only first `size`.
|
115
115
|
def sort_and_truncate_buckets(buckets, size)
|
116
116
|
buckets
|
117
|
-
.sort_by
|
118
|
-
|
117
|
+
.sort_by do |b|
|
118
|
+
# We convert key values to strings to ensure they are comparable. Otherwise, we can get an error like:
|
119
|
+
#
|
120
|
+
# > ArgumentError: comparison of Array with Array failed
|
121
|
+
#
|
122
|
+
# Note that this turns it into a lexicographical sort rather than a more type-aware sort
|
123
|
+
# (10 will sort before 2, for example), but that's fine. We only sort by `key_values` as
|
124
|
+
# a time breaker to ensure deterministic results, but don't particularly care which buckets
|
125
|
+
# come first.
|
126
|
+
[-b.fetch("doc_count"), b.fetch("key_values").map(&:to_s)]
|
127
|
+
end.first(size)
|
119
128
|
end
|
120
129
|
|
121
130
|
def missing_bucket_path_from(buckets_path)
|
@@ -15,7 +15,7 @@ module ElasticGraph
|
|
15
15
|
# Responsible for building a search index expression for a specific query based on the filters.
|
16
16
|
class IndexExpressionBuilder
|
17
17
|
def initialize(schema_names:)
|
18
|
-
@filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, Support::TimeSet::ALL) do |operator, filter_value|
|
18
|
+
@filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, Support::TimeSet::ALL, Support::TimeSet::EMPTY) do |operator, filter_value|
|
19
19
|
case operator
|
20
20
|
when :gt, :gte, :lt, :lte
|
21
21
|
if date_string?(filter_value)
|
@@ -16,8 +16,9 @@ module ElasticGraph
|
|
16
16
|
def initialize(schema_names:)
|
17
17
|
# @type var all_values_set: _RoutingValueSet
|
18
18
|
all_values_set = RoutingValueSet::ALL
|
19
|
+
empty_set = RoutingValueSet::EMPTY
|
19
20
|
|
20
|
-
@filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, all_values_set) do |operator, filter_value|
|
21
|
+
@filter_value_set_extractor = Filtering::FilterValueSetExtractor.new(schema_names, all_values_set, empty_set) do |operator, filter_value|
|
21
22
|
if operator == :equal_to_any_of
|
22
23
|
# This calls `.compact` to remove `nil` filter_value values
|
23
24
|
RoutingValueSet.of(filter_value.compact)
|
@@ -70,6 +71,7 @@ module ElasticGraph
|
|
70
71
|
end
|
71
72
|
|
72
73
|
ALL = of_all_except([])
|
74
|
+
EMPTY = of([])
|
73
75
|
|
74
76
|
def intersection(other_set)
|
75
77
|
# Here we return `self` to preserve the commutative property of `intersection`. Returning `self`
|
@@ -61,7 +61,7 @@ module ElasticGraph
|
|
61
61
|
# Unfortunately, the Elasticsearch/OpenSearch clients don't support setting a per-request client-side timeout,
|
62
62
|
# even though Faraday (the underlying HTTP client) does. To work around this, we pass our desired
|
63
63
|
# timeout in a specific header that the `SupportTimeouts` Faraday middleware will use.
|
64
|
-
headers = {TIMEOUT_MS_HEADER => msearch_request_timeout_from(queries)}.compact
|
64
|
+
headers = {TIMEOUT_MS_HEADER => msearch_request_timeout_from(queries)&.to_s}.compact
|
65
65
|
|
66
66
|
queries_and_header_body_tuples_by_datastore_client = header_body_tuples_by_query.group_by do |(query, header_body_tuples)|
|
67
67
|
@datastore_clients_by_name.fetch(query.cluster_name)
|
@@ -118,6 +118,8 @@ module ElasticGraph
|
|
118
118
|
return if failures.empty?
|
119
119
|
|
120
120
|
formatted_failures = failures.map do |(query, response), index|
|
121
|
+
raise_execution_exception_for_known_public_error(response)
|
122
|
+
|
121
123
|
# Note: we intentionally omit the body of the request here, because it could contain PII
|
122
124
|
# or other sensitive values that we don't want logged.
|
123
125
|
<<~ERROR
|
@@ -130,6 +132,21 @@ module ElasticGraph
|
|
130
132
|
raise Errors::SearchFailedError, "Got #{failures.size} search failure(s):\n\n#{formatted_failures}"
|
131
133
|
end
|
132
134
|
|
135
|
+
# In general, when we receive a datastore response indicating a search failed, we raise
|
136
|
+
# `Errors::SearchFailedError` which translates into a `500 Internal Server Error`. That's
|
137
|
+
# appropriate for transient errors (particularly when there's nothing the client can do about
|
138
|
+
# it) but for non-transient errors that the client can do something about, we'd like to provide
|
139
|
+
# a friendlier error. This method handles those cases.
|
140
|
+
#
|
141
|
+
# GraphQL::ExecutionError is automatically translated into a nice error response.
|
142
|
+
def raise_execution_exception_for_known_public_error(response)
|
143
|
+
if response.dig("error", "caused_by", "type") == "too_many_buckets_exception"
|
144
|
+
max_buckets = response.dig("error", "caused_by", "max_buckets")
|
145
|
+
raise ::GraphQL::ExecutionError, "Aggregation query produces too many groupings. " \
|
146
|
+
"Reduce the grouping cardinality to less than #{max_buckets} and try again."
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
133
150
|
# Examine successful query responses and log any shard failure they encounter
|
134
151
|
def log_shard_failure_if_necessary(responses_by_query)
|
135
152
|
shard_failures = responses_by_query.each_with_index.select do |(query, response), query_numeric_index|
|
@@ -33,12 +33,7 @@ module ElasticGraph
|
|
33
33
|
bool_node[occurrence].concat(clauses)
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
# I haven't found any sort of literal `false` we can pass in the compound expression
|
38
|
-
# or even a literal `1 = 0` as is sometimes used in SQL. Instead, we use this for that
|
39
|
-
# case.
|
40
|
-
empty_array = [] # : ::Array[untyped]
|
41
|
-
ALWAYS_FALSE_FILTER = filter({ids: {values: empty_array}})
|
36
|
+
ALWAYS_FALSE_FILTER = filter({match_none: {}})
|
42
37
|
end
|
43
38
|
end
|
44
39
|
end
|
@@ -59,14 +59,9 @@ module ElasticGraph
|
|
59
59
|
|
60
60
|
def process_filter_hash(bool_node, filter_hash, field_path)
|
61
61
|
filter_hash.each do |field_or_op, expression|
|
62
|
-
# `nil` filter predicates should be ignored, so we can safely `compact` them out.
|
63
|
-
# It also is simpler to handle them once here instead of the different branches
|
64
|
-
# below having to be aware of possible `nil` predicates.
|
65
|
-
expression = expression.compact if expression.is_a?(::Hash)
|
66
|
-
|
67
62
|
case filter_node_interpreter.identify_node_type(field_or_op, expression)
|
68
63
|
when :empty
|
69
|
-
# This is an "empty" filter predicate and
|
64
|
+
# This is an "empty" filter predicate and can be treated as `true`.
|
70
65
|
when :not
|
71
66
|
process_not_expression(bool_node, expression, field_path)
|
72
67
|
when :list_any_filter
|
@@ -109,10 +104,14 @@ module ElasticGraph
|
|
109
104
|
|
110
105
|
def process_not_expression(bool_node, expression, field_path)
|
111
106
|
sub_filter = build_bool_hash do |inner_node|
|
112
|
-
process_filter_hash(inner_node, expression, field_path)
|
107
|
+
process_filter_hash(inner_node, expression || {}, field_path)
|
113
108
|
end
|
114
109
|
|
115
|
-
|
110
|
+
unless sub_filter
|
111
|
+
# Since an empty expression is treated as `true`, convert to `false` when negating.
|
112
|
+
BooleanQuery::ALWAYS_FALSE_FILTER.merge_into(bool_node)
|
113
|
+
return
|
114
|
+
end
|
116
115
|
|
117
116
|
# Prevent any negated filters from being unnecessarily double-negated by
|
118
117
|
# converting them to a positive filter (i.e., !!A == A).
|
@@ -138,6 +137,8 @@ module ElasticGraph
|
|
138
137
|
# this because we do not generate `any_satisfy` filters on `object` list fields (instead,
|
139
138
|
# they get generated on their leaf fields).
|
140
139
|
def process_list_any_filter_expression(bool_node, filter, field_path)
|
140
|
+
return if filter.nil? || filter == {}
|
141
|
+
|
141
142
|
if filters_on_sub_fields?(filter)
|
142
143
|
process_any_satisfy_filter_expression_on_nested_object_list(bool_node, filter, field_path)
|
143
144
|
else
|
@@ -188,22 +189,37 @@ module ElasticGraph
|
|
188
189
|
end
|
189
190
|
end
|
190
191
|
|
192
|
+
# We want to provide the following semantics for `any_of`:
|
193
|
+
#
|
194
|
+
# * `filter: {anyOf: []}` -> return no results
|
195
|
+
# * `filter: {anyOf: [{field: null}]}` -> return all results
|
196
|
+
# * `filter: {anyOf: [{field: null}, {field: ...}]}` -> return all results
|
191
197
|
def process_any_of_expression(bool_node, expressions, field_path)
|
198
|
+
return if expressions.nil? || expressions == {}
|
199
|
+
|
200
|
+
if expressions.empty?
|
201
|
+
# When our `expressions` array is empty, we want to match no documents. However, that's
|
202
|
+
# not the behavior the datastore will give us if we have an empty array in the query under
|
203
|
+
# `should`. To get the behavior we want, we need to pass the datastore some filter criteria
|
204
|
+
# that will evaluate to false for every document.
|
205
|
+
BooleanQuery::ALWAYS_FALSE_FILTER.merge_into(bool_node)
|
206
|
+
return
|
207
|
+
end
|
208
|
+
|
192
209
|
shoulds = expressions.filter_map do |expression|
|
193
210
|
build_bool_hash do |inner_bool_node|
|
194
211
|
process_filter_hash(inner_bool_node, expression, field_path)
|
195
212
|
end
|
196
213
|
end
|
197
214
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
# criteria that will evaluate to false for every document.
|
202
|
-
bool_query = shoulds.empty? ? BooleanQuery::ALWAYS_FALSE_FILTER : BooleanQuery.should(*shoulds)
|
203
|
-
bool_query.merge_into(bool_node)
|
215
|
+
return if shoulds.size < expressions.size
|
216
|
+
|
217
|
+
BooleanQuery.should(*shoulds).merge_into(bool_node)
|
204
218
|
end
|
205
219
|
|
206
220
|
def process_all_of_expression(bool_node, expressions, field_path)
|
221
|
+
return if expressions.nil? || expressions == {}
|
222
|
+
|
207
223
|
# `all_of` represents an AND. AND is the default way that `process_filter_hash` combines
|
208
224
|
# filters so we just have to call it for each sub-expression.
|
209
225
|
expressions.each do |sub_expression|
|
@@ -212,6 +228,8 @@ module ElasticGraph
|
|
212
228
|
end
|
213
229
|
|
214
230
|
def process_operator_expression(bool_node, operator, expression, field_path)
|
231
|
+
return if expression.nil? || expression == {}
|
232
|
+
|
215
233
|
# `operator` is a filtering operator, and `expression` is the value the filtering
|
216
234
|
# operator should be applied to. The `op_applicator` lambda, when called, will
|
217
235
|
# return a Clause instance (defined in this module).
|
@@ -299,6 +317,8 @@ module ElasticGraph
|
|
299
317
|
end
|
300
318
|
|
301
319
|
def process_list_count_expression(bool_node, expression, field_path)
|
320
|
+
return if expression.nil? || expression == {}
|
321
|
+
|
302
322
|
# Normally, we don't have to do anything special for list count expressions.
|
303
323
|
# That's the case, for example, for an expression like:
|
304
324
|
#
|
@@ -313,7 +333,7 @@ module ElasticGraph
|
|
313
333
|
# While we index an explicit count of 0, the count field will be missing from documents indexed before
|
314
334
|
# the list field was defined on the ElasticGraph schema. To properly match those documents, we need to
|
315
335
|
# convert this into an OR (using `any_of`) to also match documents that lack the field entirely.
|
316
|
-
|
336
|
+
if filters_to_range_including_zero?(expression)
|
317
337
|
expression = {schema_names.any_of => [
|
318
338
|
expression,
|
319
339
|
{schema_names.equal_to_any_of => [nil]}
|
@@ -326,7 +346,7 @@ module ElasticGraph
|
|
326
346
|
def build_bool_hash(&block)
|
327
347
|
bool_node = Hash.new { |h, k| h[k] = [] }.tap(&block)
|
328
348
|
|
329
|
-
# To
|
349
|
+
# To treat "empty" filter predicates as `true` we need to return `nil` here.
|
330
350
|
return nil if bool_node.empty?
|
331
351
|
|
332
352
|
# According to https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html#bool-min-should-match,
|
@@ -337,20 +357,28 @@ module ElasticGraph
|
|
337
357
|
{bool: bool_node}
|
338
358
|
end
|
339
359
|
|
340
|
-
# Determines if the given
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
360
|
+
# Determines if the given expression filters to a range that includes `0`.
|
361
|
+
# If it does not do any filtering (e.g. an empty expression) it will return `false`.
|
362
|
+
def filters_to_range_including_zero?(expression)
|
363
|
+
expression = expression.compact
|
364
|
+
|
365
|
+
expression.size > 0 && expression.none? do |operator, operand|
|
366
|
+
operator_excludes_zero?(operator, operand)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
# Determines if the given operator and operand exclude 0 as a matched value.
|
371
|
+
def operator_excludes_zero?(operator, operand)
|
372
|
+
case operator
|
373
|
+
when schema_names.equal_to_any_of then !operand.include?(0)
|
374
|
+
when schema_names.lt then operand <= 0
|
375
|
+
when schema_names.lte then operand < 0
|
376
|
+
when schema_names.gt then operand >= 0
|
377
|
+
when schema_names.gte then operand > 0
|
378
|
+
else
|
379
|
+
# :nocov: -- all operators are covered above. But simplecov complains about an implicit `else` branch being uncovered, so here we've defined it to wrap it with `:nocov:`.
|
380
|
+
false
|
381
|
+
# :nocov:
|
354
382
|
end
|
355
383
|
end
|
356
384
|
|
@@ -25,22 +25,32 @@ module ElasticGraph
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def identify_node_type(field_or_op, sub_expression)
|
28
|
-
|
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
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def identify_by_field_or_op(field_or_op)
|
29
38
|
return :not if field_or_op == schema_names.not
|
30
39
|
return :list_any_filter if field_or_op == schema_names.any_satisfy
|
31
40
|
return :all_of if field_or_op == schema_names.all_of
|
32
41
|
return :any_of if field_or_op == schema_names.any_of
|
33
42
|
return :operator if filter_operators.key?(field_or_op)
|
34
43
|
return :list_count if field_or_op == LIST_COUNTS_FIELD
|
35
|
-
return :sub_field if sub_expression.is_a?(::Hash)
|
36
|
-
:unknown
|
37
|
-
end
|
38
44
|
|
39
|
-
|
40
|
-
@filter_operators ||= build_filter_operators(runtime_metadata)
|
45
|
+
nil
|
41
46
|
end
|
42
47
|
|
43
|
-
|
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
|
53
|
+
end
|
44
54
|
|
45
55
|
def build_filter_operators(runtime_metadata)
|
46
56
|
filter_by_time_of_day_script_id = runtime_metadata
|
@@ -12,9 +12,10 @@ 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, &build_set_for_filter)
|
15
|
+
def initialize(schema_names, all_values_set, empty_set, &build_set_for_filter)
|
16
16
|
@schema_names = schema_names
|
17
17
|
@all_values_set = all_values_set
|
18
|
+
@empty_set = empty_set
|
18
19
|
@build_set_for_filter = build_set_for_filter
|
19
20
|
end
|
20
21
|
|
@@ -55,7 +56,7 @@ module ElasticGraph
|
|
55
56
|
# outside the `map_reduce_sets` block below so we only do it once instead of N times.
|
56
57
|
target_field_path_parts = target_field_path.split(".")
|
57
58
|
|
58
|
-
# Here we intersect the filter value
|
59
|
+
# Here we intersect the filter value set, because when we have multiple `filter_hashes`,
|
59
60
|
# the filters are ANDed together. Only documents that match ALL the filters will be
|
60
61
|
# returned. Therefore, we want the intersection of filter value sets.
|
61
62
|
map_reduce_sets(filter_hashes, :intersection, negate: false) do |filter_hash|
|
@@ -83,7 +84,7 @@ module ElasticGraph
|
|
83
84
|
# to a particular field.
|
84
85
|
def filter_value_set_for_filter_hash_entry(field_or_op, filter_value, target_field_path_parts, traversed_field_path_parts, negate:)
|
85
86
|
if filter_value.nil?
|
86
|
-
# Any filter with a `nil` value is effectively
|
87
|
+
# Any filter with a `nil` value is effectively treated as `true` by our filtering logic, so we need
|
87
88
|
# to return our `@all_values_set` to indicate this filter matches all documents.
|
88
89
|
@all_values_set
|
89
90
|
elsif field_or_op == @schema_names.not
|
@@ -105,6 +106,11 @@ module ElasticGraph
|
|
105
106
|
|
106
107
|
# Determines the set of filter values for an `any_of` clause, which is used for ORing multiple filters together.
|
107
108
|
def filter_value_set_for_any_of(filter_hashes, target_field_path_parts, traversed_field_path_parts, negate:)
|
109
|
+
# Here we treat `any_of: []` as matching no values.
|
110
|
+
if filter_hashes.empty?
|
111
|
+
return negate ? @all_values_set : @empty_set
|
112
|
+
end
|
113
|
+
|
108
114
|
# Here we union the filter value sets because `any_of` represents an OR. If we can determine specific
|
109
115
|
# filter values for all `any_of` clauses, we will OR them together. Alternately, if we cannot
|
110
116
|
# determine specific filter values for any clauses, we will union `@all_values_set`,
|
@@ -130,6 +130,10 @@ module ElasticGraph
|
|
130
130
|
# Ignore an empty string operationName.
|
131
131
|
params = params.merge("operationName" => nil) if params["operationName"] && params["operationName"].empty?
|
132
132
|
|
133
|
+
if (variables = params["variables"]) && !variables.is_a?(::Hash)
|
134
|
+
return HTTPResponse.error(400, "`variables` must be a JSON object but was not.")
|
135
|
+
end
|
136
|
+
|
133
137
|
yield params
|
134
138
|
end
|
135
139
|
|
@@ -101,7 +101,7 @@ module ElasticGraph
|
|
101
101
|
|
102
102
|
def filter_value_set_extractor
|
103
103
|
@filter_value_set_extractor ||=
|
104
|
-
Filtering::FilterValueSetExtractor.new(schema_element_names, IncludesNilSet) do |operator, filter_value|
|
104
|
+
Filtering::FilterValueSetExtractor.new(schema_element_names, IncludesNilSet, ExcludesNilSet) do |operator, filter_value|
|
105
105
|
if operator == :equal_to_any_of && filter_value.include?(nil)
|
106
106
|
IncludesNilSet
|
107
107
|
else
|
@@ -29,7 +29,7 @@ module ElasticGraph
|
|
29
29
|
scalar_types.to_set.union(introspection_types)
|
30
30
|
)
|
31
31
|
|
32
|
-
attr_reader :element_names, :
|
32
|
+
attr_reader :element_names, :config, :graphql_schema, :runtime_metadata
|
33
33
|
|
34
34
|
def initialize(
|
35
35
|
graphql_schema_string:,
|
@@ -53,7 +53,6 @@ module ElasticGraph
|
|
53
53
|
)
|
54
54
|
end
|
55
55
|
|
56
|
-
@types_by_name = Hash.new { |hash, key| hash[key] = lookup_type_by_name(key) }
|
57
56
|
@build_resolver = build_resolver
|
58
57
|
|
59
58
|
# Note: as part of loading the schema, the GraphQL gem may use the resolver (such
|
@@ -68,7 +67,7 @@ module ElasticGraph
|
|
68
67
|
|
69
68
|
# Pre-load all defined types so that all field extras can get configured as part
|
70
69
|
# of loading the schema, before we execute the first query.
|
71
|
-
@
|
70
|
+
@types_by_name = build_types_by_name
|
72
71
|
end
|
73
72
|
|
74
73
|
def type_from(graphql_type)
|
@@ -80,7 +79,11 @@ module ElasticGraph
|
|
80
79
|
# get type objects for wrapped types, but you need to get it from a field object of that
|
81
80
|
# type.
|
82
81
|
def type_named(type_name)
|
83
|
-
@types_by_name
|
82
|
+
@types_by_name.fetch(type_name.to_s)
|
83
|
+
rescue KeyError => e
|
84
|
+
msg = "No type named #{type_name} could be found"
|
85
|
+
msg += "; Possible alternatives: [#{e.corrections.join(", ").delete('"')}]." if e.corrections.any?
|
86
|
+
raise Errors::NotFoundError, msg
|
84
87
|
end
|
85
88
|
|
86
89
|
def document_type_stored_in(index_definition_name)
|
@@ -106,6 +109,10 @@ module ElasticGraph
|
|
106
109
|
@indexed_document_types ||= defined_types.select(&:indexed_document?)
|
107
110
|
end
|
108
111
|
|
112
|
+
def defined_types
|
113
|
+
@defined_types ||= @types_by_name.except(*BUILT_IN_TYPE_NAMES).values
|
114
|
+
end
|
115
|
+
|
109
116
|
def to_s
|
110
117
|
"#<#{self.class.name} 0x#{__id__.to_s(16)} indexed_document_types=#{indexed_document_types.map(&:name).sort.to_s.delete(":")}>"
|
111
118
|
end
|
@@ -128,24 +135,14 @@ module ElasticGraph
|
|
128
135
|
def_delegators :resolver, :call, :resolve_type, :coerce_input, :coerce_result
|
129
136
|
end
|
130
137
|
|
131
|
-
def lookup_type_by_name(type_name)
|
132
|
-
type_from(@graphql_schema.types.fetch(type_name))
|
133
|
-
rescue KeyError => e
|
134
|
-
msg = "No type named #{type_name} could be found"
|
135
|
-
msg += "; Possible alternatives: [#{e.corrections.join(", ").delete('"')}]." if e.corrections.any?
|
136
|
-
raise Errors::NotFoundError, msg
|
137
|
-
end
|
138
|
-
|
139
138
|
def resolver
|
140
139
|
@resolver ||= @build_resolver.call(self)
|
141
140
|
end
|
142
141
|
|
143
|
-
def
|
144
|
-
graphql_schema
|
145
|
-
|
146
|
-
|
147
|
-
.reject { |t| BUILT_IN_TYPE_NAMES.include?(t.graphql_name) }
|
148
|
-
.map { |t| type_named(t.graphql_name) }
|
142
|
+
def build_types_by_name
|
143
|
+
graphql_schema.types.transform_values do |graphql_type|
|
144
|
+
@types_by_graphql_type[graphql_type]
|
145
|
+
end
|
149
146
|
end
|
150
147
|
|
151
148
|
def indexed_document_types_by_index_definition_name
|
@@ -142,7 +142,13 @@ module ElasticGraph
|
|
142
142
|
def graphql_gem_plugins
|
143
143
|
@graphql_gem_plugins ||= begin
|
144
144
|
require "graphql"
|
145
|
-
{
|
145
|
+
{
|
146
|
+
# We depend on this to avoid N+1 calls to the datastore.
|
147
|
+
::GraphQL::Dataloader => {},
|
148
|
+
# This is new in the graphql-ruby 2.4 release, and will be required in the future.
|
149
|
+
# We pass `preload: true` because the way we handle the schema depends on it being preloaded.
|
150
|
+
::GraphQL::Schema::Visibility => {preload: true}
|
151
|
+
}
|
146
152
|
end
|
147
153
|
end
|
148
154
|
|
@@ -244,6 +250,9 @@ module ElasticGraph
|
|
244
250
|
# at boot time instead of deferring dependency loading until we handle the first query. In other environments (such as tests),
|
245
251
|
# it's nice to load dependencies when needed.
|
246
252
|
def load_dependencies_eagerly
|
253
|
+
require "graphql"
|
254
|
+
::GraphQL.eager_load!
|
255
|
+
|
247
256
|
# run a simple GraphQL query to force load any dependencies needed to handle GraphQL queries
|
248
257
|
graphql_query_executor.execute(EAGER_LOAD_QUERY, client: Client::ELASTICGRAPH_INTERNAL)
|
249
258
|
graphql_http_endpoint # force load this too.
|
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elasticgraph-graphql
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.19.0.0.rc2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Myron Marston
|
8
8
|
- Ben VandenBos
|
9
|
-
-
|
9
|
+
- Block Engineering
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date: 2024-
|
13
|
+
date: 2024-12-03 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rubocop-factory_bot
|
@@ -46,42 +46,42 @@ dependencies:
|
|
46
46
|
requirements:
|
47
47
|
- - "~>"
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: '3.
|
49
|
+
version: '3.1'
|
50
50
|
type: :development
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
53
53
|
requirements:
|
54
54
|
- - "~>"
|
55
55
|
- !ruby/object:Gem::Version
|
56
|
-
version: '3.
|
56
|
+
version: '3.1'
|
57
57
|
- !ruby/object:Gem::Dependency
|
58
58
|
name: standard
|
59
59
|
requirement: !ruby/object:Gem::Requirement
|
60
60
|
requirements:
|
61
61
|
- - "~>"
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: 1.
|
63
|
+
version: 1.41.0
|
64
64
|
type: :development
|
65
65
|
prerelease: false
|
66
66
|
version_requirements: !ruby/object:Gem::Requirement
|
67
67
|
requirements:
|
68
68
|
- - "~>"
|
69
69
|
- !ruby/object:Gem::Version
|
70
|
-
version: 1.
|
70
|
+
version: 1.41.0
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
72
|
name: steep
|
73
73
|
requirement: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
75
|
- - "~>"
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version: '1.
|
77
|
+
version: '1.8'
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
80
|
version_requirements: !ruby/object:Gem::Requirement
|
81
81
|
requirements:
|
82
82
|
- - "~>"
|
83
83
|
- !ruby/object:Gem::Version
|
84
|
-
version: '1.
|
84
|
+
version: '1.8'
|
85
85
|
- !ruby/object:Gem::Dependency
|
86
86
|
name: coderay
|
87
87
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,14 +136,14 @@ dependencies:
|
|
136
136
|
requirements:
|
137
137
|
- - "~>"
|
138
138
|
- !ruby/object:Gem::Version
|
139
|
-
version: '0.
|
139
|
+
version: '0.13'
|
140
140
|
type: :development
|
141
141
|
prerelease: false
|
142
142
|
version_requirements: !ruby/object:Gem::Requirement
|
143
143
|
requirements:
|
144
144
|
- - "~>"
|
145
145
|
- !ruby/object:Gem::Version
|
146
|
-
version: '0.
|
146
|
+
version: '0.13'
|
147
147
|
- !ruby/object:Gem::Dependency
|
148
148
|
name: simplecov
|
149
149
|
requirement: !ruby/object:Gem::Requirement
|
@@ -254,126 +254,126 @@ dependencies:
|
|
254
254
|
requirements:
|
255
255
|
- - "~>"
|
256
256
|
- !ruby/object:Gem::Version
|
257
|
-
version: '3.
|
257
|
+
version: '3.5'
|
258
258
|
type: :development
|
259
259
|
prerelease: false
|
260
260
|
version_requirements: !ruby/object:Gem::Requirement
|
261
261
|
requirements:
|
262
262
|
- - "~>"
|
263
263
|
- !ruby/object:Gem::Version
|
264
|
-
version: '3.
|
264
|
+
version: '3.5'
|
265
265
|
- !ruby/object:Gem::Dependency
|
266
266
|
name: elasticgraph-datastore_core
|
267
267
|
requirement: !ruby/object:Gem::Requirement
|
268
268
|
requirements:
|
269
269
|
- - '='
|
270
270
|
- !ruby/object:Gem::Version
|
271
|
-
version: 0.
|
271
|
+
version: 0.19.0.0.rc2
|
272
272
|
type: :runtime
|
273
273
|
prerelease: false
|
274
274
|
version_requirements: !ruby/object:Gem::Requirement
|
275
275
|
requirements:
|
276
276
|
- - '='
|
277
277
|
- !ruby/object:Gem::Version
|
278
|
-
version: 0.
|
278
|
+
version: 0.19.0.0.rc2
|
279
279
|
- !ruby/object:Gem::Dependency
|
280
280
|
name: elasticgraph-schema_artifacts
|
281
281
|
requirement: !ruby/object:Gem::Requirement
|
282
282
|
requirements:
|
283
283
|
- - '='
|
284
284
|
- !ruby/object:Gem::Version
|
285
|
-
version: 0.
|
285
|
+
version: 0.19.0.0.rc2
|
286
286
|
type: :runtime
|
287
287
|
prerelease: false
|
288
288
|
version_requirements: !ruby/object:Gem::Requirement
|
289
289
|
requirements:
|
290
290
|
- - '='
|
291
291
|
- !ruby/object:Gem::Version
|
292
|
-
version: 0.
|
292
|
+
version: 0.19.0.0.rc2
|
293
293
|
- !ruby/object:Gem::Dependency
|
294
294
|
name: graphql
|
295
295
|
requirement: !ruby/object:Gem::Requirement
|
296
296
|
requirements:
|
297
297
|
- - "~>"
|
298
298
|
- !ruby/object:Gem::Version
|
299
|
-
version: 2.
|
299
|
+
version: 2.4.5
|
300
300
|
type: :runtime
|
301
301
|
prerelease: false
|
302
302
|
version_requirements: !ruby/object:Gem::Requirement
|
303
303
|
requirements:
|
304
304
|
- - "~>"
|
305
305
|
- !ruby/object:Gem::Version
|
306
|
-
version: 2.
|
306
|
+
version: 2.4.5
|
307
307
|
- !ruby/object:Gem::Dependency
|
308
308
|
name: elasticgraph-admin
|
309
309
|
requirement: !ruby/object:Gem::Requirement
|
310
310
|
requirements:
|
311
311
|
- - '='
|
312
312
|
- !ruby/object:Gem::Version
|
313
|
-
version: 0.
|
313
|
+
version: 0.19.0.0.rc2
|
314
314
|
type: :development
|
315
315
|
prerelease: false
|
316
316
|
version_requirements: !ruby/object:Gem::Requirement
|
317
317
|
requirements:
|
318
318
|
- - '='
|
319
319
|
- !ruby/object:Gem::Version
|
320
|
-
version: 0.
|
320
|
+
version: 0.19.0.0.rc2
|
321
321
|
- !ruby/object:Gem::Dependency
|
322
322
|
name: elasticgraph-elasticsearch
|
323
323
|
requirement: !ruby/object:Gem::Requirement
|
324
324
|
requirements:
|
325
325
|
- - '='
|
326
326
|
- !ruby/object:Gem::Version
|
327
|
-
version: 0.
|
327
|
+
version: 0.19.0.0.rc2
|
328
328
|
type: :development
|
329
329
|
prerelease: false
|
330
330
|
version_requirements: !ruby/object:Gem::Requirement
|
331
331
|
requirements:
|
332
332
|
- - '='
|
333
333
|
- !ruby/object:Gem::Version
|
334
|
-
version: 0.
|
334
|
+
version: 0.19.0.0.rc2
|
335
335
|
- !ruby/object:Gem::Dependency
|
336
336
|
name: elasticgraph-opensearch
|
337
337
|
requirement: !ruby/object:Gem::Requirement
|
338
338
|
requirements:
|
339
339
|
- - '='
|
340
340
|
- !ruby/object:Gem::Version
|
341
|
-
version: 0.
|
341
|
+
version: 0.19.0.0.rc2
|
342
342
|
type: :development
|
343
343
|
prerelease: false
|
344
344
|
version_requirements: !ruby/object:Gem::Requirement
|
345
345
|
requirements:
|
346
346
|
- - '='
|
347
347
|
- !ruby/object:Gem::Version
|
348
|
-
version: 0.
|
348
|
+
version: 0.19.0.0.rc2
|
349
349
|
- !ruby/object:Gem::Dependency
|
350
350
|
name: elasticgraph-indexer
|
351
351
|
requirement: !ruby/object:Gem::Requirement
|
352
352
|
requirements:
|
353
353
|
- - '='
|
354
354
|
- !ruby/object:Gem::Version
|
355
|
-
version: 0.
|
355
|
+
version: 0.19.0.0.rc2
|
356
356
|
type: :development
|
357
357
|
prerelease: false
|
358
358
|
version_requirements: !ruby/object:Gem::Requirement
|
359
359
|
requirements:
|
360
360
|
- - '='
|
361
361
|
- !ruby/object:Gem::Version
|
362
|
-
version: 0.
|
362
|
+
version: 0.19.0.0.rc2
|
363
363
|
- !ruby/object:Gem::Dependency
|
364
364
|
name: elasticgraph-schema_definition
|
365
365
|
requirement: !ruby/object:Gem::Requirement
|
366
366
|
requirements:
|
367
367
|
- - '='
|
368
368
|
- !ruby/object:Gem::Version
|
369
|
-
version: 0.
|
369
|
+
version: 0.19.0.0.rc2
|
370
370
|
type: :development
|
371
371
|
prerelease: false
|
372
372
|
version_requirements: !ruby/object:Gem::Requirement
|
373
373
|
requirements:
|
374
374
|
- - '='
|
375
375
|
- !ruby/object:Gem::Version
|
376
|
-
version: 0.
|
376
|
+
version: 0.19.0.0.rc2
|
377
377
|
description:
|
378
378
|
email:
|
379
379
|
- myron@squareup.com
|
@@ -461,10 +461,15 @@ files:
|
|
461
461
|
- lib/elastic_graph/graphql/schema/type.rb
|
462
462
|
- script/dump_time_zones
|
463
463
|
- script/dump_time_zones.java
|
464
|
-
homepage:
|
464
|
+
homepage: https://block.github.io/elasticgraph/
|
465
465
|
licenses:
|
466
466
|
- MIT
|
467
467
|
metadata:
|
468
|
+
bug_tracker_uri: https://github.com/block/elasticgraph/issues
|
469
|
+
changelog_uri: https://github.com/block/elasticgraph/releases/tag/v0.19.0.0.rc2
|
470
|
+
documentation_uri: https://block.github.io/elasticgraph/docs/main/
|
471
|
+
homepage_uri: https://block.github.io/elasticgraph/
|
472
|
+
source_code_uri: https://github.com/block/elasticgraph/tree/v0.19.0.0.rc2/elasticgraph-graphql
|
468
473
|
gem_category: core
|
469
474
|
post_install_message:
|
470
475
|
rdoc_options: []
|