# `GeoGenius.Pipeline.Relate`
[🔗](https://github.com/agoodway/geo_genius/blob/main/lib/geo_genius/pipeline/relate.ex#L1)

The import phase that rebuilds measured relations and writes every edge a
provider asserts.

The two paths compose rather than one replacing the other: a provider
whose areas nest spatially or by code returns `:rebuild` from
`relations/1`, and a provider whose source carries a hierarchy no overlap
test can derive -- a parent column on every row, or a source with no
geometry at all -- returns edges from `asserted_relations/2`. A provider
can do both, and both land in the same release.

`rebuild_relations/1` runs first, so a rebuilt measured relation is on the
release before any asserted edge is written. If a provider asserts an edge
for a pair the rebuild also measured, the assertion wins: it upserts onto
the same row, nulling the measured overlap columns and overwriting the
geometry-classified `relation_type` with no warning that a measurement was
replaced. A well-behaved provider keeps its asserted edges disjoint from
the pairs its own geometry would measure.

Asserted edges are written after normalization, so an edge may name any
area the run produced rather than only the ones its own row emitted --
`GeoGenius.Catalog.put_relation_many/4` requires both areas to already be
members of the release, which every area `GeoGenius.Pipeline.Normalize`
wrote already is by the time this phase runs.

Staging is read in pages, and each page is one heartbeat, the same as
`GeoGenius.Pipeline.Normalize.normalize/1`: the import this exists to
serve stages tens of thousands of rows, and before asserted edges existed
this phase was a single SQL call under `:timeout` -- streaming rows with no
heartbeat between them would let a long asserting pass outrun the run's
lease with nothing renewing it.

A provider's own defect -- an edge naming an area absent from the release,
an unknown `relation_type` -- raises out of `Catalog.put_relation_many/4` and is
left to propagate. It is not rescued here: `GeoGenius.Pipeline` catches
whatever a phase raises and records it as `{:exception, exception,
stacktrace}`, the same structured detail every other phase's provider call
is recorded with. Catching it again here first would only discard the
exception's type and stacktrace in favor of a flatter string.

# `relate`

```elixir
@spec relate(GeoGenius.Pipeline.State.t()) :: GeoGenius.Pipeline.State.result()
```

Rebuilds measured relations, writes every edge the release's providers
assert, and merges both into one metrics map.

Measures `"relations"` (present only when at least one provider's
`relations/1` returns `:rebuild`) and `"asserted_relations"`, which counts
edges asserted rather
than distinct edges written: two rows asserting the same edge each add
one, even though `put_relation_many/4` deduplicates and the edge itself
converges to a single row.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
