Skip to content

feat(extension-paradedb): query ops and demo#433

Draft
SevInf wants to merge 2 commits intopsl-index-plusfrom
worktree/parade-db-ops
Draft

feat(extension-paradedb): query ops and demo#433
SevInf wants to merge 2 commits intopsl-index-plusfrom
worktree/parade-db-ops

Conversation

@SevInf
Copy link
Copy Markdown
Contributor

@SevInf SevInf commented May 7, 2026

Intent

Wire up the paradedb extension's query plane on top of the bm25 index-type registration that landed on psl-index-plus. Authoring + emit + DDL already work via the upstream registry; this PR adds the runtime-side: a complete set of paradedb query operations, the proximity chain builder, and an end-to-end CLI demo that exercises everything against a live paradedb container.

Change map

The extension itself (packages/3-extensions/paradedb) gains:

  • 11 query operations through two shared helpers in src/core/descriptor-meta.ts:
    • matchOp covers the five match-mode operators (paradeDbMatch/@@@, paradeDbMatchAny/|||, paradeDbMatchAll/&&&, paradeDbTerm/===, paradeDbPhrase/###).
    • typmodCastOp covers the four typmod casts (paradeDbFuzzy, paradeDbBoost, paradeDbConst, paradeDbSlop). Each wraps its integer argument in LiteralExpr.of(n) because Postgres rejects parameterized typmods, with per-op range validators.
  • paradeDbScore for pdb.score(<key>).
  • ParadeDbProximityChain in src/core/proximity-chain.ts — an immutable builder with .within(distance, term, { ordered? }) for multi-step chains mixing ## and ##>. Distances render as LiteralExpr (chained operators only accept literal slop). The chain implements Expression<text> so it composes through paradeDbMatch via the @@@ overload.
  • Runtime descriptor at src/exports/runtime.ts and pg_search install via SqlControlExtensionDescriptor.databaseDependencies.

examples/paradedb-demo is a new CLI example. prisma/contract.ts authors a single Item model with a bm25 index using the upstream constraints.index([...], { type: 'bm25', options: { key_field: 'id' } }) shape; pnpm db:init produces the actual CREATE INDEX … USING bm25 … DDL via the registry path. A docker-compose.yaml plus init/01-create-demo-db.sql create a dedicated demo database to sidestep the paradedb image's preloaded PostGIS tables.

CLI commands: match, top, fuzzy, proximity, proximity-chain, chain-demo (hardcoded multi-step + mixed direction), mode-tour (curated comparison of the five match modes against contrastive seed data), and cast-demo (boost/const/slop).

Also includes a small adapter fix in packages/3-targets/6-adapters/postgres/src/core/control-adapter.ts — index introspection now orders columns by array_position(indkey, attnum) rather than attnum, so multi-column indexes whose declared column order differs from the table's column declaration order satisfy schema verification.

Behavior evidence

  • pnpm start -- mode-tour returns distinct, contrastive results across the five match modes against the seed data, demonstrating each operator's character (any vs all vs term vs phrase).
  • pnpm start -- cast-demo shows boost/const/slop modifying the BM25 score as expected (pdb.boost(5) raises score ~2 → ~10; pdb.const(1) flattens to 1; pdb.slop(1) matches non-adjacent phrase tokens).
  • 17 paradedb unit tests cover all 11 operations + the proximity chain.

Follow-ups / open questions

  • paradeDbFuzzy/Boost/Const/Slop are typed as returning pg/text@1; actual SQL types are paradedb-specific (pdb.fuzzy, pdb.boost, etc.). The TS fudge works because match operators are overloaded to accept these types alongside text, but a more honest typing would introduce a paradedb codec.
  • text[] query input (term-set with ARRAY[...], pretokenized phrase) is deferred pending scalar-list support.
  • Tokenizer-override casts ('q'::pdb.whitespace), highlight (pdb.snippet*), aggregations, and search-time joins are not yet exposed.

Non-goals

  • The bm25 index-type registry, adapter introspection of type/options, and PSL @@index parsing all live in psl-index-plus, not here.
  • The legacy bm25Index({...}) and bm25.text/numeric/... authoring helpers the original branch carried were intentionally dropped in favor of the generic constraints.index({ type, options }) registry path.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro

Run ID: e4c0cfa1-b3c9-4d36-ba48-03cae20f0a8f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch worktree/parade-db-ops

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link
Copy Markdown

pkg-pr-new Bot commented May 7, 2026

Open in StackBlitz

@prisma-next/mongo-runtime

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-runtime@433

@prisma-next/family-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/family-mongo@433

@prisma-next/sql-runtime

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-runtime@433

@prisma-next/family-sql

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/family-sql@433

@prisma-next/extension-arktype-json

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-arktype-json@433

@prisma-next/middleware-telemetry

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/middleware-telemetry@433

@prisma-next/mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo@433

@prisma-next/extension-paradedb

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-paradedb@433

@prisma-next/extension-pgvector

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/extension-pgvector@433

@prisma-next/postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/postgres@433

@prisma-next/sql-orm-client

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-orm-client@433

@prisma-next/sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sqlite@433

@prisma-next/target-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/target-mongo@433

@prisma-next/adapter-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/adapter-mongo@433

@prisma-next/driver-mongo

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/driver-mongo@433

@prisma-next/contract

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/contract@433

@prisma-next/utils

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/utils@433

@prisma-next/config

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/config@433

@prisma-next/errors

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/errors@433

@prisma-next/framework-components

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/framework-components@433

@prisma-next/operations

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/operations@433

@prisma-next/ts-render

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/ts-render@433

@prisma-next/contract-authoring

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/contract-authoring@433

@prisma-next/ids

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/ids@433

@prisma-next/psl-parser

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/psl-parser@433

@prisma-next/psl-printer

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/psl-printer@433

@prisma-next/cli

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/cli@433

@prisma-next/emitter

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/emitter@433

@prisma-next/migration-tools

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/migration-tools@433

prisma-next

npm i https://pkg.pr.new/prisma/prisma-next@433

@prisma-next/vite-plugin-contract-emit

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/vite-plugin-contract-emit@433

@prisma-next/mongo-codec

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-codec@433

@prisma-next/mongo-contract

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-contract@433

@prisma-next/mongo-value

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-value@433

@prisma-next/mongo-contract-psl

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-contract-psl@433

@prisma-next/mongo-contract-ts

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-contract-ts@433

@prisma-next/mongo-emitter

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-emitter@433

@prisma-next/mongo-schema-ir

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-schema-ir@433

@prisma-next/mongo-query-ast

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-query-ast@433

@prisma-next/mongo-orm

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-orm@433

@prisma-next/mongo-query-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-query-builder@433

@prisma-next/mongo-lowering

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-lowering@433

@prisma-next/mongo-wire

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/mongo-wire@433

@prisma-next/sql-contract

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract@433

@prisma-next/sql-errors

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-errors@433

@prisma-next/sql-operations

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-operations@433

@prisma-next/sql-schema-ir

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-schema-ir@433

@prisma-next/sql-contract-psl

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract-psl@433

@prisma-next/sql-contract-ts

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract-ts@433

@prisma-next/sql-contract-emitter

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-contract-emitter@433

@prisma-next/sql-lane-query-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-lane-query-builder@433

@prisma-next/sql-relational-core

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-relational-core@433

@prisma-next/sql-builder

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/sql-builder@433

@prisma-next/target-postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/target-postgres@433

@prisma-next/target-sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/target-sqlite@433

@prisma-next/adapter-postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/adapter-postgres@433

@prisma-next/adapter-sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/adapter-sqlite@433

@prisma-next/driver-postgres

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/driver-postgres@433

@prisma-next/driver-sqlite

npm i https://pkg.pr.new/prisma/prisma-next/@prisma-next/driver-sqlite@433

commit: 2ef7bec

Adds the paradedb extension query plane on top of upstream bm25 index-type
registration:

- 11 query operations via two shared helpers — matchOp for the five
  match-mode operators (paradeDbMatch @@@, paradeDbMatchAny |||,
  paradeDbMatchAll &&&, paradeDbTerm ===, paradeDbPhrase ###) and
  typmodCastOp for the four typmod casts (paradeDbFuzzy, paradeDbBoost,
  paradeDbConst, paradeDbSlop). Cast args wrap a JS-side integer in
  LiteralExpr.of(n) because PG rejects parameterized typmods; per-op
  validators enforce the documented ranges.
- paradeDbScore (pdb.score(key)) and ParadeDbProximityChain — an
  immutable builder with .within(distance, term, { ordered? }) for
  arbitrary-length chains mixing ## and ##>. Distances render as
  LiteralExpr (chained ##/##> only accept literal slop). The chain
  implements Expression<text> so it composes directly through
  paradeDbMatch via the @@@ overload.
- pg_search install via SqlControlExtensionDescriptor.databaseDependencies.

examples/paradedb-demo runs every operation end-to-end against a live
paradedb container. Contract authored with the new shape:
constraints.index([cols...], { type: "bm25", options: { key_field: "id" },
name: "item_bm25_idx" }). docker-compose.yaml + init script create a
dedicated demo database to sidestep the paradedb image preloaded PostGIS
tables in the default database. CLI commands: match, top, fuzzy,
proximity, proximity-chain, chain-demo, mode-tour (curated tour of the
five match modes against contrastive seed data), cast-demo.

Also fixes a pre-existing introspection bug in adapter-postgres: the
indexes query ordered columns by pg_attribute.attnum (table declaration
order) rather than by position within pg_index.indkey (the index key
order). Multi-column indexes whose declared column order differs from
the table column declaration order would spuriously fail schema
verification — including any contract whose columns get alphabetized
during canonicalization while an index declares its own ordering.
@SevInf SevInf force-pushed the worktree/parade-db-ops branch from 6af7b34 to e5643ee Compare May 7, 2026 13:23
…onTypes

Drop matchOp/typmodCastOp helpers and inline all 11 entries in a record
annotated with `DescriptorMap<CT>`, a mapped type derived from
`QueryOperationTypes<CT>`. Renaming a method or changing a return codec in
the type module now fails typecheck inside `descriptor-meta.ts` instead of
silently drifting into a runtime mismatch.
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.

1 participant