Skip to content

Add parent_relationship API and RelationshipChainResolver with validation#1238

Merged
myronmarston merged 8 commits into
block:mainfrom
ellisandrews-toast:parent-relationship-api-and-validations
Jun 8, 2026
Merged

Add parent_relationship API and RelationshipChainResolver with validation#1238
myronmarston merged 8 commits into
block:mainfrom
ellisandrews-toast:parent-relationship-api-and-validations

Conversation

@ellisandrews-toast

Copy link
Copy Markdown
Collaborator

Summary

ElasticGraph's sourced_from feature allows fields on an indexed type to be populated from events published by a different source type. Today, this only works for top-level indexed types.

This PR is an incremental step toward supporting sourced_from on embedded (non-indexed) types — types that live nested inside a root document.

To enable this, we introduce a parent_relationship API that lets users declare how an embedded type connects back up to the root indexed type.

This PR adds the API and validates the chain of parent_relationship declarations — producing clear error messages for misconfigured schemas. Valid configurations are artifact no-ops for now; later PRs will use the validated chain to produce runtime metadata that tells the indexer how to navigate into nested documents and update the correct fields.

Example API Usage

ElasticGraph.define_schema do |schema|
  schema.object_type "Team" do |t|
    t.field "id", "ID!"
    t.field "name", "String"
    t.field "players", "[Player!]!" do |f|
      f.mapping type: "nested"
    end
    t.relates_to_many "statLines", "StatLine", via: "teamId", dir: :in, indexing_only: true
    t.index "teams" do |i|
      i.has_had_multiple_sources!
    end
  end

  schema.object_type "Player" do |t|
    t.field "id", "ID!"
    t.field "name", "String"
    t.field "goalsScored", "Int" do |f|
      f.sourced_from "statLine", "goals"
    end
    t.relates_to_one "statLine", "StatLine", via: "playerId", dir: :in, indexing_only: true do |r|
      r.parent_relationship "Team", "statLines" # <--- NEW!
    end
  end

  schema.object_type "StatLine" do |t|
    t.field "id", "ID!"
    t.field "teamId", "ID"
    t.field "playerId", "ID"
    t.field "goals", "Int"
    t.index "stat_lines"
  end
end

Validations

  • Relationship must be indexing_only: true
  • No circular parent chains
  • Parent type must exist
  • Parent relationship must exist on parent type
  • Source types must agree across the chain
  • Chain must terminate at an indexed type
  • Embedding field must exist on parent type (auto-discovered or explicit via parent_field_name:)
  • No ambiguous embedding (multiple fields of the same type without parent_field_name:)

@myronmarston myronmarston left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not done reviewing but I wanted to submit my feedback so far.

@myronmarston myronmarston left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM--just one minor suggestion!

@myronmarston myronmarston enabled auto-merge (squash) June 8, 2026 15:33
@myronmarston myronmarston merged commit 6814370 into block:main Jun 8, 2026
32 of 34 checks passed
@ellisandrews-toast ellisandrews-toast deleted the parent-relationship-api-and-validations branch June 8, 2026 16:45
myronmarston pushed a commit that referenced this pull request Jun 8, 2026
## What

Removes the `match_field` attribute from `ListPathSegment` in the
runtime metadata, and switches the list-vs-object segment discriminator
in `SourcedFromNestedPathSegment.from_hash` from `match_field` to
`source_field`.

## Why

`match_field` was always `"id"` — ElasticGraph relationships join on
`id` via foreign keys, so the value carried no information. Serializing
a constant into runtime metadata adds noise without value, and keeping
it would have meant emitting `match_field: "id"` into
`runtime_metadata.yaml` once nested sourced paths are actually
populated.

This mirrors the equivalent cleanup already applied to the
schema-definition `PathSegment` in #1238. The match is now implicit:
list elements are matched on `id` against the value at `source_field`.
If non-`id` primary keys are supported in the future, the field can be
reintroduced then.

## Scope / risk

Low. `ListPathSegment` is not yet constructed by any production code —
only by the round-trip serialization unit test — so there is no
schema-artifact churn and nothing downstream consumes the removed field.
Specs and type checks pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants