For the complete documentation index, see llms.txt. This page is also available as Markdown.

Relationships

Entity-to-entity relationships in the catalog — ERD edges from foreign keys and graph edges between graph-store nodes.

ODD Platform tracks entity-to-entity relationships as first-class catalog objects. They show up two ways: as ERD diagrams between table-class entities (foreign-key-style edges) and as graph relationships between graph-store entities (Neo4j-style edges). Both are surfaced under Data Modelling → Relationships in the UI and through the /api/relationships API.

The two relationship classes

The platform's internal model defines a DATA_RELATIONSHIP(9) data-entity class that contains two types:

Internal type
API filter value
What it represents

ENTITY_RELATIONSHIP(25)

ERD

Foreign-key-style edges between two table-class entities. Cross-schema references are supported. The collector that catalogs the source emits one ENTITY_RELATIONSHIP per detected foreign-key constraint.

GRAPH_RELATIONSHIP(26)

GRAPH

Free-form graph edges between graph-store entities (e.g., relationships between nodes in a Neo4j database). Distinct from ERD because the cardinality model is graph-native, not relational.

A relationship is a regular catalog entity — it has an ODDRN, an owner, a namespace inherited from its source / target, and shows up in search alongside datasets. The dedicated UI surface and /api/relationships API are for list and detail navigation of just the relationship-class entities.

ERD cardinality model (ENTITY_RELATIONSHIP)

ERD relationships carry the source-and-target table pair plus a cardinality classifier. Four classifiers are defined upstream:

  • ONE_TO_EXACTLY_ONE — one source row maps to exactly one target row.

  • ONE_TO_ZERO_OR_ONE — one source row maps to zero or one target row.

  • ONE_TO_ONE_OR_MORE — one source row maps to one or more target rows.

  • ONE_TO_ZERO_ONE_OR_MORE — one source row maps to zero, one, or many target rows.

These mirror the cardinality vocabulary defined in the odd-collectors monorepo's Relationships section.

UI walkthrough

Open Data Modelling → Relationships from the top-level navigation (/data-modelling/relationships). The list page shows every visible relationship across all data sources — as of 0.28.0 it applies the catalog's default visibility rules, so soft-deleted, hollow, and excluded-from-search relationship entities are filtered out (earlier releases listed them here even after every other catalog surface hid them). The page shows:

  • A table with columns Name, Type (ERD or GRAPH), Namespace + Datasource, Source entity, Target entity.

  • A type tab strip filtering by ALL / ERD / GRAPH (matches the API enum).

  • A search input filtering by name across the visible set.

  • Infinite-scroll pagination — page size is 30 by default.

Click a row to open the relationship's detail page; the platform routes to the /api/relationships/erd/{id} or /api/relationships/graph/{id} payload depending on the relationship type.

Relationships list page — eight ENTITY_RELATIONSHIP rows from a Snowflake sample-data source, each with its name, type (ENTITY RELATIONSHIP), namespace + datasource, source entity, and target entity. The All / ERD / Graph type-filter strip sits above the table; the right-rail switches between Query Examples and Relationships, the two Data Modelling sub-surfaces.

The same data is also surfaced per-entity — every dataset's detail page has a Relationships tab (in the same tab strip as Overview / Structure / Lineage / Test reports / Alerts / Query examples / Activity / Discussions) that lists only the relationships in which the current entity participates as Parent or Child:

Per-entity Relationships tab on the ORDERS table (DS / TABLE) — two relationships: (1) ORDERS_references_CUSTOMER with CUSTOMER as Parent and ORDERS as Child, cardinality ONE TO ZERO ONE OR MORE, Is Identifying False; (2) LINEITEM_references_ORDERS with ORDERS as Parent and LINEITEM as Child, cardinality ONE TO ZERO ONE OR MORE, Is Identifying True. Each row carries an inline ERD-style cardinality glyph (crow's-foot notation) between the Parent and Child columns.

The per-entity view is the operator-relevant perspective: when reading a table's documentation, you see only its incoming and outgoing relationships, not the whole catalog's. The Parent / Child columns and the cardinality glyph match the underlying ENTITY_RELATIONSHIP model and the same /api/relationships/erd/{id} payload surfaces both in this tab and on the global Data Modelling → Relationships list above. As of 0.28.0 this tab hides soft-deleted and hollow relationship entities, but — unlike the global list — it deliberately keeps exclude_from_search-flagged ones visible: that flag scopes discovery surfaces, and the dataset's own detail tab would be silently incomplete without its real relationships.

API surface

The three endpoints exposed by RelationshipController are documented at API Reference → Relationships — GET /api/relationships (paginated list with optional type=ERD|GRAPH|ALL filter and free-text query) plus the two per-type detail endpoints. The same payloads also surface on the source and target dataset's detail pages under the dataset's relationships cluster.

How relationships get into the catalog

Relationships are populated by the ingesting collector during the source-side metadata pull. The platform does not infer relationships from naming conventions or column-name similarity — only what the collector emits is surfaced.

Adapter coverage as of the time of writing (per the upstream odd-collectors Relationships matrix):

Collector
Adapter
Relationship type
How it's derived

odd-collector

postgresql

ENTITY_RELATIONSHIP (ERD)

Foreign-key constraints in the database catalog. Cross-schema FKs are detected.

odd-collector

snowflake

ENTITY_RELATIONSHIP (ERD)

Foreign-key constraints in the database catalog. Cross-schema FKs are detected.

No other adapter currently emits relationships. The GRAPH_RELATIONSHIP type is reserved in the platform model for graph-store sources (e.g., Neo4j edges), but no adapter in the published odd-collectors Relationships matrix currently surfaces graph relationships at the time of writing — operators using a Neo4j adapter today get nodes catalogued without their inter-node edges. This is an upstream collector-side gap; if your deployment relies on graph relationships, follow the upstream repo for adapter changes.

Known operator caveats

Four behaviours of the Relationships surface are non-obvious from the UI alone — they are model contracts to know about, not defects. Two further code-side defects from earlier releases (the Target-column rendering and the unvalidated ?type= parameter) were fixed in 0.28.0; the notes below describe them for operators on 0.27.x and earlier.

Fixed in 0.28.0 — the Target column previously showed the source entity. On 0.27.x and earlier, the list-row renderer passed the source entity's data to both the Source and the Target cells, so every row displayed the same dataset twice and a discovery workflow of the form "find every relationship where ORDERS is the target" was impossible from this page. As of 0.28.0 the Target column renders the actual target entity. On affected releases, use the per-entity Relationships tab (which was never affected) for target-side questions.

Fixed in 0.28.0 — a mistyped ?type= URL parameter previously rendered a dead, blank view. On 0.27.x and earlier, the page sent the raw value to the backend, which rejected it with an opaque HTTP 400 from enum-binding; the tab strip showed no active tab and the list read "0 relationships overall" with no error message — indistinguishable from an empty catalog. As of 0.28.0 an unknown value (?type=erd lower-case, ?type=Erd mixed-case, anything outside ALL / ERD / GRAPH) falls back to the ALL view with the All tab active. The accepted tokens are still exactly ALL / ERD / GRAPH (case-sensitive) — deep links using other spellings load the unfiltered list, not the intended tab.

The relationship_id path parameter on the API is the relationship's data-entity id, not the relationships table primary key. The platform models a relationship as a regular catalog entity (entity-class id 9, DATA_RELATIONSHIP). The API parameter named relationship_id is the corresponding data_entity.id; the underlying relationships.id row is internal and is not the value the API consumes.

The same trap exists inside the API payload itself: the details response exposes erd_relationship.erd_relationship_id and graph_relationship.graph_relationship_id — internal detail-record ids from two further tables. Feeding either back into /api/relationships/{type}/{id} returns a 404 (or, on a numeric coincidence with another relationship's data-entity id, the payload of an unrelated relationship). The only value that round-trips is the relationship's own id field from the list or details payload; the UI always uses it and is unaffected. As of 0.28.0 the OpenAPI spec states this on the relationship_id parameter and on both *_relationship_id fields.

Where to next

  • Data Modelling overview — parent section; pairs Relationships with Query Examples.

  • Query Examples — the other Data Modelling sub-surface.

  • Integrations → odd-collector — the generic collector that hosts the PostgreSQL and Snowflake adapters surfacing ERD relationships today; the per-adapter feature matrix on that page calls out ERD support explicitly.

  • Main Concepts — for the broader catalog vocabulary that names these entities.

Last updated