Class JoinObserver
EdgeOrigin.LEARNED schema-graph edges from the
joins a user actually writes.
Tables often start with no declared foreign keys, but the joins users
write surface the relationships anyway. This observer walks query and view
trees and captures each cross-side column equality as a candidate
relationship the graph does not yet know. The evidence is a property of the
join condition, not of the join operator — so a single
case ConditionalJoinNode arm covers all seven uniform joins, and a
σ o.cid = c.cid (Orders × Customers) yields the very same edge as the
equivalent θ-join. The observation points are:
- every
ConditionalJoinNode(θ, the three outer joins, semi, anti, pairwise-∀) — equality conjuncts of its condition split across the two inputs; NaturalJoinNodeandCompositionNode— their matched columns (the name-intersection of the two input schemas);AsOfJoinNode— the equality conjuncts of its condition (its partition keys); the single ordering inequality is never an equijoin and is skipped;- a
SelectionNodeover aProductNode— the equality conjuncts of the σ predicate split across the ×'s two inputs, the same assertion a θ-join makes written differently.
A bare × carries no evidence, and non-equi conditions (band joins,
interval overlap) are not representable as positional equijoin endpoints
— both are ignored.
Endpoint resolution. Both sides of a candidate must bottom out in
something carrying a RelationSymbol: a RelationNode or a
relation-only RenameNode over one (whose alias is how the join
condition qualifies its columns — this is what makes a self-join observable).
A join over a filtered, projected, or aggregated intermediate is unobservable
until derived endpoints participate in learning, so
such a side yields nothing here.
Bounds. Join type sets the bounds a learned edge carries, never
whether it is captured. An outer join is the direct min = 0 signal on
its null-supplying side, which coincides with the unconstrained
[0..*] default every other join leaves in place — a join alone never
bounds fan-out (max) or guarantees a match (min ≥ 1). So every
learned edge is [0..*] on both endpoints; the outer-join distinction
is real but structurally the default, and is not fabricated into a bound the
evidence does not support (examination against candidate keys, item 5, is what
sharpens it).
De-noising. A candidate that the graph already carries between the same relations on the same columns is dropped regardless of name — a learned edge must not shadow or duplicate a declared one. Candidates observed repeatedly (the same join written across many queries) collapse to one. The caller is responsible for supplying only the trees of successful analyses; nothing is learned from a query that failed to resolve.
This class is a pure producer: it reads trees and a graph and returns candidate edges. It never mutates the graph, prompts, or persists — recording policy (confirm-or-record, session persistence) is the REPL's, shared with the conversational acquisition path (item 6).
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic List<Relationship> observe(SemanticModel model) Observes learned edges across every user-written tree of a semantic model — the inline expression of each root query plus every view body — de-noised against the model's own graph.observeTrees(Collection<RelNode> trees) Observes learned edges across the given trees, de-noised against this observer's existing graph and against one another.
-
Constructor Details
-
JoinObserver
- Parameters:
symbols- the symbol table trees resolve against; never nullexisting- the graph to de-noise against — a candidate it already carries is not re-proposed; never null (useSchemaGraph.EMPTY)
-
-
Method Details
-
observe
Observes learned edges across every user-written tree of a semantic model — the inline expression of each root query plus every view body — de-noised against the model's own graph.- Parameters:
model- an analysed model; only meaningful for a valid analysis, since nothing worth learning comes from a query that failed to resolve- Returns:
- the distinct candidate edges, in first-seen order; never null
-
observeTrees
Observes learned edges across the given trees, de-noised against this observer's existing graph and against one another.- Parameters:
trees- the query/view trees to scan; never null- Returns:
- the distinct candidate edges, in first-seen order; never null
-