feat(demo): add SQL DSL escape-hatch 'cross-author similarity' query#369
feat(demo): add SQL DSL escape-hatch 'cross-author similarity' query#369
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
📝 WalkthroughWalkthroughAdds a new CLI command and SQL-DSL function to compute cross-author post similarity via a self-join using cosine distance, plus an integration test that seeds Postgres data and verifies ordering, filtering, and limit behavior. Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as CLI
participant Runtime as Runtime
participant DB as Database
CLI->>Runtime: invoke crossAuthorSimilarity(limit)
Runtime->>DB: execute generated SQL (self-join p1/p2, cosineDistance)
DB-->>Runtime: return rows (p1, p2, distance)
Runtime-->>CLI: return JSON results
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
@prisma-next/mongo-runtime
@prisma-next/family-mongo
@prisma-next/sql-runtime
@prisma-next/family-sql
@prisma-next/middleware-telemetry
@prisma-next/mongo
@prisma-next/extension-paradedb
@prisma-next/extension-pgvector
@prisma-next/postgres
@prisma-next/sql-orm-client
@prisma-next/sqlite
@prisma-next/target-mongo
@prisma-next/adapter-mongo
@prisma-next/driver-mongo
@prisma-next/contract
@prisma-next/utils
@prisma-next/config
@prisma-next/errors
@prisma-next/framework-components
@prisma-next/operations
@prisma-next/ts-render
@prisma-next/contract-authoring
@prisma-next/ids
@prisma-next/psl-parser
@prisma-next/psl-printer
@prisma-next/cli
@prisma-next/emitter
@prisma-next/migration-tools
prisma-next
@prisma-next/vite-plugin-contract-emit
@prisma-next/runtime-executor
@prisma-next/mongo-codec
@prisma-next/mongo-contract
@prisma-next/mongo-value
@prisma-next/mongo-contract-psl
@prisma-next/mongo-contract-ts
@prisma-next/mongo-emitter
@prisma-next/mongo-schema-ir
@prisma-next/mongo-query-ast
@prisma-next/mongo-orm
@prisma-next/mongo-query-builder
@prisma-next/mongo-lowering
@prisma-next/mongo-wire
@prisma-next/sql-contract
@prisma-next/sql-errors
@prisma-next/sql-operations
@prisma-next/sql-schema-ir
@prisma-next/sql-contract-psl
@prisma-next/sql-contract-ts
@prisma-next/sql-contract-emitter
@prisma-next/sql-lane-query-builder
@prisma-next/sql-relational-core
@prisma-next/sql-builder
@prisma-next/target-postgres
@prisma-next/target-sqlite
@prisma-next/adapter-postgres
@prisma-next/adapter-sqlite
@prisma-next/driver-postgres
@prisma-next/driver-sqlite
commit: |
Introduces a standalone SQL DSL query that the ORM client cannot express, validating the SQL DSL as the escape hatch for power users (TML-2160). The 'cross-author similarity' query self-joins the Post table (aliased as p1 and p2) on a non-relation predicate (p1.userId != p2.userId), projects columns from both aliases side-by-side, and orders by pgvector cosineDistance computed over two column references. Why the ORM cannot express this: - Self-joins on non-relation predicates: the ORM's join surface is relation-shaped and can only follow declared relations. - Extension op with two column references: TML-2042's extension-op integration is column.method(boundValue) — no column-vs-column. - Projecting two rows of the same model as peers: the ORM has a single root model per query; sibling rows can't be flattened into one row. None of these are gated by an open ticket; they're fundamental to the ORM's relation-shaped query model. Additions: - src/queries/cross-author-similarity.ts — the escape-hatch query - test/sql-dsl.integration.test.ts — integration tests against Postgres proving correct, typed results (ordering, pair uniqueness, limit) - CLI: 'pnpm start -- cross-author-similarity [limit]'
ad93901 to
2feb195
Compare
…face Addresses review feedback (F01) on PR #369. The original wording claimed the ORM client fundamentally cannot express the cross-author similarity shape, but per ADR 164 the ORM client is a repository layer that can orchestrate multiple plans for one logical operation. What the code actually proves is narrower: this shape is not lowerable as a single ORM collection query because the current collection surface has no arbitrary self-join, no column-vs-column extension-op argument, and no flat peer-row projection. Updated: - src/queries/cross-author-similarity.ts: reframe as 'escape-hatch for a shape the current ORM collection surface does not lower directly', and add a note acknowledging client-side stitching as a multi-query alternative. - src/main.ts: align the CLI help text.
…face Addresses review feedback (F01) on PR #369. The original wording claimed the ORM client fundamentally cannot express the cross-author similarity shape, but per ADR 164 the ORM client is a repository layer that can orchestrate multiple plans for one logical operation. What the code actually proves is narrower: this shape is not lowerable as a single ORM collection query because the current collection surface has no arbitrary self-join, no column-vs-column extension-op argument, and no flat peer-row projection. Updated: - src/queries/cross-author-similarity.ts: reframe as 'escape-hatch for a shape the current ORM collection surface does not lower directly', and add a note acknowledging client-side stitching as a multi-query alternative. - src/main.ts: align the CLI help text.
5d89457 to
bf28fbe
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (2)
examples/prisma-next-demo/test/sql-dsl.integration.test.ts (2)
224-230: Consider using non-null assertion after length check.The guard
if (!row) throw new Error(...)afterexpect(limited).toHaveLength(1)is defensive. Since you've already asserted the array has exactly one element, you can use a non-null assertion for cleaner code.💡 Suggested simplification
expect(limited).toHaveLength(1); - const [row] = limited; - if (!row) throw new Error('expected one row'); + const row = limited[0]!; expect(row.postAUserId).not.toBe(row.postBUserId);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/prisma-next-demo/test/sql-dsl.integration.test.ts` around lines 224 - 230, Replace the defensive runtime guard after the length assertion with a non-null assertion: after expect(limited).toHaveLength(1), directly destructure the single element using const [row] = limited! (or const row = limited[0]!) instead of using if (!row) throw..., so update the usage sites (the variable row and subsequent calls to unorderedPairKey(row.postAId, row.postBId) and comparisons to seededPostIds.aliceFar / seededPostIds.bobClose) to rely on the non-null assertion.
28-28: Blind cast toSqlDriver<unknown>in test helper.The cast
as unknown as SqlDriver<unknown>bypasses type checking. Per coding guidelines, blind casts should be avoided in favor of type predicates or guards.However, this is in a test helper where the driver type is dynamically extracted from the execution stack, and the subsequent
if (!driver)check provides a runtime guard. The double cast is acceptable here per test mocking patterns guidance.💡 Optional: Add type predicate for cleaner extraction
function isSqlDriver(value: unknown): value is SqlDriver<unknown> { return value !== null && typeof value === 'object' && 'connect' in value; } // Then use: const driver = stackInstance.driver; if (!isSqlDriver(driver)) { throw new Error('Driver descriptor missing or invalid'); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@examples/prisma-next-demo/test/sql-dsl.integration.test.ts` at line 28, Replace the blind double-cast "as unknown as SqlDriver<unknown>" on stackInstance.driver with a runtime type guard: add an isSqlDriver(value: unknown): value is SqlDriver<unknown> predicate that checks structural properties (e.g., non-null object and expected driver methods/properties like "connect"), then retrieve const driver = stackInstance.driver; assert it with if (!isSqlDriver(driver)) throw new Error('Driver descriptor missing or invalid'); and use the narrowed driver thereafter (references: stackInstance.driver and SqlDriver).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@examples/prisma-next-demo/test/sql-dsl.integration.test.ts`:
- Around line 224-230: Replace the defensive runtime guard after the length
assertion with a non-null assertion: after expect(limited).toHaveLength(1),
directly destructure the single element using const [row] = limited! (or const
row = limited[0]!) instead of using if (!row) throw..., so update the usage
sites (the variable row and subsequent calls to unorderedPairKey(row.postAId,
row.postBId) and comparisons to seededPostIds.aliceFar / seededPostIds.bobClose)
to rely on the non-null assertion.
- Line 28: Replace the blind double-cast "as unknown as SqlDriver<unknown>" on
stackInstance.driver with a runtime type guard: add an isSqlDriver(value:
unknown): value is SqlDriver<unknown> predicate that checks structural
properties (e.g., non-null object and expected driver methods/properties like
"connect"), then retrieve const driver = stackInstance.driver; assert it with if
(!isSqlDriver(driver)) throw new Error('Driver descriptor missing or invalid');
and use the narrowed driver thereafter (references: stackInstance.driver and
SqlDriver).
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: c610072e-f821-453a-8ade-e5fd171bc3c2
📒 Files selected for processing (3)
examples/prisma-next-demo/src/main.tsexamples/prisma-next-demo/src/queries/cross-author-similarity.tsexamples/prisma-next-demo/test/sql-dsl.integration.test.ts
…face Addresses review feedback (F01) on PR #369. The original wording claimed the ORM client fundamentally cannot express the cross-author similarity shape, but per ADR 164 the ORM client is a repository layer that can orchestrate multiple plans for one logical operation. What the code actually proves is narrower: this shape is not lowerable as a single ORM collection query because the current collection surface has no arbitrary self-join, no column-vs-column extension-op argument, and no flat peer-row projection. Updated: - src/queries/cross-author-similarity.ts: reframe as 'escape-hatch for a shape the current ORM collection surface does not lower directly', and add a note acknowledging client-side stitching as a multi-query alternative. - src/main.ts: align the CLI help text.
bf28fbe to
6c57def
Compare
The defensive `if (!row) throw` after `expect(limited).toHaveLength(1)` was redundant and inconsistent with the non-null assertion style used elsewhere in the same file (e.g. `topTwo[0]!.distance`).
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@examples/prisma-next-demo/test/sql-dsl.integration.test.ts`:
- Around line 195-203: The test currently slices results into topTwo but never
asserts two rows exist, so add an explicit existence check (e.g.,
expect(topTwo.length).toBe(2) or
expect(results.length).toBeGreaterThanOrEqual(2)) before using topTwo; update
the block around topTwo, results, unorderedPairKey, and seededPostIds to assert
the presence of two rows first, then keep the existing unorderedPairKey and
distance assertions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yml
Review profile: CHILL
Plan: Pro
Run ID: 55f96f65-256c-4de6-b639-d9f01d6bfc0f
📒 Files selected for processing (1)
examples/prisma-next-demo/test/sql-dsl.integration.test.ts
Previously the test sliced into `topTwo` without asserting that two rows existed, which would silently pass if the query returned only a single row — defeating the point of the symmetric-pair check. Seed data produces 2×3×2 = 12 cross-author pairs; asserting this count up front makes the downstream slice and pair-key checks non-vacuous.
Closes: https://linear.app/prisma-company/issue/TML-2160/sql-dsl-standalone-query-execution
Summary
Adds a standalone SQL DSL query — "cross-author similarity" — to the
prisma-next-demoexample that exercises a shape the current ORM collection surface cannot directly express, then verifies it against a real Postgres via an integration test. This fulfills the stop condition of TML-2160 and validates the SQL DSL as the escape hatch for power users (VP1 of the Runtime pipeline project).The query
Finds the closest pairs of posts written by different authors, ordered by cosine distance between their embeddings. Projects both posts' id/title/userId side-by-side along with the distance between their embeddings.
Why this is an escape-hatch shape
The specific limitation is the single-collection surface, not the ORM client as a whole. Per ADR 164,
@prisma-next/sql-orm-clientis a repository layer that can orchestrate multiple plans for one logical operation — so a user could simulate this with client-side stitching at the cost of extra round-trips and losing single-statement ordering/limit semantics. The point of the SQL DSL escape hatch is that this shape is a single SQL statement making one pass over the data.Concretely, the query combines three facts about the current collection surface:
include('posts', ...)follows declared relations. JoiningPostto itself onp1.userId != p2.userIdis an arbitrary predicate join, not a relation, and cannot be expressed as a single collection query.cosineDistance(f.p1.embedding, f.p2.embedding)compares two columns from two aliases within one query. The ORM's extension-op integration (TML-2042) iscolumn.method(boundValue)— method-on-receiver form where the other argument must be a materialized value.ormClientFindSimilarPostsworks around this by running a separate query to load the reference embedding first. The collection surface has no "column vs column within a single query" form.Postrows projected flat into one output row is not a shape the single-collection surface produces.Tests
Integration tests against real Postgres (2 tests, new file)
crossAuthorSimilarity returns closest cross-author pairs ordered by cosine distancepostAUserId !== postBUserId).aliceFar × bobClose, distance ≈ 0.07).crossAuthorSimilarity respects the limit argumentlimit=1returns exactly one row, from the closest unordered pair.Full demo test suite: 36/36 passing.
Acceptance criteria from TML-2160 — all satisfied
@prisma/devPGlite-backed server.userIdcolumn refs andembeddingcolumn refs render as column references, not bound params.{ postAId: Char<36>, postATitle: string, postAUserId: string, postBId: Char<36>, postBTitle: string, postBUserId: string, distance: number }.Files changed
examples/prisma-next-demo/src/queries/cross-author-similarity.ts— the escape-hatch query (new)examples/prisma-next-demo/test/sql-dsl.integration.test.ts— 2 integration tests (new)examples/prisma-next-demo/src/main.ts— CLI commandcross-author-similarity [limit]Related side-findings
LATERAL JOINwas explored first but the demo's emitted capabilities includepostgres.lateral: truewithoutsql.lateral: true, which is whatlateralJoin's type-level gate requires. Filed as TML-2299.avg(cosineDistance(...))) is a genuine collection-surface gap but already tracked as TML-2137; cross-author similarity is the stronger shape that stays outside the single-collection surface regardless.Summary by CodeRabbit
New Features
Tests