Class: ElasticGraph::SchemaDefinition::SchemaElements::Relationship

Inherits:
Field
  • Object
show all
Defined in:
lib/elastic_graph/schema_definition/schema_elements/relationship.rb

Overview

Wraps a Field to provide additional relationship-specific functionality when defining a field via TypeWithSubfields#relates_to_one or TypeWithSubfields#relates_to_many.

Examples:

Define relationships between two types

ElasticGraph.define_schema do |schema|
  schema.object_type "Orchestra" do |t|
    t.field "id", "ID"
    t.relates_to_many "musicians", "Musician", via: "orchestraId", dir: :in, singular: "musician" do |r|
      # In this block, `r` is a `Relationship`.
    end
    t.index "orchestras"
  end

  schema.object_type "Musician" do |t|
    t.field "id", "ID"
    t.field "instrument", "String"
    t.relates_to_one "orchestra", "Orchestra", via: "orchestraId", dir: :out do |r|
      # In this block, `r` is a `Relationship`.
    end
    t.index "musicians"
  end
end

Constant Summary

Constants inherited from Field

Field::ACCURACY_SCORES

Constants included from Mixins::HasTypeInfo

Mixins::HasTypeInfo::CUSTOMIZABLE_DATASTORE_PARAMS

Instance Attribute Summary collapse

Attributes inherited from Field

#accuracy_confidence, #aggregatable, #aggregated_values_customizations, #args, #as_input, #backing_indexing_field, #filter_customizations, #filterable, #graphql_only, #groupable, #grouped_by_customizations, #legacy_grouping_schema, #name, #non_nullable_in_json_schema, #original_type, #original_type_for_derived_types, #parent_type, #relationship, #runtime_field_script, #runtime_metadata_graphql_field, #schema_def_state, #singular_name, #sort_order_enum_value_customizations, #sortable, #source, #sub_aggregations_customizations

Attributes included from Mixins::HasDocumentation

#doc_comment

Instance Method Summary collapse

Methods inherited from Field

#add_grouped_by_field_documentation, #aggregatable?, #argument, #customize_aggregated_values_field, #customize_filter_field, #customize_grouped_by_field, #customize_sort_order_enum_values, #customize_sub_aggregations_field, #define_aggregated_values_field, #define_grouped_by_field, #define_relay_pagination_arguments!, #define_sub_aggregations_field, #filterable?, #groupable?, #grouped_by_field_name, #grouped_by_field_type_name, #index_leaf?, #json_schema, #list_field_groupable_by_single_values?, #mapping_type, #name_in_index, #nested?, #on_each_generated_schema_element, #paths_to_lists_for_count_indexing, pick_most_accurate_from, #renamed_from, #resolve_mapping, #runtime_script, #sortable?, #sourced_from, #sub_aggregatable?, #to_filter_field, #to_indexing_field, #to_indexing_field_reference, #to_sdl, #type, #type_for_derived_types

Methods included from Mixins::HasTypeInfo

#json_schema, #json_schema_options, #mapping, #mapping_options

Methods included from Mixins::HasDirectives

#directive, #directives, #directives_sdl

Methods included from Mixins::HasDocumentation

#append_to_documentation, #derived_documentation, #documentation, #formatted_documentation

Methods included from Mixins::VerifiesGraphQLName

verify_name!

Constructor Details

#initialize(field, cardinality:, related_type:, foreign_key:, direction:) ⇒ Relationship

Returns a new instance of Relationship.



46
47
48
49
50
51
52
53
54
# File 'lib/elastic_graph/schema_definition/schema_elements/relationship.rb', line 46

def initialize(field, cardinality:, related_type:, foreign_key:, direction:)
  super(field)
  @cardinality = cardinality
  @related_type = related_type
  @foreign_key = foreign_key
  @direction = direction
  @equivalent_field_paths_by_local_path = {}
  @additional_filter = {}
end

Instance Attribute Details

Returns the type this relationship relates to.

Returns:



43
44
45
# File 'lib/elastic_graph/schema_definition/schema_elements/relationship.rb', line 43

def related_type
  @related_type
end

Instance Method Details

#additional_filter(filter) ⇒ void

This method returns an undefined value.

Adds additional filter conditions to a relationship beyond the foreign key.

Examples:

Define additional filter conditions on a ‘relates_to_one` relationship

ElasticGraph.define_schema do |schema|
  schema.object_type "Orchestra" do |t|
    t.field "id", "ID"
    t.relates_to_many "musicians", "Musician", via: "orchestraId", dir: :in, singular: "musician"
    t.relates_to_one "firstViolin", "Musician", via: "orchestraId", dir: :in do |r|
      r.additional_filter isFirstViolon: true
    end

    t.index "orchestras"
  end

  schema.object_type "Musician" do |t|
    t.field "id", "ID"
    t.field "instrument", "String"
    t.field "isFirstViolon", "Boolean"
    t.relates_to_one "orchestra", "Orchestra", via: "orchestraId", dir: :out
    t.index "musicians"
  end
end

Parameters:

  • filter (Hash<Symbol, Object>, Hash<String, Object>)

    additional filter conditions for this relationship



81
82
83
84
# File 'lib/elastic_graph/schema_definition/schema_elements/relationship.rb', line 81

def additional_filter(filter)
  stringified_filter = Support::HashUtil.stringify_keys(filter)
  @additional_filter = Support::HashUtil.deep_merge(@additional_filter, stringified_filter)
end

#equivalent_field(path, locally_named: path) ⇒ void

This method returns an undefined value.

Indicates that ‘path` (a field on the related type) is the equivalent of `locally_named` on this type.

Use this API to specify a local field’s equivalent path on the related type. This must be used on relationships used by Field#sourced_from when the local type uses Indexing::Index#route_with or Indexing::Index#rollover so that ElasticGraph can determine what field from the related type to use to route the update requests to the correct index and shard.

Examples:

ElasticGraph.define_schema do |schema|
  schema.object_type "Campaign" do |t|
    t.field "id", "ID!"
    t.field "name", "String"
    t.field "createdAt", "DateTime"

    t.relates_to_one "launchPlan", "CampaignLaunchPlan", via: "campaignId", dir: :in do |r|
      r.equivalent_field "campaignCreatedAt", locally_named: "createdAt"
    end

    t.field "launchDate", "Date" do |f|
      f.sourced_from "launchPlan", "launchDate"
    end

    t.index "campaigns"do |i|
      i.rollover :yearly, "createdAt"
    end
  end

  schema.object_type "CampaignLaunchPlan" do |t|
    t.field "id", "ID"
    t.field "campaignId", "ID"
    t.field "campaignCreatedAt", "DateTime"
    t.field "launchDate", "Date"

    t.index "campaign_launch_plans"
  end
end

Parameters:

  • path (String)

    path to a routing or rollover field on the related type

  • locally_named (String) (defaults to: path)

    path on the local type to the equivalent field



125
126
127
128
129
130
131
132
# File 'lib/elastic_graph/schema_definition/schema_elements/relationship.rb', line 125

def equivalent_field(path, locally_named: path)
  if @equivalent_field_paths_by_local_path.key?(locally_named)
    raise SchemaError, "`equivalent_field` has been called multiple times on `#{parent_type.name}.#{name}` with the same " \
      "`locally_named` value (#{locally_named.inspect}), but each local field can have only one `equivalent_field`."
  else
    @equivalent_field_paths_by_local_path[locally_named] = path
  end
end

#many?Boolean

Returns:

  • (Boolean)


188
189
190
# File 'lib/elastic_graph/schema_definition/schema_elements/relationship.rb', line 188

def many?
  @cardinality == :many
end

#rollover_timestamp_value_source_for_index(index) ⇒ Object

Gets the ‘rollover_timestamp_value_source` from this relationship for the given `index`, based on the configured equivalent fields and the rollover configuration used by `index`.

Returns the GraphQL field name (not the ‘name_in_index`).



154
155
156
157
158
159
160
# File 'lib/elastic_graph/schema_definition/schema_elements/relationship.rb', line 154

def rollover_timestamp_value_source_for_index(index)
  return nil unless (rollover_config = index.rollover_config)

  @equivalent_field_paths_by_local_path.fetch(rollover_config.timestamp_field_path.path) do |local_need|
    yield local_need
  end
end

#routing_value_source_for_index(index) ⇒ Object

Gets the ‘routing_value_source` from this relationship for the given `index`, based on the configured routing used by `index` and the configured equivalent fields.

Returns the GraphQL field name (not the ‘name_in_index`).



140
141
142
143
144
145
146
# File 'lib/elastic_graph/schema_definition/schema_elements/relationship.rb', line 140

def routing_value_source_for_index(index)
  return nil unless index.uses_custom_routing?

  @equivalent_field_paths_by_local_path.fetch(index.routing_field_path.path) do |local_need|
    yield local_need
  end
end

#runtime_metadataObject



193
194
195
196
197
198
199
# File 'lib/elastic_graph/schema_definition/schema_elements/relationship.rb', line 193

def 
  field_path_resolver = SchemaElements::FieldPath::Resolver.new
  resolved_related_type = (_ = related_type.unwrap_list.as_object_type) # : indexableType
  foreign_key_nested_paths = field_path_resolver.determine_nested_paths(resolved_related_type, @foreign_key)
  foreign_key_nested_paths ||= [] # : ::Array[::String]
  SchemaArtifacts::::Relation.new(foreign_key: @foreign_key, direction: @direction, additional_filter: @additional_filter, foreign_key_nested_paths: foreign_key_nested_paths)
end

#validate_equivalent_fields(field_path_resolver) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/elastic_graph/schema_definition/schema_elements/relationship.rb', line 163

def validate_equivalent_fields(field_path_resolver)
  resolved_related_type = (_ = related_type.as_object_type) # : indexableType

  @equivalent_field_paths_by_local_path.flat_map do |local_path_string, related_type_path_string|
    errors = [] # : ::Array[::String]

    local_path = resolve_and_validate_field_path(parent_type, local_path_string, field_path_resolver) do |error|
      errors << error
    end

    related_type_path = resolve_and_validate_field_path(resolved_related_type, related_type_path_string, field_path_resolver) do |error|
      errors << error
    end

    if local_path && related_type_path && local_path.type.unwrap_non_null != related_type_path.type.unwrap_non_null
      errors << "Field `#{related_type_path.full_description}` is defined as an equivalent of " \
        "`#{local_path.full_description}` via an `equivalent_field` definition on `#{parent_type.name}.#{name}`, " \
        "but their types do not agree. To continue, change one or the other so that they agree."
    end

    errors
  end
end