Skip to content

feat(analytics): multi-hop relationship joins for datasets (ADR-0071)#2296

Merged
os-zhuang merged 1 commit into
mainfrom
feat/dataset-multihop-joins
Jun 24, 2026
Merged

feat(analytics): multi-hop relationship joins for datasets (ADR-0071)#2296
os-zhuang merged 1 commit into
mainfrom
feat/dataset-multihop-joins

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Summary

Implements the remaining open gap from ADR-0071multi-hop joins (matrix/across was already shipped). A dataset can now group or aggregate by a field two or three to-one relationships away:

opportunity → account → owner → region    (dimension field: account.owner.region)

Design (per the ADR)

  • To-one onlyinclude holds lookup / master_detail (child→parent) relationships, which never fan out. So SUM/COUNT stay correct with zero symmetric-aggregate machinery. To-many traversal is explicitly out of scope.
  • Dotted paths, dot-free aliases — authors write account.owner.region (dotted, self-disambiguating). The SQL alias is account__owner (dots → __, the Cube.js convention) because the read-scope SQL guard fail-closed-rejects dotted identifiers. Single-segment paths are unchanged ⇒ single-hop joins are byte-for-byte identical.
  • Per-hop RLS for free — the strategy's tenant/read-scope loop already iterates every registered join alias, so each hop's object is scoped without touching the security path. (A new test asserts base + both hops are scoped.)
  • Depth cap 3 hops (4 objects, Salesforce-report-type parity) — a perf guard, not a correctness one; rejected at parse (spec refine) and compile.
  • D-C preserved — undeclared paths rejected; declaring a.b auto-includes intermediate a.

Touch points

File Change
spec/src/ui/dataset.zod.ts include paths + ≤3-hop refine + field docs
service-analytics/dataset-compiler.ts RelationshipResolver → {object,table}; chain-resolve into per-prefix joins; joinAlias; allowlist = prefixes
service-analytics/native-sql-strategy.ts qualifyAndRegisterJoin chain; resolveStorageTarget multi-hop

Tests

  • 27 new/updated unit tests (compiler chain + allowlist + auto-intermediate + depth cap + back-compat string resolver; strategy chained LEFT JOINs + per-hop RLS + prefix-allowlist enforcement).
  • Full analytics suite 164/164 green; spec dataset tests 10/10; spec check:api-surface unchanged.

Review notes

Touches the analytics SQL builder + RLS scoping, so I'm opening this for review rather than auto-merging. The follow-up (ADR P2 — objectui multi-hop field picker / include-path editor) is not in this PR.

🤖 Generated with Claude Code

Datasets can now group/aggregate by a field two or three to-one relationships
away (`account.owner.region`), not just one. Implements ADR-0071's remaining
gap (matrix/across was already shipped).

- spec: `include` accepts dotted relationship PATHS (≤3 hops, refined at parse);
  dimension/measure `field` may be a multi-hop path. To-many is out of scope.
- compiler: resolve each path hop-by-hop into one `cube.join` per prefix; the
  join alias is the path with dots → `__` (a single valid identifier — quoted
  dotted identifiers are rejected by the read-scope SQL guard). `RelationshipResolver`
  may now return `{ object, table }` to chain (string return stays back-compat).
  Declaring `a.b` auto-adds the intermediate `a`. allowlist = every prefix alias.
- strategy: `qualifyAndRegisterJoin` registers the chain (parent→child) and the
  deepest alias qualifies the column; `resolveStorageTarget` resolves the owning
  object at the full path. Per-hop RLS comes for free (the scope loop iterates
  every join alias).
- to-one only ⇒ no fan-out ⇒ aggregates correct with no symmetric aggregates.
- single-hop unchanged (alias no-op); 27 new/updated tests; 164 analytics tests
  green; spec api-surface unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 24, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
spec Ready Ready Preview, Comment Jun 24, 2026 3:49pm

Request Review

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling size/m labels Jun 24, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 2 package(s): packages/services, @objectstack/spec.

93 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/concepts/architecture.mdx (via @objectstack/spec)
  • content/docs/concepts/cloud-artifact-api.mdx (via packages/spec)
  • content/docs/concepts/cluster-semantics.mdx (via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx (via packages/spec)
  • content/docs/concepts/implementation-status.mdx (via @objectstack/spec)
  • content/docs/concepts/index.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx (via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx (via packages/spec)
  • content/docs/concepts/north-star.mdx (via packages/spec)
  • content/docs/concepts/packages.mdx (via @objectstack/spec)
  • content/docs/concepts/setup-app.mdx (via @objectstack/spec)
  • content/docs/concepts/skills.mdx (via @objectstack/spec)
  • content/docs/concepts/webhook-delivery.mdx (via @objectstack/spec)
  • content/docs/getting-started/architecture.mdx (via @objectstack/spec)
  • content/docs/getting-started/cli.mdx (via @objectstack/spec)
  • content/docs/getting-started/core-concepts.mdx (via @objectstack/spec)
  • content/docs/getting-started/examples.mdx (via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx (via @objectstack/spec)
  • content/docs/guides/adding-a-metadata-type.mdx (via @objectstack/spec)
  • content/docs/guides/ai-capabilities.mdx (via @objectstack/spec)
  • content/docs/guides/airtable-dashboard-analysis.mdx (via @objectstack/spec)
  • content/docs/guides/analytics-datasets.mdx (via @objectstack/spec)
  • content/docs/guides/api-reference.mdx (via @objectstack/spec)
  • content/docs/guides/business-logic.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/backward-compatibility.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/error-catalog.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-type-gallery.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/field-validation-rules.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/permissions-matrix.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/protocol-diagram.mdx (via packages/spec)
  • content/docs/guides/cheatsheets/query-cheat-sheet.mdx (via @objectstack/spec)
  • content/docs/guides/cheatsheets/quick-reference.mdx (via @objectstack/spec)
  • content/docs/guides/client-sdk.mdx (via @objectstack/spec)
  • content/docs/guides/common-patterns.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/auth-service.mdx (via packages/spec)
  • content/docs/guides/contracts/cache-service.mdx (via packages/spec)
  • content/docs/guides/contracts/data-engine.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/index.mdx (via @objectstack/spec)
  • content/docs/guides/contracts/metadata-service.mdx (via packages/spec)
  • content/docs/guides/contracts/storage-service.mdx (via packages/spec)
  • content/docs/guides/data-modeling.mdx (via @objectstack/spec)
  • content/docs/guides/deployment-vercel.mdx (via @objectstack/spec)
  • content/docs/guides/driver-configuration.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-client.mdx (via @objectstack/spec)
  • content/docs/guides/error-handling-server.mdx (via @objectstack/spec)
  • content/docs/guides/external-datasources.mdx (via @objectstack/spec)
  • content/docs/guides/formula.mdx (via @objectstack/spec)
  • content/docs/guides/hook-bodies.mdx (via packages/spec)
  • content/docs/guides/kernel-services.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/dashboard.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/field.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/flow.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/index.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/object.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/validation.mdx (via @objectstack/spec)
  • content/docs/guides/metadata/workflow.mdx (via @objectstack/spec)
  • content/docs/guides/packages.mdx (via packages/services, @objectstack/spec)
  • content/docs/guides/plugin-development.mdx (via @objectstack/spec)
  • content/docs/guides/plugins.mdx (via @objectstack/spec)
  • content/docs/guides/project-scoping.mdx (via @objectstack/spec)
  • content/docs/guides/public-forms.mdx (via @objectstack/spec)
  • content/docs/guides/runtime-services/audit-service.mdx (via packages/services)
  • content/docs/guides/runtime-services/email-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/index.mdx (via packages/services, packages/spec)
  • content/docs/guides/runtime-services/queue-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/settings-service.mdx (via packages/services)
  • content/docs/guides/runtime-services/sharing-service.mdx (via packages/spec)
  • content/docs/guides/runtime-services/storage-service.mdx (via packages/spec)
  • content/docs/guides/security.mdx (via @objectstack/spec)
  • content/docs/guides/seed-data.mdx (via @objectstack/spec)
  • content/docs/guides/skills.mdx (via @objectstack/spec)
  • content/docs/guides/standards.mdx (via @objectstack/spec)
  • content/docs/guides/troubleshooting.mdx (via @objectstack/spec)
  • content/docs/guides/validating-metadata.mdx (via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/config-resolution.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/i18n-standard.mdx (via packages/services, @objectstack/spec)
  • content/docs/protocol/objectos/lifecycle.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/plugin-spec.mdx (via @objectstack/spec)
  • content/docs/protocol/objectos/runtime-capabilities.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx (via packages/spec)
  • content/docs/protocol/objectql/query-syntax.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx (via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx (via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx (via packages/spec)
  • content/docs/protocol/objectui/record-alert.mdx (via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx (via @objectstack/spec)
  • content/docs/releases/index.mdx (via @objectstack/spec)
  • content/docs/releases/v9.mdx (via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@os-zhuang
os-zhuang merged commit 5eef4cf into main Jun 24, 2026
16 of 17 checks passed
@os-zhuang
os-zhuang deleted the feat/dataset-multihop-joins branch June 24, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/m tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant