Skip to content

feat(orm): add field-level @fuzzy attribute to gate fuzzy search#2642

Merged
ymc9 merged 4 commits intodevfrom
feat/fuzzy-attribute
May 5, 2026
Merged

feat(orm): add field-level @fuzzy attribute to gate fuzzy search#2642
ymc9 merged 4 commits intodevfrom
feat/fuzzy-attribute

Conversation

@ymc9
Copy link
Copy Markdown
Member

@ymc9 ymc9 commented May 4, 2026

Summary

  • Adds a new @fuzzy field-level ZModel attribute that opts a String field into fuzzy search.
  • Gates the fuzzy: filter operator and _fuzzyRelevance orderBy at the type, zod, and runtime layers — fields without @fuzzy no longer participate.
  • Validator enforces @fuzzy is only allowed when the datasource provider is postgresql.
  • Hoists getDataSourceProvider from document.ts into shared language/utils.ts so both the validator and document loader use the same helper.

Layer breakdown

  • Language — declared @fuzzy() @@@targetField([StringField]) @@@once in stdlib.zmodel; new @check('@fuzzy') validator gating to postgres provider.
  • Schema — added fuzzy?: boolean to FieldDef; generator emits fuzzy: true when the attribute is present.
  • ORM typesAddFuzzyFilterIfSupported now requires GetModelField<...>['fuzzy'] extends true; new FuzzyFields<> used by _fuzzyRelevance.fields.
  • Zod factorywithFuzzy flag threaded through makeStringFilterSchema/makePrimitiveFilterSchema; _fuzzyRelevance.fields enum filters by def.fuzzy === true.
  • Runtimebase-dialect.ts throws InvalidInputError if fuzzy: filter or _fuzzyRelevance.fields references a non-@fuzzy field.

Tests

  • New dedicated tests/e2e/orm/schemas/fuzzy-search/ schema with @fuzzy on name/description and a non-fuzzy notes field.
  • Switched fuzzy-search.test.ts to the new schema; added two negative tests for non-@fuzzy field rejection.
  • Removed Flavor from basic schema (was only used for fuzzy testing).
  • 4 new validator tests in attribute-application.test.ts covering postgres/sqlite/mysql/non-String cases.

Breaking change

Existing schemas using fuzzy search must annotate target fields with @fuzzy to retain access. Since fuzzy search shipped only in #2573 (one PR ago), real-world fallout should be minimal.

Test plan

  • Language tests pass (68 incl. 4 new fuzzy validator tests)
  • ORM typecheck clean
  • e2e typecheck clean
  • Postgres lane runs fuzzy-search.test.ts (skipped under sqlite/mysql)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Fuzzy-search for string fields via a new @fuzzy attribute; enables fuzzy filtering and _fuzzyRelevance ordering (PostgreSQL only; requires pg_trgm).
  • Validation

    • Validation now rejects fuzzy operators on non-fuzzy fields or with unsupported data providers, with clear error messages.
  • SDK & Types

    • Generated schemas, runtime guards and client types mark and enforce fuzzy-capable fields.
  • Tests

    • New unit and e2e tests covering fuzzy usage and provider-specific validation.
  • Documentation

    • Expanded inline docs for @omit and updated fuzzy-mode entries in OpenAPI baseline.

Adds a new ZModel field-level `@fuzzy` attribute that opts a `String` field
into fuzzy search. Without `@fuzzy`, the `fuzzy` filter operator and
`_fuzzyRelevance` orderBy are unavailable both at the type level and at runtime.

- Language: declare `@fuzzy` in stdlib (StringField only, @@@once)
- Validator: enforce postgres-only via shared `getDataSourceProvider` helper
- Schema: surface `fuzzy?: boolean` on `FieldDef`; generator emits it
- ORM types: gate `fuzzy:` filter and `_fuzzyRelevance.fields` to opted-in fields
- Zod factory: thread `withFuzzy` through string filter; filter `_fuzzyRelevance` enum
- Runtime: throw `InvalidInputError` when fuzzy is used on non-`@fuzzy` fields
- Tests: dedicated `fuzzy-search` test schema; validator coverage for postgres/sqlite/mysql/non-String

Breaking: existing schemas using fuzzy search must annotate fields with
`@fuzzy` to retain access. Fuzzy search shipped only one PR ago (#2573).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 4, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 89d18b89-563d-45a1-ab7c-6b7d7fb68d09

📥 Commits

Reviewing files that changed from the base of the PR and between 08ac228 and e853fbd.

📒 Files selected for processing (1)
  • tests/e2e/orm/client-api/fuzzy-search.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/e2e/orm/client-api/fuzzy-search.test.ts

📝 Walkthrough

Walkthrough

Adds a field-level @fuzzy StringField attribute and propagates it through language, validation, schema metadata, SDK generation, ORM types and runtime, Zod schema generation, OpenAPI fragments, and tests; enforces PostgreSQL-only usage and restricts fuzzy operators and relevance ordering to fields annotated @fuzzy. (50 words)

Changes

Fuzzy-Search Attribute and Integration

Layer / File(s) Summary
Language Definition
packages/language/res/stdlib.zmodel
Expanded @omit docs and added documented attribute @fuzzy() targeting StringField (@@once).
AST / Utility
packages/language/src/utils.ts
Added isDataSource import and exported `getDataSourceProvider(model): string
Document Wiring
packages/language/src/document.ts
Imported getDataSourceProvider from ./utils; removed local helper implementation.
Attribute Validation
packages/language/src/validators/attribute-application-validator.ts, packages/language/test/attribute-application.test.ts
Registered @fuzzy checker that resolves model datasource provider and errors if provider ≠ postgresql; tests assert postgres acceptance and sqlite/mysql/non-String rejections.
Schema Type
packages/schema/src/schema.ts
Added optional fuzzy?: boolean to exported FieldDef type.
SDK Generation
packages/sdk/src/ts-schema-generator.ts
createDataFieldObject emits fuzzy: true when field has @fuzzy.
ORM Type System
packages/orm/src/client/crud-types.ts
Refactored AddFuzzyFilterIfSupported to accept Schema,Model,Field and derive field type; added FuzzyFields<Schema,Model> and tightened _fuzzyRelevance.fields to @fuzzy string fields.
ORM Runtime / Dialect
packages/orm/src/client/crud/dialects/base-dialect.ts
buildPrimitiveFilter passes fieldDef into string filter builder; buildStringFilter and _fuzzyRelevance assert fieldDef.fuzzy === true for fuzzy ops and throw informative invariants.
Zod Schema Generation
packages/orm/src/client/zod/factory.ts
Threaded withFuzzy flag into primitive/string filter schema builders to conditionally include fuzzy operator; _fuzzyRelevance enumerates only def.fuzzy === true fields and registers a Fuzzy-suffixed schema id when enabled.
OpenAPI Baseline
packages/server/test/openapi/baseline/rpc.baseline.yaml
Updated fuzzy mode enum values in component schemas to `default
Tests & Fixtures
tests/e2e/orm/client-api/fuzzy-search.test.ts, tests/e2e/orm/schemas/fuzzy-search/*, tests/e2e/orm/schemas/basic/*
Added Postgres fuzzy-search schema and tests (including runtime assertions rejecting fuzzy usage on non-@fuzzy fields); adjusted basic schema fixtures (replaced Flavor with Plain).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • zenstackhq/zenstack#2573 — Implements overlapping fuzzy-search feature changes across ORM types, Zod schemas, dialect checks, and tests.

Poem

🐰 I hopped through code with nimble paws,
I stitched @fuzzy into stringy laws,
Postgres trigrams hum and sing,
From language down to tests and Zod schema ring,
I left a trail of specs and tiny claws ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding a field-level @fuzzy attribute to control fuzzy search eligibility, which is the core objective of the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/fuzzy-attribute

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.

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 4, 2026

Claude finished @ymc9's task —— View job


PR Review: Field-level @fuzzy attribute implementation

I've conducted a comprehensive review of this PR that introduces the @fuzzy attribute for field-level fuzzy search gating. This is a well-structured implementation that successfully adds type safety and runtime validation across all layers.

Strengths

📚 Language Layer Excellence

  • Proper attribute definition: @fuzzy() @@@targetField([StringField]) correctly constrains to String fields only (stdlib.zmodel:408)
  • PostgreSQL validation: Clean validator implementation (attribute-application-validator.ts:354-364) properly gates usage to PostgreSQL provider
  • Utility refactoring: Smart extraction of getDataSourceProvider to shared utils eliminates duplication

**🏗️ Schema & Generation Robustness

  • Clean schema extension: Simple fuzzy?: boolean addition to FieldDef (schema.ts:79)
  • Consistent generation: TypeScript generator properly emits fuzzy: true when attribute is present (ts-schema-generator.ts:629-630)

**🎯 Type System Precision

  • Type-level gating: AddFuzzyFilterIfSupported elegantly requires GetModelField<...>['fuzzy'] extends true (crud-types.ts:409-425)
  • Clean fuzzy fields extraction: FuzzyFields<> type properly filters to only @fuzzy-annotated String fields (crud-types.ts:943-945)
  • Proper conditional types: All fuzzy functionality is correctly gated on both provider support and field annotation

**⚡ Runtime Validation

  • Clear error messages: Runtime validation provides helpful error messages like "field 'notes' is not fuzzy-searchable; add the \@fuzzy` attribute" (base-dialect.ts:945`)
  • Consistent validation: Both fuzzy: filter and _fuzzyRelevance ordering properly validate field annotations (base-dialect.ts:1135-1137)

**🔧 Zod Integration

  • Thread-through support: withFuzzy parameter properly flows through the factory (factory.ts:796, 1019)
  • Schema filtering: _fuzzyRelevance.fields enum correctly filters by def.fuzzy === true (factory.ts:1325-1327)
  • Clean registration: Fuzzy suffix properly added to schema names for disambiguation (factory.ts:1055-1058)

🧪 Comprehensive Testing

Positive tests: Dedicated fuzzy-search schema with mixed fuzzy/non-fuzzy fields provides excellent coverage
Negative tests: Two new tests properly validate rejection of fuzzy operations on non-@fuzzy fields
Validator coverage: 4 new tests covering all provider combinations and non-String field rejection
Schema cleanup: Removal of Flavor from basic schema maintains clean separation

🚀 Architecture & Design

Breaking change handling: Well-documented and justified - minimal real-world impact given fuzzy search was just introduced
Layer separation: Clean implementation across language, schema, ORM, and runtime layers
Future-proofing: Design allows for potential extension to other providers or search types

⚠️ Minor Considerations

  1. Documentation: Consider adding JSDoc examples for the @fuzzy attribute usage patterns
  2. Error context: Runtime error messages could include the model name for better debugging in large schemas
  3. Performance: No performance concerns - validation is compile-time + minimal runtime checks

📊 Security & Performance

  • No security issues: Validation prevents misuse, no injection vectors introduced
  • Performance neutral: Gating is type-level and minimal runtime validation
  • Resource efficiency: Proper constraint validation prevents unnecessary query building

🎯 Overall Assessment

This is an exemplary implementation that demonstrates excellent TypeScript engineering:

  • Type safety: Complete compile-time safety for fuzzy search operations
  • Runtime safety: Clear validation with helpful error messages
  • Clean architecture: Proper separation of concerns across all layers
  • Comprehensive testing: Excellent coverage of both positive and negative cases

Recommendation: ✅ APPROVE

This PR successfully implements field-level fuzzy search gating with robust type safety, comprehensive validation, and excellent test coverage. The breaking change is well-justified and the implementation follows ZenStack's architectural patterns perfectly.


Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
packages/language/res/stdlib.zmodel (1)

408-408: 💤 Low value

Optional: consider adding @@@once to prevent duplicate @fuzzy annotations.

Without @@@once, a field with @fuzzy @fuzzy`` would silently produce duplicate entries in the generated attributes array (though `fuzzy: true` is idempotent). Adding `@@@once` makes the constraint explicit and consistent with other single-application attributes like `@id` and `@unique`.

✨ Proposed fix
-attribute `@fuzzy`() @@@targetField([StringField])
+attribute `@fuzzy`() @@@targetField([StringField]) @@@once
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/language/res/stdlib.zmodel` at line 408, The attribute declaration
"attribute `@fuzzy`() @@@targetField([StringField])" can be applied multiple times
and may produce duplicate entries; update the attribute definition to include
the singleton constraint by adding "@@@once" to the attribute metadata (i.e.,
change the attribute declaration for `@fuzzy` to "attribute `@fuzzy`() @@@once
@@@targetField([StringField])") so the model enforces single application like
other single-use attributes such as `@id` and `@unique`.
packages/orm/src/client/crud/dialects/base-dialect.ts (1)

1132-1139: 💤 Low value

requireField is called twice per field — once for validation, once inside buildFieldRef.

The validation loop (line 1133) calls requireField, then line 1139's buildFieldRef call invokes it again. For a small value.fields array this is negligible, but you can avoid the double lookup by storing the refs during validation:

♻️ Proposed refactor
-                    for (const fieldName of value.fields as string[]) {
-                        const fieldDef = requireField(this.schema, model, fieldName);
-                        invariant(
-                            fieldDef.fuzzy === true,
-                            `field "${fieldName}" is not fuzzy-searchable; add the \`@fuzzy\` attribute to use it in \`_fuzzyRelevance\``,
-                        );
-                    }
-                    const fieldRefs = value.fields.map((f: string) => buildFieldRef(model, f, modelAlias));
+                    const fieldRefs = (value.fields as string[]).map((fieldName) => {
+                        const fieldDef = requireField(this.schema, model, fieldName);
+                        invariant(
+                            fieldDef.fuzzy === true,
+                            `field "${fieldName}" is not fuzzy-searchable; add the \`@fuzzy\` attribute to use it in \`_fuzzyRelevance\``,
+                        );
+                        return buildFieldRef(model, fieldName, modelAlias);
+                    });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/orm/src/client/crud/dialects/base-dialect.ts` around lines 1132 -
1139, The code calls requireField twice for each entry in value.fields (once in
the validation loop and again inside buildFieldRef), causing redundant lookups;
modify the loop that iterates value.fields to both validate and build/cache the
field reference in one pass (use requireField to get fieldDef and then call
buildFieldRef once per field), collect those refs into fieldRefs and remove the
separate map call so buildFieldRef is not invoked a second time; update any use
of modelAlias, model, schema, requireField, buildFieldRef, and fieldRefs
accordingly.
tests/e2e/orm/client-api/fuzzy-search.test.ts (1)

820-822: 💤 Low value

Section label "E" is a duplicate — should be "R".

"E. fuzzy in logical compositions" already appears earlier in the file (line ~204). This new block should be "R" (the next letter after Q at line 786) to keep the section labels sequential.

🔤 Suggested fix
-    // E. `@fuzzy` attribute gating
+    // R. `@fuzzy` attribute gating
🤖 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 820 - 822, Change
the duplicated section label "E. `@fuzzy` attribute gating" to "R. `@fuzzy`
attribute gating" so section letters remain sequential (the earlier "E. fuzzy in
logical compositions" exists already); locate the header string "E. `@fuzzy`
attribute gating" in tests/e2e/orm/client-api/fuzzy-search.test.ts and replace
the leading letter only, ensuring the rest of the heading text and any related
comments or test descriptions (e.g., the surrounding section divider lines)
remain unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/language/res/stdlib.zmodel`:
- Line 408: The attribute declaration "attribute `@fuzzy`()
@@@targetField([StringField])" can be applied multiple times and may produce
duplicate entries; update the attribute definition to include the singleton
constraint by adding "@@@once" to the attribute metadata (i.e., change the
attribute declaration for `@fuzzy` to "attribute `@fuzzy`() @@@once
@@@targetField([StringField])") so the model enforces single application like
other single-use attributes such as `@id` and `@unique`.

In `@packages/orm/src/client/crud/dialects/base-dialect.ts`:
- Around line 1132-1139: The code calls requireField twice for each entry in
value.fields (once in the validation loop and again inside buildFieldRef),
causing redundant lookups; modify the loop that iterates value.fields to both
validate and build/cache the field reference in one pass (use requireField to
get fieldDef and then call buildFieldRef once per field), collect those refs
into fieldRefs and remove the separate map call so buildFieldRef is not invoked
a second time; update any use of modelAlias, model, schema, requireField,
buildFieldRef, and fieldRefs accordingly.

In `@tests/e2e/orm/client-api/fuzzy-search.test.ts`:
- Around line 820-822: Change the duplicated section label "E. `@fuzzy` attribute
gating" to "R. `@fuzzy` attribute gating" so section letters remain sequential
(the earlier "E. fuzzy in logical compositions" exists already); locate the
header string "E. `@fuzzy` attribute gating" in
tests/e2e/orm/client-api/fuzzy-search.test.ts and replace the leading letter
only, ensuring the rest of the heading text and any related comments or test
descriptions (e.g., the surrounding section divider lines) remain unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 917aab72-a24c-49ba-a1a5-179ebffcf932

📥 Commits

Reviewing files that changed from the base of the PR and between 679f91f and 8f70f17.

📒 Files selected for processing (18)
  • packages/language/res/stdlib.zmodel
  • packages/language/src/document.ts
  • packages/language/src/utils.ts
  • packages/language/src/validators/attribute-application-validator.ts
  • packages/language/test/attribute-application.test.ts
  • packages/orm/src/client/crud-types.ts
  • packages/orm/src/client/crud/dialects/base-dialect.ts
  • packages/orm/src/client/zod/factory.ts
  • packages/schema/src/schema.ts
  • packages/sdk/src/ts-schema-generator.ts
  • tests/e2e/orm/client-api/fuzzy-search.test.ts
  • tests/e2e/orm/schemas/basic/input.ts
  • tests/e2e/orm/schemas/basic/models.ts
  • tests/e2e/orm/schemas/basic/schema.ts
  • tests/e2e/orm/schemas/basic/schema.zmodel
  • tests/e2e/orm/schemas/fuzzy-search/index.ts
  • tests/e2e/orm/schemas/fuzzy-search/schema.ts
  • tests/e2e/orm/schemas/fuzzy-search/schema.zmodel
💤 Files with no reviewable changes (4)
  • tests/e2e/orm/schemas/basic/schema.ts
  • tests/e2e/orm/schemas/basic/schema.zmodel
  • tests/e2e/orm/schemas/basic/input.ts
  • tests/e2e/orm/schemas/basic/models.ts

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 4, 2026

Claude finished @ymc9's task —— View job


PR Review: Field-level @fuzzy attribute implementation

I've conducted a comprehensive review of this PR that introduces the @fuzzy attribute for field-level fuzzy search gating. This is a well-structured implementation that successfully adds type safety and runtime validation across all layers.

Strengths

📚 Language Layer Excellence

  • Proper attribute definition: @fuzzy() @@@targetField([StringField]) correctly constrains to String fields only (stdlib.zmodel:408)
  • PostgreSQL validation: Clean validator implementation (attribute-application-validator.ts:354-364) properly gates usage to PostgreSQL provider
  • Utility refactoring: Smart extraction of getDataSourceProvider to shared utils eliminates duplication

**🏗️ Schema & Generation Robustness

  • Clean schema extension: Simple fuzzy?: boolean addition to FieldDef (schema.ts:79)
  • Consistent generation: TypeScript generator properly emits fuzzy: true when attribute is present (ts-schema-generator.ts:629-630)

**🎯 Type System Precision

  • Type-level gating: AddFuzzyFilterIfSupported elegantly requires GetModelField<...>['fuzzy'] extends true (crud-types.ts:409-425)
  • Clean fuzzy fields extraction: FuzzyFields<> type properly filters to only @fuzzy-annotated String fields (crud-types.ts:943-945)
  • Proper conditional types: All fuzzy functionality is correctly gated on both provider support and field annotation

**⚡ Runtime Validation

  • Clear error messages: Runtime validation provides helpful error messages like "field 'notes' is not fuzzy-searchable; add the \@fuzzy` attribute" (base-dialect.ts:945`)
  • Consistent validation: Both fuzzy: filter and _fuzzyRelevance ordering properly validate field annotations (base-dialect.ts:1135-1137)

**🔧 Zod Integration

  • Thread-through support: withFuzzy parameter properly flows through the factory (factory.ts:796, 1019)
  • Schema filtering: _fuzzyRelevance.fields enum correctly filters by def.fuzzy === true (factory.ts:1325-1327)
  • Clean registration: Fuzzy suffix properly added to schema names for disambiguation (factory.ts:1055-1058)

🧪 Comprehensive Testing

Positive tests: Dedicated fuzzy-search schema with mixed fuzzy/non-fuzzy fields provides excellent coverage
Negative tests: Two new tests properly validate rejection of fuzzy operations on non-@fuzzy fields
Validator coverage: 4 new tests covering all provider combinations and non-String field rejection
Schema cleanup: Removal of Flavor from basic schema maintains clean separation

🚀 Architecture & Design

Breaking change handling: Well-documented and justified - minimal real-world impact given fuzzy search was just introduced
Layer separation: Clean implementation across language, schema, ORM, and runtime layers
Future-proofing: Design allows for potential extension to other providers or search types

⚠️ Minor Considerations

  1. Documentation: Consider adding JSDoc examples for the @fuzzy attribute usage patterns
  2. Error context: Runtime error messages could include the model name for better debugging in large schemas
  3. Performance: No performance concerns - validation is compile-time + minimal runtime checks

📊 Security & Performance

  • No security issues: Validation prevents misuse, no injection vectors introduced
  • Performance neutral: Gating is type-level and minimal runtime validation
  • Resource efficiency: Proper constraint validation prevents unnecessary query building

🎯 Overall Assessment

This is an exemplary implementation that demonstrates excellent TypeScript engineering:

  • Type safety: Complete compile-time safety for fuzzy search operations
  • Runtime safety: Clear validation with helpful error messages
  • Clean architecture: Proper separation of concerns across all layers
  • Comprehensive testing: Excellent coverage of both positive and negative cases

Recommendation: ✅ APPROVE

This PR successfully implements field-level fuzzy search gating with robust type safety, comprehensive validation, and excellent test coverage. The breaking change is well-justified and the implementation follows ZenStack's architectural patterns perfectly.


Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 5, 2026

Claude finished @ymc9's task —— View job


Code Review: Field-level @fuzzy attribute implementation

I've conducted a comprehensive review of this PR that introduces the @fuzzy attribute for field-level fuzzy search gating. This is a well-structured implementation that successfully adds type safety and runtime validation across all layers.

Strengths

📚 Language Layer Excellence

  • Proper attribute definition: @fuzzy() @@@targetField([StringField]) @@@once correctly constrains to String fields only (stdlib.zmodel:408)
  • PostgreSQL validation: Clean validator implementation (attribute-application-validator.ts:354-364) properly gates usage to PostgreSQL provider
  • Utility refactoring: Smart extraction of getDataSourceProvider to shared utils (utils.ts:178-188) eliminates duplication

🏗️ Schema & Generation Robustness

  • Clean schema extension: Simple fuzzy?: boolean addition to FieldDef (schema.ts:79)
  • Consistent generation: TypeScript generator properly emits fuzzy: true when attribute is present (ts-schema-generator.ts:629-630)

🎯 Type System Precision

  • Type-level gating: AddFuzzyFilterIfSupported elegantly requires GetModelField<...>['fuzzy'] extends true (crud-types.ts:409-425)
  • Clean fuzzy fields extraction: FuzzyFields<> type properly filters to only @fuzzy-annotated String fields (crud-types.ts:943-945)
  • Proper conditional types: All fuzzy functionality is correctly gated on both provider support and field annotation

⚡ Runtime Validation

  • Clear error messages: Runtime validation provides helpful error messages like "field 'notes' is not fuzzy-searchable; add the \@fuzzy` attribute" (base-dialect.ts:945`)
  • Consistent validation: Both fuzzy: filter and _fuzzyRelevance ordering properly validate field annotations (base-dialect.ts:1135-1137)

🔧 Zod Integration

  • Thread-through support: withFuzzy parameter properly flows through the factory (factory.ts:796, 1019)
  • Schema filtering: _fuzzyRelevance.fields enum correctly filters by def.fuzzy === true (factory.ts:1325-1327)
  • Clean registration: Fuzzy suffix properly added to schema names for disambiguation (factory.ts:1055-1058)

🧪 Comprehensive Testing

Positive tests: Dedicated fuzzy-search schema with mixed fuzzy/non-fuzzy fields provides excellent coverage
Negative tests: Two new tests properly validate rejection of fuzzy operations on non-@fuzzy fields (fuzzy-search.test.ts:824-839)
Validator coverage: 4 new tests covering all provider combinations and non-String field rejection (attribute-application.test.ts:434-499)
Schema cleanup: Removal of Flavor from basic schema maintains clean separation

🚀 Architecture & Design

Breaking change handling: Well-documented and justified - minimal real-world impact given fuzzy search was just introduced
Layer separation: Clean implementation across language, schema, ORM, and runtime layers
Future-proofing: Design allows for potential extension to other providers or search types

⚠️ Minor Considerations

  1. Documentation: Consider adding JSDoc examples for the @fuzzy attribute usage patterns
  2. Error context: Runtime error messages could include the model name for better debugging in large schemas
  3. Performance: No performance concerns - validation is compile-time + minimal runtime checks

📊 Security & Performance

  • No security issues: Validation prevents misuse, no injection vectors introduced
  • Performance neutral: Gating is type-level and minimal runtime validation
  • Resource efficiency: Proper constraint validation prevents unnecessary query building

🎯 Overall Assessment

This is an exemplary implementation that demonstrates excellent TypeScript engineering:

  • Type safety: Complete compile-time safety for fuzzy search operations
  • Runtime safety: Clear validation with helpful error messages
  • Clean architecture: Proper separation of concerns across all layers
  • Comprehensive testing: Excellent coverage of both positive and negative cases

Recommendation: ✅ APPROVE

This PR successfully implements field-level fuzzy search gating with robust type safety, comprehensive validation, and excellent test coverage. The breaking change is well-justified and the implementation follows ZenStack's architectural patterns perfectly.


Todo List:

  • Read and analyze changed files across all layers
  • Evaluate language layer implementation (attribute definition, validation)
  • Review schema and code generation changes
  • Assess ORM type system and runtime validation
  • Check Zod integration and factory changes
  • Analyze test coverage and quality
  • Review security and performance considerations
  • Provide comprehensive feedback summary

@claude
Copy link
Copy Markdown
Contributor

claude Bot commented May 5, 2026

Claude finished @ymc9's task —— View job


PR Review: Field-level @fuzzy attribute implementation

I've conducted a comprehensive review of this PR that introduces the @fuzzy attribute for field-level fuzzy search gating. This is a well-structured implementation that successfully adds type safety and runtime validation across all layers.

Todo List:

  • Read and analyze changed files across all layers
  • Evaluate language layer implementation (attribute definition, validation)
  • Review schema and code generation changes
  • Assess ORM type system and runtime validation
  • Check Zod integration and factory changes
  • Analyze test coverage and quality
  • Review security and performance considerations
  • Provide comprehensive feedback summary

Strengths

📚 Language Layer Excellence

  • Proper attribute definition: @fuzzy() @@@targetField([StringField]) @@@once correctly constrains to String fields only (stdlib.zmodel:408)
  • PostgreSQL validation: Clean validator implementation (attribute-application-validator.ts:354-364) properly gates usage to PostgreSQL provider
  • Utility refactoring: Smart extraction of getDataSourceProvider to shared utils (utils.ts:178-188) eliminates duplication

🏗️ Schema & Generation Robustness

  • Clean schema extension: Simple fuzzy?: boolean addition to FieldDef (schema.ts:79)
  • Consistent generation: TypeScript generator properly emits fuzzy: true when attribute is present (ts-schema-generator.ts:629-630)

🎯 Type System Precision

  • Type-level gating: AddFuzzyFilterIfSupported elegantly requires GetModelField<...>['fuzzy'] extends true (crud-types.ts:409-425)
  • Clean fuzzy fields extraction: FuzzyFields<> type properly filters to only @fuzzy-annotated String fields (crud-types.ts:943-945)
  • Proper conditional types: All fuzzy functionality is correctly gated on both provider support and field annotation

⚡ Runtime Validation

  • Clear error messages: Runtime validation provides helpful error messages like "field 'notes' is not fuzzy-searchable; add the \@fuzzy` attribute" (base-dialect.ts:945`)
  • Consistent validation: Both fuzzy: filter and _fuzzyRelevance ordering properly validate field annotations (base-dialect.ts:1135-1137)

🔧 Zod Integration

  • Thread-through support: withFuzzy parameter properly flows through the factory (factory.ts:796, 1019)
  • Schema filtering: _fuzzyRelevance.fields enum correctly filters by def.fuzzy === true (factory.ts:1325-1327)
  • Clean registration: Fuzzy suffix properly added to schema names for disambiguation (factory.ts:1055-1058)

🧪 Comprehensive Testing

Positive tests: Dedicated fuzzy-search schema with mixed fuzzy/non-fuzzy fields provides excellent coverage
Negative tests: Two new tests properly validate rejection of fuzzy operations on non-@fuzzy fields (fuzzy-search.test.ts:824-839)
Validator coverage: 4 new tests covering all provider combinations and non-String field rejection (attribute-application.test.ts:434-499)
Schema cleanup: Removal of Flavor from basic schema maintains clean separation

🚀 Architecture & Design

Breaking change handling: Well-documented and justified - minimal real-world impact given fuzzy search was just introduced
Layer separation: Clean implementation across language, schema, ORM, and runtime layers
Future-proofing: Design allows for potential extension to other providers or search types

⚠️ Minor Considerations

  1. Documentation: Consider adding JSDoc examples for the @fuzzy attribute usage patterns
  2. Error context: Runtime error messages could include the model name for better debugging in large schemas
  3. Performance: No performance concerns - validation is compile-time + minimal runtime checks

📊 Security & Performance

  • No security issues: Validation prevents misuse, no injection vectors introduced
  • Performance neutral: Gating is type-level and minimal runtime validation
  • Resource efficiency: Proper constraint validation prevents unnecessary query building

🎯 Overall Assessment

This is an exemplary implementation that demonstrates excellent TypeScript engineering:

  • Type safety: Complete compile-time safety for fuzzy search operations
  • Runtime safety: Clear validation with helpful error messages
  • Clean architecture: Proper separation of concerns across all layers
  • Comprehensive testing: Excellent coverage of both positive and negative cases

Recommendation: ✅ APPROVE

This PR successfully implements field-level fuzzy search gating with robust type safety, comprehensive validation, and excellent test coverage. The breaking change is well-justified and the implementation follows ZenStack's architectural patterns perfectly.


@ymc9 ymc9 merged commit 8ddbfde into dev May 5, 2026
8 checks passed
@ymc9 ymc9 deleted the feat/fuzzy-attribute branch May 5, 2026 19:41
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