Class: ElasticGraph::GraphQL::Schema::Type

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/elastic_graph/graphql/schema/type.rb

Overview

Represents a GraphQL type.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(schema, graphql_type, index_definitions, object_runtime_metadata, enum_runtime_metadata, scalar_runtime_metadata, resolvers_needing_lookahead) ⇒ Type

Returns a new instance of Type.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# File 'lib/elastic_graph/graphql/schema/type.rb', line 28

def initialize(
  schema,
  graphql_type,
  index_definitions,
  ,
  ,
  ,
  resolvers_needing_lookahead
)
  @schema = schema
  @graphql_type = graphql_type
  @enum_values_by_name = Hash.new do |hash, key|
    hash[key] = lookup_enum_value_by_name(key)
  end

  @index_definitions = index_definitions
  @object_runtime_metadata = 
  @elasticgraph_category = &.elasticgraph_category
  @graphql_only_return_type = &.graphql_only_return_type
  @enum_runtime_metadata = 
  @resolvers_needing_lookahead = resolvers_needing_lookahead
  @enum_value_names_by_original_name = (&.values_by_name || {}).to_h do |name, value|
    [value.alternate_original_name || name, name]
  end

  @fields_by_name = build_fields_by_name_hash(schema, graphql_type).freeze
  @grouping_missing_value_placeholder = determine_grouping_missing_value_placeholder(&.grouping_missing_value_placeholder)
end

Instance Attribute Details

#elasticgraph_categoryObject (readonly)

Returns the value of attribute elasticgraph_category.



20
21
22
# File 'lib/elastic_graph/graphql/schema/type.rb', line 20

def elasticgraph_category
  @elasticgraph_category
end

#fields_by_nameObject (readonly)

Returns the value of attribute fields_by_name.



20
21
22
# File 'lib/elastic_graph/graphql/schema/type.rb', line 20

def fields_by_name
  @fields_by_name
end

#graphql_only_return_typeObject (readonly)

Returns the value of attribute graphql_only_return_type.



20
21
22
# File 'lib/elastic_graph/graphql/schema/type.rb', line 20

def graphql_only_return_type
  @graphql_only_return_type
end

#graphql_typeObject (readonly)

Returns the value of attribute graphql_type.



20
21
22
# File 'lib/elastic_graph/graphql/schema/type.rb', line 20

def graphql_type
  @graphql_type
end

#grouping_missing_value_placeholderString, ... (readonly)

Returns the grouping missing value placeholder for this type, if one is defined. This is used to handle missing values in aggregations without creating separate missing subaggregations, reducing the exponential explosion of subaggregations.

Returns:

  • (String, Numeric, nil)

    the placeholder value to use for missing values in grouping operations



26
27
28
# File 'lib/elastic_graph/graphql/schema/type.rb', line 26

def grouping_missing_value_placeholder
  @grouping_missing_value_placeholder
end

#index_definitionsObject (readonly)

Returns the value of attribute index_definitions.



20
21
22
# File 'lib/elastic_graph/graphql/schema/type.rb', line 20

def index_definitions
  @index_definitions
end

Instance Method Details

#abstract?Boolean

Returns:

  • (Boolean)


205
206
207
208
# File 'lib/elastic_graph/graphql/schema/type.rb', line 205

def abstract?
  return unwrap_non_null.abstract? if non_null?
  @graphql_type.kind.abstract?
end

#coerce_result(result) ⇒ Object



173
174
175
# File 'lib/elastic_graph/graphql/schema/type.rb', line 173

def coerce_result(result)
  @enum_value_names_by_original_name.fetch(result, result)
end

#collection?Boolean

Returns:

  • (Boolean)


247
248
249
# File 'lib/elastic_graph/graphql/schema/type.rb', line 247

def collection?
  list? || relay_connection?
end

#embedded_object?Boolean

Indicates if this type is an object type that is embedded in another indexed type in the index mapping. Note: we have avoided the term ‘nested` here because it is a specific Elasticsearch/OpenSearch mapping type that we will not necessarily be using: www.elastic.co/guide/en/elasticsearch/reference/current/nested.html

Returns:

  • (Boolean)


241
242
243
244
245
# File 'lib/elastic_graph/graphql/schema/type.rb', line 241

def embedded_object?
  return unwrap_non_null.embedded_object? if non_null?
  return false if relay_edge? || relay_connection? || @graphql_type.kind.input_object?
  object? && !indexed_document? && !indexed_aggregation?
end

#enum?Boolean

Returns:

  • (Boolean)


210
211
212
213
# File 'lib/elastic_graph/graphql/schema/type.rb', line 210

def enum?
  return unwrap_non_null.enum? if non_null?
  @graphql_type.kind.enum?
end

#enum_value_named(enum_value_name) ⇒ Object



169
170
171
# File 'lib/elastic_graph/graphql/schema/type.rb', line 169

def enum_value_named(enum_value_name)
  @enum_values_by_name[enum_value_name]
end

#field_named(field_name) ⇒ Object



157
158
159
160
161
162
163
# File 'lib/elastic_graph/graphql/schema/type.rb', line 157

def field_named(field_name)
  @fields_by_name.fetch(field_name)
rescue KeyError => e
  msg = "No field named #{field_name} (on type #{name}) could be found"
  msg += "; Possible alternatives: [#{e.corrections.join(", ").delete('"')}]." if e.corrections.any?
  raise Errors::NotFoundError, msg
end

#fields_by_name_in_indexObject



165
166
167
# File 'lib/elastic_graph/graphql/schema/type.rb', line 165

def fields_by_name_in_index
  @fields_by_name_in_index ||= @fields_by_name.values.group_by(&:name_in_index)
end

#hidden_from_queries?Boolean

Indicates this type should be hidden in the GraphQL schema so as to not be queryable. We only hide a type if both of the following are true:

  • It’s backed by one or more search index definitions

  • None of the search index definitions are accessible from queries

Returns:

  • (Boolean)


264
265
266
267
# File 'lib/elastic_graph/graphql/schema/type.rb', line 264

def hidden_from_queries?
  return false if search_index_definitions.empty?
  search_index_definitions.none?(&:accessible_from_queries?)
end

#indexed_aggregation?Boolean

Returns:

  • (Boolean)


233
234
235
# File 'lib/elastic_graph/graphql/schema/type.rb', line 233

def indexed_aggregation?
  unwrapped_has_category?(:indexed_aggregation)
end

#indexed_document?Boolean

Is the type a user-defined document type directly indexed in the index?

Returns:

  • (Boolean)


226
227
228
229
230
231
# File 'lib/elastic_graph/graphql/schema/type.rb', line 226

def indexed_document?
  return unwrap_non_null.indexed_document? if non_null?
  return false if indexed_aggregation?
  return true if subtypes.any? && subtypes.all?(&:indexed_document?)
  @index_definitions.any?
end

#nameObject



57
58
59
# File 'lib/elastic_graph/graphql/schema/type.rb', line 57

def name
  @name ||= @graphql_type.to_type_signature
end

#nullable?Boolean

Returns:

  • (Boolean)


201
202
203
# File 'lib/elastic_graph/graphql/schema/type.rb', line 201

def nullable?
  !non_null?
end

#object?Boolean

Returns ‘true` if this type serializes as a JSON object, with sub-fields. Note this is slightly different from the GraphQL gem and GraphQL spec: it considers inputs to be distinct from objects, but for our purposes we consider inputs to be objects since they have sub-fields and serialize as JSON objects.

Returns:

  • (Boolean)


219
220
221
222
223
# File 'lib/elastic_graph/graphql/schema/type.rb', line 219

def object?
  return unwrap_non_null.object? if non_null?
  kind = @graphql_type.kind
  kind.abstract? || kind.object? || kind.input_object?
end

#relay_connection?Boolean

Returns:

  • (Boolean)


251
252
253
# File 'lib/elastic_graph/graphql/schema/type.rb', line 251

def relay_connection?
  unwrapped_has_category?(:relay_connection)
end

#relay_edge?Boolean

Returns:

  • (Boolean)


255
256
257
# File 'lib/elastic_graph/graphql/schema/type.rb', line 255

def relay_edge?
  unwrapped_has_category?(:relay_edge)
end

#search_index_definitionsObject Also known as: indexing_index_definitions

List of index definitions that should be searched for this type.



62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/elastic_graph/graphql/schema/type.rb', line 62

def search_index_definitions
  @search_index_definitions ||=
    if (st = source_type)
      # When a type has a source type (a prime example being indexed aggregations), we delegate
      # to the source type. This works better than dumping index definitions in the runtime metadata
      # of the derived type itself because of abstract (interface/union) types. The source document
      # type handles that (since there is a supertype/subtype relationship on the document types)
      # but that relationship does not exist on derived types.
      #
      # For example, assume we have these indexed document types:
      # - type Person {}
      # - type Company {}
      # - union Inventor = Person | Company
      #
      # We can go from `Inventor` to its subtypes to find the search indexes. However, `InventorAggregation`
      # is NOT a union of `PersonAggregation` and `CompanyAggregation`, so we can't do the same thing on the
      # indexed aggregation types. Delegating to the source type solves this case.
      st.search_index_definitions
    elsif abstract?
      # For abstract types, derive search indexes purely from concrete subtypes. This correctly
      # handles cases where subtypes override the abstract type's declared index with a dedicated
      # one — only indexes that actually contain documents for this type are searched.
      # Note: subtypes returns all concrete subtypes at any depth, so no explicit recursion is needed.
      subtypes.flat_map(&:search_index_definitions).to_set
    else
      @index_definitions
    end
end

#shares_index_with_non_subtypes?Boolean

Returns true if any of this type’s search indexes contain any concrete document types that are not subtypes of this type. Used to determine whether a ‘__typename` filter is needed when querying an abstract type.

Returns:

  • (Boolean)


147
148
149
150
151
152
153
154
155
# File 'lib/elastic_graph/graphql/schema/type.rb', line 147

def shares_index_with_non_subtypes?
  return @shares_index_with_non_subtypes if defined?(@shares_index_with_non_subtypes)
  @shares_index_with_non_subtypes =
    search_index_definitions.any? do |index_def|
      @schema.document_types_stored_in(index_def.name).any? do |t|
        t != self && !subtypes.include?(t) && !t.abstract?
      end
    end
end

#source_typeObject

For derived types (e.g. indexed aggregations), returns the underlying source document type. Returns ‘nil` for non-derived types.



139
140
141
142
# File 'lib/elastic_graph/graphql/schema/type.rb', line 139

def source_type
  return @source_type if defined?(@source_type)
  @source_type = @object_runtime_metadata&.source_type&.then { |st| @schema.type_named(st) }
end

#subtypesObject

Returns all concrete subtypes, at any depth. This is like ‘#possible_types` provided by the GraphQL gem, but that includes a type itself when you ask for the possible types of a non-abstract type.



128
129
130
131
132
133
134
135
# File 'lib/elastic_graph/graphql/schema/type.rb', line 128

def subtypes
  @subtypes ||= @schema
    .graphql_schema
    .possible_types(graphql_type, visibility_profile: :boot)
    .map { |t| @schema.type_from(t) }
    .reject { |t| t == self }
    .to_set
end

#to_sObject Also known as: inspect



177
178
179
# File 'lib/elastic_graph/graphql/schema/type.rb', line 177

def to_s
  "#<#{self.class.name} #{name}>"
end

#unwrap_fullyObject

Fully unwraps this type, in order to extracts the underlying type (an object or scalar) from its wrappings. As needed, this will unwrap any of these wrappings:

- non-null
- list
- relay connection


112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/elastic_graph/graphql/schema/type.rb', line 112

def unwrap_fully
  @unwrap_fully ||= begin
    unwrapped = @schema.type_from(@graphql_type.unwrap)

    if unwrapped.relay_connection?
      unwrapped
        .field_named(@schema.element_names.edges).type.unwrap_fully
        .field_named(@schema.element_names.node).type.unwrap_fully
    else
      unwrapped
    end
  end
end

#unwrap_non_nullObject

Unwraps the non-null type wrapping, if this type is non-null. If this type is nullable, returns it as-is.



101
102
103
104
# File 'lib/elastic_graph/graphql/schema/type.rb', line 101

def unwrap_non_null
  return self if nullable?
  @schema.type_from(@graphql_type.of_type)
end