@@ -559,6 +559,17 @@ export class InputValidator<Schema extends SchemaDef> {
559559 ) : ZodType {
560560 const modelDef = requireModel ( this . schema , model ) ;
561561
562+ // unique field used in unique filters bypass filter slicing
563+ const uniqueFieldNames = unique
564+ ? getUniqueFields ( this . schema , model )
565+ . filter (
566+ ( uf ) : uf is { name : string ; def : FieldDef } =>
567+ // single-field unique
568+ 'def' in uf ,
569+ )
570+ . map ( ( uf ) => uf . name )
571+ : undefined ;
572+
562573 const fields : Record < string , any > = { } ;
563574 for ( const field of Object . keys ( modelDef . fields ) ) {
564575 const fieldDef = requireField ( this . schema , model , field ) ;
@@ -602,11 +613,13 @@ export class InputValidator<Schema extends SchemaDef> {
602613 }
603614 }
604615 } else {
616+ const ignoreSlicing = ! ! uniqueFieldNames ?. includes ( field ) ;
617+
605618 const enumDef = getEnum ( this . schema , fieldDef . type ) ;
606619 if ( enumDef ) {
607620 // enum
608621 if ( Object . keys ( enumDef . values ) . length > 0 ) {
609- fieldSchema = this . makeEnumFilterSchema ( model , fieldDef , withAggregations ) ;
622+ fieldSchema = this . makeEnumFilterSchema ( model , fieldDef , withAggregations , ignoreSlicing ) ;
610623 }
611624 } else if ( fieldDef . array ) {
612625 // array field
@@ -615,7 +628,7 @@ export class InputValidator<Schema extends SchemaDef> {
615628 fieldSchema = this . makeTypedJsonFilterSchema ( model , fieldDef ) ;
616629 } else {
617630 // primitive field
618- fieldSchema = this . makePrimitiveFilterSchema ( model , fieldDef , withAggregations ) ;
631+ fieldSchema = this . makePrimitiveFilterSchema ( model , fieldDef , withAggregations , ignoreSlicing ) ;
619632 }
620633 }
621634
@@ -626,6 +639,7 @@ export class InputValidator<Schema extends SchemaDef> {
626639
627640 if ( unique ) {
628641 // add compound unique fields, e.g. `{ id1_id2: { id1: 1, id2: 1 } }`
642+ // compound-field filters are not affected by slicing
629643 const uniqueFields = getUniqueFields ( this . schema , model ) ;
630644 for ( const uniqueField of uniqueFields ) {
631645 if ( 'defs' in uniqueField ) {
@@ -639,13 +653,12 @@ export class InputValidator<Schema extends SchemaDef> {
639653 if ( enumDef ) {
640654 // enum
641655 if ( Object . keys ( enumDef . values ) . length > 0 ) {
642- fieldSchema = this . makeEnumFilterSchema ( model , def , false ) ;
656+ fieldSchema = this . makeEnumFilterSchema ( model , def , false , true ) ;
643657 } else {
644658 fieldSchema = z . never ( ) ;
645659 }
646660 } else {
647- // regular field
648- fieldSchema = this . makePrimitiveFilterSchema ( model , def , false ) ;
661+ fieldSchema = this . makePrimitiveFilterSchema ( model , def , false , true ) ;
649662 }
650663 return [ key , fieldSchema ] ;
651664 } ) ,
@@ -776,7 +789,12 @@ export class InputValidator<Schema extends SchemaDef> {
776789 }
777790
778791 @cache ( )
779- private makeEnumFilterSchema ( model : string , fieldInfo : FieldInfo , withAggregations : boolean ) {
792+ private makeEnumFilterSchema (
793+ model : string ,
794+ fieldInfo : FieldInfo ,
795+ withAggregations : boolean ,
796+ ignoreSlicing : boolean = false ,
797+ ) {
780798 const enumName = fieldInfo . type ;
781799 const optional = ! ! fieldInfo . optional ;
782800 const array = ! ! fieldInfo . array ;
@@ -787,7 +805,7 @@ export class InputValidator<Schema extends SchemaDef> {
787805 if ( array ) {
788806 return this . internalMakeArrayFilterSchema ( model , fieldInfo . name , baseSchema ) ;
789807 }
790- const allowedFilterKinds = this . getEffectiveFilterKinds ( model , fieldInfo . name ) ;
808+ const allowedFilterKinds = ignoreSlicing ? undefined : this . getEffectiveFilterKinds ( model , fieldInfo . name ) ;
791809 const components = this . makeCommonPrimitiveFilterComponents (
792810 baseSchema ,
793811 optional ,
@@ -797,12 +815,7 @@ export class InputValidator<Schema extends SchemaDef> {
797815 allowedFilterKinds ,
798816 ) ;
799817
800- // If all filter operators are excluded, return z.never()
801- if ( Object . keys ( components ) . length === 0 ) {
802- return z . never ( ) ;
803- }
804-
805- return z . union ( [ this . nullableIf ( baseSchema , optional ) , z . strictObject ( components ) ] ) ;
818+ return this . createUnionFilterSchema ( baseSchema , optional , components , allowedFilterKinds ) ;
806819 }
807820
808821 @cache ( )
@@ -831,8 +844,13 @@ export class InputValidator<Schema extends SchemaDef> {
831844 }
832845
833846 @cache ( )
834- private makePrimitiveFilterSchema ( model : string , fieldInfo : FieldInfo , withAggregations : boolean ) {
835- const allowedFilterKinds = this . getEffectiveFilterKinds ( model , fieldInfo . name ) ;
847+ private makePrimitiveFilterSchema (
848+ model : string ,
849+ fieldInfo : FieldInfo ,
850+ withAggregations : boolean ,
851+ ignoreSlicing = false ,
852+ ) {
853+ const allowedFilterKinds = ignoreSlicing ? undefined : this . getEffectiveFilterKinds ( model , fieldInfo . name ) ;
836854 const type = fieldInfo . type as BuiltinType ;
837855 const optional = ! ! fieldInfo . optional ;
838856 return match ( type )
@@ -935,12 +953,7 @@ export class InputValidator<Schema extends SchemaDef> {
935953 allowedFilterKinds ,
936954 ) ;
937955
938- // If all filter operators are excluded, return z.never()
939- if ( Object . keys ( components ) . length === 0 ) {
940- return z . never ( ) ;
941- }
942-
943- return z . union ( [ this . nullableIf ( z . boolean ( ) , optional ) , z . strictObject ( components ) ] ) ;
956+ return this . createUnionFilterSchema ( z . boolean ( ) , optional , components , allowedFilterKinds ) ;
944957 }
945958
946959 @cache ( )
@@ -959,12 +972,7 @@ export class InputValidator<Schema extends SchemaDef> {
959972 allowedFilterKinds ,
960973 ) ;
961974
962- // If all filter operators are excluded, return z.never()
963- if ( Object . keys ( components ) . length === 0 ) {
964- return z . never ( ) ;
965- }
966-
967- return z . union ( [ this . nullableIf ( baseSchema , optional ) , z . strictObject ( components ) ] ) ;
975+ return this . createUnionFilterSchema ( baseSchema , optional , components , allowedFilterKinds ) ;
968976 }
969977
970978 private makeCommonPrimitiveFilterComponents (
@@ -1022,12 +1030,7 @@ export class InputValidator<Schema extends SchemaDef> {
10221030 allowedFilterKinds ,
10231031 ) ;
10241032
1025- // If all filter operators are excluded, return z.never()
1026- if ( Object . keys ( components ) . length === 0 ) {
1027- return z . never ( ) ;
1028- }
1029-
1030- return z . union ( [ this . nullableIf ( baseSchema , optional ) , z . strictObject ( components ) ] ) ;
1033+ return this . createUnionFilterSchema ( baseSchema , optional , components , allowedFilterKinds ) ;
10311034 }
10321035
10331036 private makeNumberFilterSchema (
@@ -1078,12 +1081,7 @@ export class InputValidator<Schema extends SchemaDef> {
10781081 ...filteredStringOperators ,
10791082 } ;
10801083
1081- // If all filter operators are excluded, return z.never()
1082- if ( Object . keys ( allComponents ) . length === 0 ) {
1083- return z . never ( ) ;
1084- }
1085-
1086- return z . union ( [ this . nullableIf ( z . string ( ) , optional ) , z . strictObject ( allComponents ) ] ) ;
1084+ return this . createUnionFilterSchema ( z . string ( ) , optional , allComponents , allowedFilterKinds ) ;
10871085 }
10881086
10891087 private makeStringModeSchema ( ) {
@@ -2173,6 +2171,31 @@ export class InputValidator<Schema extends SchemaDef> {
21732171 ) as Partial < T > ;
21742172 }
21752173
2174+ private createUnionFilterSchema (
2175+ valueSchema : ZodType ,
2176+ optional : boolean ,
2177+ components : Record < string , ZodType > ,
2178+ allowedFilterKinds : Set < string > | undefined ,
2179+ ) {
2180+ // If all filter operators are excluded
2181+ if ( Object . keys ( components ) . length === 0 ) {
2182+ // if equality filters are allowed, allow direct value
2183+ if ( ! allowedFilterKinds || allowedFilterKinds . has ( 'Equality' ) ) {
2184+ return this . nullableIf ( valueSchema , optional ) ;
2185+ }
2186+ // otherwise nothing is allowed
2187+ return z . never ( ) ;
2188+ }
2189+
2190+ if ( ! allowedFilterKinds || allowedFilterKinds . has ( 'Equality' ) ) {
2191+ // direct value or filter operators
2192+ return z . union ( [ this . nullableIf ( valueSchema , optional ) , z . strictObject ( components ) ] ) ;
2193+ } else {
2194+ // filter operators
2195+ return z . strictObject ( components ) ;
2196+ }
2197+ }
2198+
21762199 /**
21772200 * Checks if a model is included in the slicing configuration.
21782201 * Returns true if the model is allowed, false if it's excluded.
0 commit comments