feat(orm): add fuzzy search and relevance ordering (PostgreSQL)#2573
feat(orm): add fuzzy search and relevance ordering (PostgreSQL)#2573ymc9 merged 9 commits intozenstackhq:devfrom
Conversation
… only) - Introduced fuzzy search operators (`fuzzy`, `fuzzyContains`) in the ORM. - Added `RelevanceOrderBy` type for sorting based on fuzzy search relevance. - Implemented fuzzy search filters in PostgreSQL dialect. - Added error handling for unsupported fuzzy search features in MySQL and SQLite dialects. - Updated Zod schema factory to include fuzzy search fields. - Created a new `Flavor` model in the schema for testing purposes.
|
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:
📝 WalkthroughWalkthroughAdds a PostgreSQL-backed fuzzy text-search feature: new Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Warning Review ran into problems🔥 ProblemsGit: Failed to clone repository. Please run the Tip 💬 Introducing Slack Agent: Turn conversations into code.Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.
Built for teams:
One agent for your entire SDLC. Right inside Slack. 👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value). 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
packages/orm/src/client/crud/dialects/postgresql.ts (1)
561-590: Well-implemented PostgreSQL fuzzy search using pg_trgm.The implementation correctly uses:
- Trigram similarity operator (
%) forfuzzy- Word similarity operator (
<%) forfuzzyContainswith proper operand orderingGREATEST()aggregation for multi-field relevance scoringThe use of
sqltemplate tags is appropriate here as these are PostgreSQL-specific operators not available in Kysely's type-safe API. Thesqltemplate is Kysely's escape hatch mechanism.Note: Extension dependencies (
pg_trgmandunaccent) are already documented in the type definitions (crud-types.ts). Consider adding runtime error handling if extensions are missing, similar to thecreateNotSupportedErrorpattern used for MySQL/SQLite, to provide users with a clearer message instead of a generic PostgreSQL error.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/orm/src/client/crud/dialects/postgresql.ts` around lines 561 - 590, Add runtime checks for the required PostgreSQL extensions and throw a clear user-facing error if missing: implement an internal check (e.g., ensurePostgresExtensionsAvailable) that queries pg_extension for 'pg_trgm' and 'unaccent' and call it from the PostgreSQL dialect initialization or lazily before using fuzzy features; update buildFuzzyFilter, buildFuzzyContainsFilter, and buildRelevanceOrderBy to call this check (or ensure it's called beforehand) and throw a createNotSupportedError-style error with a clear message and remediation steps if either extension is absent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@packages/orm/src/client/crud-types.ts`:
- Around line 912-930: Update the RelevanceOrderBy type and its JSDoc to match
runtime behavior: change the _relevance.fields type from plain array to a
NonEmptyArray<NonRelationFields<Schema, Model>> so an empty fields list is
rejected at the type level, and revise the comment for _relevance to indicate
that relevance uses PostgreSQL similarity() (and that MySQL is not supported /
throws NotSupported at runtime) so IntelliSense reflects actual provider
constraints; locate the RelevanceOrderBy type and the _relevance field
declaration to make these edits.
In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1110-1131: The _relevance branch adds complex ordering but cursor
pagination still assumes simple {field: 'asc'|'desc'} entries; update handling
so cursor with a _relevance order is either rejected early or supported: modify
the code path that constructs cursor filters (function buildCursorFilter) to
detect order entries where field === '_relevance' (created via
buildRelevanceOrderBy / buildFieldRef / negateSort) and generate a comparison
that first compares computed relevance (value.search against the same fields)
then applies a deterministic tie-breaker (e.g., primary key) in the same sort
direction, or alternatively throw a clear validation error when a cursor is
supplied alongside an _relevance order; ensure tests cover both rejection and
correct SQL generation if you implement support.
In `@packages/orm/src/client/zod/factory.ts`:
- Around line 1180-1192: The _relevance.fields enum is currently built from all
scalar fields (scalarFieldNames) which allows non-string types; change the
scalarFieldNames computation in the getModelFields/filter pipeline to include
only string-typed scalar fields (e.g., filter by the field metadata indicating
type === 'String' or equivalent in your field definition) so that
_relevance.fields contains only string fields, and keep the z.enum(...) usage
but fed from the new string-only scalarFieldNames; update the code around
getModelFields, scalarFieldNames, and the _relevance strictObject construction
to reflect this restriction.
---
Nitpick comments:
In `@packages/orm/src/client/crud/dialects/postgresql.ts`:
- Around line 561-590: Add runtime checks for the required PostgreSQL extensions
and throw a clear user-facing error if missing: implement an internal check
(e.g., ensurePostgresExtensionsAvailable) that queries pg_extension for
'pg_trgm' and 'unaccent' and call it from the PostgreSQL dialect initialization
or lazily before using fuzzy features; update buildFuzzyFilter,
buildFuzzyContainsFilter, and buildRelevanceOrderBy to call this check (or
ensure it's called beforehand) and throw a createNotSupportedError-style error
with a clear message and remediation steps if either extension is absent.
🪄 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.yaml
Review profile: CHILL
Plan: Pro
Run ID: 5c89d679-7173-415f-83ce-5738308b98ee
📒 Files selected for processing (12)
packages/orm/src/client/constants.tspackages/orm/src/client/crud-types.tspackages/orm/src/client/crud/dialects/base-dialect.tspackages/orm/src/client/crud/dialects/mysql.tspackages/orm/src/client/crud/dialects/postgresql.tspackages/orm/src/client/crud/dialects/sqlite.tspackages/orm/src/client/zod/factory.tstests/e2e/orm/client-api/fuzzy-search.test.tstests/e2e/orm/schemas/basic/input.tstests/e2e/orm/schemas/basic/models.tstests/e2e/orm/schemas/basic/schema.tstests/e2e/orm/schemas/basic/schema.zmodel
- _relevance.fields restreint aux champs String dans le schéma Zod - Rejet du cursor pagination combiné avec _relevance ordering - Type RelevanceOrderBy restreint aux StringFields avec tuple non-vide - JSDoc mis à jour pour refléter le support PostgreSQL uniquement
|
Hi @docloulou , thanks for this amazing PR, very useful feature and well implemented! I'm wondering if you're fine with delaying it to release v3.7 or 3.8. Asking this because, although not directly related, it's a bit odd to support fuzzy search but not regular full text search (a feature gap from Prisma). I hope to get FTS implemented, probably in 3.7, and we can have this feature either together or in a subsequent minor release. What do you think? |
|
No problem for me. If the code in this PR looks solid to you, it can serve as a good template for adding the FTS feature. The main things left to handle would be adding the Note: one thing to watch out for - in this PR I’m using _relevance (as Prisma does for FTS) for the fuzzy search, so there could be a conflict. |
|
I'm really loving the idea of this! I would definitely use this a lot :D |
Yes, it'll definitely help FTS implementation. Maybe we can share the _relevance field if conflict can very carefully avoided. |
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 `@packages/orm/src/client/crud-types.ts`:
- Around line 906-922: The _relevance.fields type currently allows enum-backed
string literal unions because StringFields is based on MapModelFieldType
returning any string-like type; change the typing so only true free-form string
columns are allowed (i.e. require MapModelFieldType<Schema, Model, Key> to be
exactly the primitive string type, not a string-literal union or enum type) and
use that stricter alias in RelevanceOrderBy._relevance.fields; align this
compile-time guard with the runtime check implemented in the zod factory (the
string-only schema logic around factory.ts handling of String) by
creating/renaming StringFields to something like StringFieldsOnlyString and
swapping it into RelevanceOrderBy._relevance.fields.
🪄 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.yaml
Review profile: CHILL
Plan: Pro
Run ID: 4df1bb7c-a65b-44f5-8926-58fcd6c21dee
📒 Files selected for processing (6)
packages/orm/src/client/crud-types.tspackages/orm/src/client/crud/dialects/base-dialect.tspackages/orm/src/client/crud/dialects/mysql.tspackages/orm/src/client/crud/dialects/postgresql.tspackages/orm/src/client/crud/dialects/sqlite.tspackages/orm/src/client/zod/factory.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- packages/orm/src/client/crud/dialects/postgresql.ts
- packages/orm/src/client/zod/factory.ts
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 `@packages/server/test/openapi/baseline/rpc.baseline.yaml`:
- Around line 4326-4329: The OpenAPI generator is advertising PostgreSQL-only
search operators (fuzzy, fuzzyContains, and _relevance) unconditionally; update
the generator in packages/orm/src/client/zod/factory.ts so that the schema
properties for "fuzzy", "fuzzyContains" and "_relevance" are only emitted when
the datasource provider is Postgres (or when the required extension is enabled),
otherwise either omit them or add an explicit description/enum stating
"PostgreSQL-only / extension required"; locate the emission logic around the
existing provider-agnostic blocks (see the sections you flagged near lines
~1025-1035 and ~1304-1316) and add a provider check (or description branch)
before adding those properties to the OpenAPI schema.
🪄 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.yaml
Review profile: CHILL
Plan: Pro
Run ID: 456d6bee-273c-4080-a6e3-e57912084b89
📒 Files selected for processing (1)
packages/server/test/openapi/baseline/rpc.baseline.yaml
ymc9
left a comment
There was a problem hiding this comment.
Hi @docloulou , I've finished a review pass and added several comments. Please review them when you have time. Thanks!
…tured options - Refactored fuzzy search parameters to use an object structure, allowing for search term, mode, threshold, and accent sensitivity options. - Updated relevant types and methods across PostgreSQL, MySQL, SQLite, and Zod schema to support the new fuzzy search format. - Adjusted tests to reflect the new fuzzy search implementation and ensure compatibility with existing functionality.
…nce scoring
- Introduced `mode` options ('simple', 'word', 'strictWord') to enhance fuzzy search relevance ranking.
- Added `unaccent` option to control accent sensitivity during searches.
- Updated relevant types and methods across PostgreSQL, MySQL, SQLite, and Zod schema to accommodate new options.
- Enhanced tests to validate the new functionality and ensure expected behavior with various search scenarios.
|
@ymc9 Thanks for the thorough review! I've addressed all three comments, added 1.
|
| You proposed | Implemented as |
|---|---|
mode: 'simple' (operator %) |
✅ mode: 'simple' — field % search (or similarity(field, search) > threshold) |
mode: 'word' (operator <%) |
✅ mode: 'word' — search <% field (or word_similarity(search, field) > threshold) |
mode: 'strictWord' (operator <<%) |
✅ mode: 'strictWord' — search <<% field (or strict_word_similarity(search, field) > threshold) |
threshold extension |
✅ Implemented — when set, switches from operator form to <func>() > threshold so it bypasses session-level pg_trgm.*_threshold and is per-query |
unaccent opt-in |
✅ Implemented — default is false; when false, drops the unaccent() wrap, but keeps lower() so case-insensitivity is preserved |
Backward-incompatible by design — no internal silent fallback from fuzzy: 'string'. Since the feature is unreleased it felt cleaner to break the shape now than to ship a dual-form API. |
3. Should FuzzyRelevanceOrderBy live inside the OrArray wrapper? ✅ — keeping it as OrArray<OrderBy<...> & FuzzyRelevanceOrderBy<...>>
This is the part I want to argue most carefully because the alternatives drop genuinely useful capabilities.
What the current shape (OrArray<OrderBy & Fuzzy>) enables
// (a) Single relevance entry — basic case
orderBy: {
_fuzzyRelevance: {
fields: ['name'],
search: 'creme',
sort: 'desc',
},
}
// (b) Relevance + scalar tie-breaker — required for deterministic pagination
orderBy: [
{
_fuzzyRelevance: {
fields: ['name'],
search: 'creme',
sort: 'desc',
},
},
{ id: 'asc' },
]
// (c) Multi-relevance chained
orderBy: [
{
_fuzzyRelevance: {
fields: ['name'],
search: 'creme',
sort: 'desc',
},
},
{
_fuzzyRelevance: {
fields: ['description'],
search: 'french',
sort: 'desc',
},
},
]All three execute correctly: each array element calls .orderBy() on the Kysely builder, so SQL chains as ORDER BY similarity(...) DESC, "id" ASC and so on. I added e2e tests covering each case.
_fuzzyRelevance now also supports mode + unaccent
Latest update: relevance ordering now accepts the same matching mode and accent normalization controls as the where.fuzzy filter:
orderBy: {
_fuzzyRelevance: {
fields: ['name'],
search: 'choco',
mode: 'word', // default 'simple'
unaccent: true, // default false
sort: 'desc',
},
}Mapping:
_fuzzyRelevance.mode |
Ranking function |
|---|---|
simple |
similarity(field, search) |
word |
word_similarity(search, field) |
strictWord |
strict_word_similarity(search, field) |
unaccent: true wraps both sides with unaccent(lower(...)); unaccent: false uses lower(...) only. |
|
I intentionally did not add threshold to _fuzzyRelevance: threshold is a filtering concern, so it stays on where.fuzzy. Ranking should produce a score/order; filtering should decide whether a row is included. |
Why not pull it out, i.e. OrArray<OrderBy<...>> & FuzzyRelevanceOrderBy<...>?
That alternative resolves to (OrderBy | OrderBy[]) & { _fuzzyRelevance?: ... }. Concretely it would mean:
_fuzzyRelevancelives next toorderBy, not inside an element of it- You lose case (b) — no per-array tie-breaker; the relevance clause has no array slot to share
- You lose case (c) — no multi-relevance
- The runtime parser (
buildOrderBy) already treats_fuzzyRelevanceas a per-element key alongside_count/_avg/_min/_maxand scalar fields — pulling it out would create a type/runtime mismatch - Prisma exposes
_relevanceas anorderBykey with{ fields, search, sort }, and Prisma users already expectorderByto support chained array entries; this keeps the same user-facing mental model without depending on Prisma's exact internal generated type shape
What stops users from putting two keys in the same object?
The Zod refineAtMostOneKey refinement on the orderBy element (factory.ts). So this is rejected at runtime:
// ❌ Throws "Each orderBy element must have at most one key"
orderBy: {
_fuzzyRelevance: { fields: ['name'], search: 'creme', sort: 'desc' },
id: 'asc',
}The intersection at the type level is purely there to allow _fuzzyRelevance as a valid key choice on each element; the per-element single-key constraint comes from Zod, exactly like every other orderBy element today.
Cursor pagination guard
Kept the explicit guard, with the renamed key:
throw createNotSupportedError('cursor pagination cannot be combined with "_fuzzyRelevance" ordering');Heads-up for a follow-up: the guard is shallow (top-level keys only), so a nested { relation: { _fuzzyRelevance: {...} } } inside a to-one relation orderBy would currently slip through. I left this as-is to keep the diff focused, but happy to deepen the check in a follow-up commit if you'd prefer.
Test coverage
fuzzy-search.test.ts went from 35 → 62 tests, all passing on PostgreSQL:
- A–H: existing tests migrated to the new shape
- I:
fuzzy mode 'word'(formerlyfuzzyContains) - J–K: mutations +
groupBywith the new shape - L:
threshold— high (0.9) vs low (0.05), edge cases at 0 and 1, threshold + each mode (simple/word/strictWord), per-query independence (proves it's not session-wide) - M:
mode: 'strictWord'— including the strictness ordering vs'word' - N:
unaccent— defaults tofalse(locked by an explicit contract test assertingfuzzy: { search: 'creme' }does not match'Crème brûlée'), opt-inunaccent: truefinds accented terms,unaccent: falseworks alongsidethreshold+mode - O: cursor +
_fuzzyRelevancerejection - P: OrArray + intersection contract — pins the design decision from this review:
- case (a) single object form
orderBy: { _fuzzyRelevance: {...} } - single object ⇔ single-element array equivalence (proves
enumerate()normalization) - case (b) relevance + scalar tie-breaker, with
id: 'asc'vsid: 'desc'reversing the order on tied similarity (proves the tie-breaker is genuinely emitted as a chainedORDER BYcolumn) - case (b) tie-breaker survives
skip/takepagination - case (c) multi-relevance: secondary
_fuzzyRelevanceclause breaks ties on the primary (swapping its search term flips the result order) - case (c) multi-relevance + scalar tie-breaker chained together (3 records, primary tied, secondary tied between two, scalar decides)
- empty
{}array element is silently no-op - multi-key in a single orderBy element rejected by
refineAtMostOneKeyZod refinement
- case (a) single object form
- Q:
_fuzzyRelevanceoptions:mode: 'word'ranks an exact embedded word ("Hot choco drink") above a prefix-only match ("Chocolate")mode: 'strictWord'ranks a word-boundary match above a non-boundary matchunaccent: falsevsunaccent: trueflips the relevance ranking between"Cremezzzz"and"Crème", provingunaccentaffects the score used for ordering, not just filtering
The (b) and (c) OrArray tests force ties by inserting records with identical names, then assert that swapping the tie-breaker direction (or the secondary search term) flips the result order. This is the empirical proof that Kysely's.orderBy()is genuinely additive.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
tests/e2e/orm/client-api/fuzzy-search.test.ts (1)
781-786: ⚡ Quick winReject empty
orderByelements instead of locking in a silent no-op.This test currently turns
{}into supported behavior, which makes malformedorderBypayloads indistinguishable from valid ones. I’d prefer tightening the runtime check to require exactly one key per element and flipping this to an error assertion instead.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/e2e/orm/client-api/fuzzy-search.test.ts` around lines 781 - 786, The test currently treats an empty object element in orderBy as a silent no-op; change the runtime validation (where buildOrderBy processes orderBy elements) to reject any array element that does not have exactly one key and throw a clear error, then update this test (fuzzy-search.test.ts) to assert that client.flavor.findMany({ orderBy: [{}, { id: 'asc' }] }) throws the expected validation error instead of matching IDs against the ref, referencing buildOrderBy and the client.flavor.findMany call to locate the spots to modify.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/e2e/orm/client-api/fuzzy-search.test.ts`:
- Around line 789-801: The test currently uses a broad await
expect(...).rejects.toThrow(); tighten it to assert the Zod refinement failure
from refineAtMostOneKey in zod/factory.ts by expecting the specific error
message or error path: call client.flavor.findMany(...) as before but replace
the generic rejects.toThrow() with an assertion that the thrown error includes
the refineAtMostOneKey message (or the Zod issue path for the _fuzzyRelevance
field), e.g., expect(...).rejects.toThrow(/refineAtMostOneKey|at most one key/)
or inspect the error.errors array for the exact path to assert the single-key
rule. Ensure the test still targets the same input used in the current it block.
---
Nitpick comments:
In `@tests/e2e/orm/client-api/fuzzy-search.test.ts`:
- Around line 781-786: The test currently treats an empty object element in
orderBy as a silent no-op; change the runtime validation (where buildOrderBy
processes orderBy elements) to reject any array element that does not have
exactly one key and throw a clear error, then update this test
(fuzzy-search.test.ts) to assert that client.flavor.findMany({ orderBy: [{}, {
id: 'asc' }] }) throws the expected validation error instead of matching IDs
against the ref, referencing buildOrderBy and the client.flavor.findMany call to
locate the spots to modify.
🪄 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.yaml
Review profile: CHILL
Plan: Pro
Run ID: ac15d851-c051-4f42-b5e7-820bbd09a126
📒 Files selected for processing (9)
packages/orm/src/client/constants.tspackages/orm/src/client/crud-types.tspackages/orm/src/client/crud/dialects/base-dialect.tspackages/orm/src/client/crud/dialects/mysql.tspackages/orm/src/client/crud/dialects/postgresql.tspackages/orm/src/client/crud/dialects/sqlite.tspackages/orm/src/client/zod/factory.tspackages/server/test/openapi/baseline/rpc.baseline.yamltests/e2e/orm/client-api/fuzzy-search.test.ts
✅ Files skipped from review due to trivial changes (1)
- packages/orm/src/client/constants.ts
ymc9
left a comment
There was a problem hiding this comment.
Thanks for the quick follow-up @docloulou . Overall it's looking great to me. I've attached a few more minor comments. I think we're very close to getting it merged.
Mirror the existing `providerSupportsCaseSensitivity` pattern: introduce `providerSupportsFuzzySearch` and use it to add the `fuzzy` operator and the `_fuzzyRelevance` orderBy schema only when the schema's provider is postgresql.
…s at type level Follow the existing `ProviderSupportsDistinct` pattern (review feedback zenstackhq#4): - Add `ProviderSupportsFuzzy<Schema>` mirroring `ProviderSupportsDistinct`. - Extract the fuzzy payload into a reusable `FuzzyFilterPayload` type. - Gate `_fuzzyRelevance` in `SortAndTakeArgs.orderBy` so it is only typed when provider is postgresql. - Gate `fuzzy` on `StringFilter` via a non-invasive `AddFuzzyIfPostgres` wrapper applied in `FieldFilter` (which already has `Schema` in scope). The wrapper returns `Base` unchanged for non-postgres to preserve nullability (intersecting with `{}` would strip null/undefined). - Update the e2e fuzzy test suite to override the static sqlite provider type: the test only runs on postgres at runtime but typechecks against the basic schema's compile-time provider.
ymc9
left a comment
There was a problem hiding this comment.
LGTM! I'm merging and will release it with v3.7.
Summary
fuzzyandfuzzyContainsfilter operators for String fields inwhereclauses, using PostgreSQL'spg_trgmextension withunaccentfor accent-insensitive trigram matching_relevanceordering inorderByto sort results by fuzzy similarity score, supporting single and multiple fieldsNotSupportederrors for these operatorsNew API
Prerequisites (PostgreSQL)
The user must enable the following extensions in their PostgreSQL database:
Files changed
packages/orm/src/client/crud-types.tsfuzzy,fuzzyContainsinStringFilter;RelevanceOrderBytypepackages/orm/src/client/constants.tsFuzzyfilter kind inFILTER_PROPERTY_TO_KINDpackages/orm/src/client/crud/dialects/base-dialect.tsfuzzy,fuzzyContains,_relevancein filter/orderBy builders; 3 abstract methodspackages/orm/src/client/crud/dialects/postgresql.tspg_trgm+unaccentimplementation (%,<%,similarity(),GREATEST())packages/orm/src/client/crud/dialects/mysql.tsNotSupportederrorspackages/orm/src/client/crud/dialects/sqlite.tsNotSupportederrorspackages/orm/src/client/zod/factory.tsfuzzy,fuzzyContains,_relevancetests/e2e/orm/schemas/basic/schema.zmodelFlavormodeltests/e2e/orm/client-api/fuzzy-search.test.tsImplementation details
fuzzyfilterUses PostgreSQL trigram similarity operator
%withunaccentandlowerfor accent-insensitive, case-insensitive matching:fuzzyContainsfilterUses PostgreSQL word similarity operator
<%to check if the search term is approximately contained as a substring:_relevanceorderingUses
similarity()function for single fields,GREATEST()for multiple fields:Test plan
_relevanceordering (single field, multiple fields, with pagination)Documentation : zenstackhq/zenstack-docs#596
Summary by CodeRabbit
New Features
Database Support
Tests
Documentation