Skip to content

docs(schema): correct from+to relationship cardinality to many-to-one#602

Open
kriszyp wants to merge 2 commits into
mainfrom
docs/relationship-from-to-cardinality
Open

docs(schema): correct from+to relationship cardinality to many-to-one#602
kriszyp wants to merge 2 commits into
mainfrom
docs/relationship-from-to-cardinality

Conversation

@kriszyp

@kriszyp kriszyp commented Jul 22, 2026

Copy link
Copy Markdown
Member

What

Fixes the second bug from #582: the @relationship(from: attribute, to: attribute) example in reference/database/schema.md joins two scalar fields (productSku -> sku) but was described as "useful for many-to-many relationships." Reworded the section to state that cardinality follows the to attribute (scalar -> many-to-one, array -> many-to-many via the array pattern already shown in the from-only section above), and relabeled the example's inline comment as many-to-one.

Why

Per #582: Harper's search.ts decides many-to-many purely by whether the target to attribute is an array (isManyToMany = Boolean(findAttribute(relatedTable.attributes, attribute.relationship.to)?.elements)). A scalar-to-scalar from+to join can never be many-to-many, so the old text mislabeled the example and could mislead readers (and downstream agent-rules generation in HarperFast/skills) into modeling it wrong.

The issue's first finding (reference/rest/querying.md pointing from at singular resellerId instead of resellerIds) was already fixed on main by 7e373fa prior to this dispatch — no change needed here.

Test plan

  • npx prettier --check reference/database/schema.md reference/rest/querying.md — passes
  • Verified against ~/dev/harper's search.ts isManyToMany logic per the issue's citation

Refs #582

🤖 Generated with Claude Code

The @relationship(from:, to:) example joined two scalar attributes
(productSku -> sku) but was labeled "useful for many-to-many
relationships" — cardinality in Harper is decided by whether the
target `to` attribute is an array (search.ts's isManyToMany check),
so a scalar-to-scalar join is many-to-one, not many-to-many.

The other bug in issue #582 (querying.md resellers example pointing
`from` at the singular resellerId) was already fixed on main by
7e373fa before this dispatch ran.

Refs #582

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request updates the documentation in reference/database/schema.md to clarify the cardinality rules for foreign key to foreign key relationships using @relationship(from: attribute, to: attribute). The review feedback correctly identifies an inaccuracy in the explanation of how cardinality is determined and provides a clearer, simplified phrasing to resolve the issue.

Comment thread reference/database/schema.md Outdated
@github-actions
github-actions Bot temporarily deployed to pr-602 July 22, 2026 21:33 Inactive
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-602

This preview will update automatically when you push new commits.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@kriszyp
kriszyp marked this pull request as ready for review July 22, 2026 22:37
@kriszyp
kriszyp requested a review from a team as a code owner July 22, 2026 22:37
@github-actions

Copy link
Copy Markdown

🚀 Preview Deployment

Your preview deployment is ready!

🔗 Preview URL: https://preview.harper-documentation.harperfabric.com/pr-602

This preview will update automatically when you push new commits.

orderId: Long @indexed
productSku: Long @indexed
product: Product @relationship(from: productSku, to: sku) # join on sku, not primary key
product: Product @relationship(from: productSku, to: sku) # many-to-one join on sku, not primary key

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

High: the corrected example does not actually work in Harper

Relabeling the join as many-to-one is directionally right for the intended cardinality, but the schema as written doesn't resolve at all on current main (513f87471), regardless of label. Table.ts's attribute-wiring (updatedAttributes(), around resources/Table.ts:4414-4450) is if (relationship.to) { if (attribute.elements?.definition) {...} } else if (relationship.from) {...}. Since to: sku is set, it takes the first branch unconditionally — and because product: Product is a scalar field (no attribute.elements), that branch's else fires: console.error('...must have an array type referencing a table as the elements'), and attribute.resolve is left null. The else if (relationship.from) branch — the one that actually knows how to resolve a single scalar relationship — is never reached when to is also set.

I verified this empirically against a live instance built from origin/main with this exact schema: GET /OrderItem/:id (no select) silently omits product from the response entirely (not even null), and GET /OrderItem/:id?select(...,product{...}) (the standard way to expand a relationship, as used elsewhere in these docs/tests) 500s with TypeError: Cannot read properties of undefined (reading 'propertyResolvers') in Resource.ts (transformForSelect), because it dereferences attribute.resolve.definition on a null resolve.

The combined from+to resolver path only registers when the field type is an array (attribute.elements?.definition) — there is no code path that resolves a scalar foreign-key-to-foreign-key relationship by a non-primary-key to attribute. So the fix that actually makes this example work is products: [Product] @relationship(from: productSku, to: sku) (array field), not a comment/prose relabel — and once it's array-typed, it's structurally a one-to-many/many-to-many resolver in the code regardless of how unique sku is in practice.

This is a pre-existing bug in the example (not introduced by this PR), but the PR's stated purpose is specifically to make this example accurate, and its test plan only checked search.ts's isManyToMany (the query-filter cardinality calc), not Table.ts's resolver wiring (the code path that determines whether the field resolves at all). Worth fixing before merge so the docs don't keep shipping a schema that 500s when a reader follows it.

Suggested fix: change the field to an array type (products: [Product] @relationship(from: productSku, to: sku)) and update the surrounding prose (line 400) accordingly, or drop the to argument and this section's non-primary-key framing until Harper actually supports scalar-target foreign-key-to-foreign-key resolution.


Generated by Barber AI

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This issue sounds legit.

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