@@ -718,6 +718,8 @@ export class InMemoryDriver implements DriverInterface {
718718 */
719719 private normalizeFilterCondition ( filter : Record < string , any > ) : Record < string , any > {
720720 const result : Record < string , any > = { } ;
721+ const extraAndConditions : Record < string , any > [ ] = [ ] ;
722+
721723 for ( const key of Object . keys ( filter ) ) {
722724 const value = filter [ key ] ;
723725 // Recurse into logical operators
@@ -740,33 +742,63 @@ export class InMemoryDriver implements DriverInterface {
740742 }
741743 // Field-level: value may be primitive (implicit eq) or operator object
742744 if ( value && typeof value === 'object' && ! Array . isArray ( value ) && ! ( value instanceof Date ) && ! ( value instanceof RegExp ) ) {
743- result [ key ] = this . normalizeFieldOperators ( value ) ;
745+ const normalized = this . normalizeFieldOperators ( value ) ;
746+ // Handle multiple regex conditions on the same field (e.g. $startsWith + $endsWith)
747+ if ( normalized . __$andRegex ) {
748+ const regexConditions : Record < string , any > [ ] = normalized . __$andRegex ;
749+ delete normalized . __$andRegex ;
750+ // Each regex becomes its own { field: { $regex: ... } } inside $and
751+ for ( const rc of regexConditions ) {
752+ extraAndConditions . push ( { [ key ] : { ...normalized , ...rc } } ) ;
753+ }
754+ } else {
755+ result [ key ] = normalized ;
756+ }
744757 } else {
745758 result [ key ] = value ;
746759 }
747760 }
761+
762+ // Merge extra $and conditions from multi-regex fields
763+ if ( extraAndConditions . length > 0 ) {
764+ const existing = result . $and ;
765+ const andArray = Array . isArray ( existing ) ? existing : [ ] ;
766+ // Include the rest of result as a condition too
767+ if ( Object . keys ( result ) . filter ( k => k !== '$and' ) . length > 0 ) {
768+ const rest = { ...result } ;
769+ delete rest . $and ;
770+ andArray . push ( rest ) ;
771+ }
772+ andArray . push ( ...extraAndConditions ) ;
773+ return { $and : andArray } ;
774+ }
775+
748776 return result ;
749777 }
750778
751779 /**
752780 * Convert non-standard field operators to Mingo-compatible format.
781+ * When multiple regex-producing operators appear on the same field
782+ * (e.g. $startsWith + $endsWith), they are combined via $and.
753783 */
754784 private normalizeFieldOperators ( ops : Record < string , any > ) : Record < string , any > {
755785 const result : Record < string , any > = { } ;
786+ const regexConditions : Record < string , any > [ ] = [ ] ;
787+
756788 for ( const op of Object . keys ( ops ) ) {
757789 const val = ops [ op ] ;
758790 switch ( op ) {
759791 case '$contains' :
760- result . $regex = new RegExp ( this . escapeRegex ( val ) , 'i' ) ;
792+ regexConditions . push ( { $regex : new RegExp ( this . escapeRegex ( val ) , 'i' ) } ) ;
761793 break ;
762794 case '$notContains' :
763795 result . $not = { $regex : new RegExp ( this . escapeRegex ( val ) , 'i' ) } ;
764796 break ;
765797 case '$startsWith' :
766- result . $regex = new RegExp ( `^${ this . escapeRegex ( val ) } ` , 'i' ) ;
798+ regexConditions . push ( { $regex : new RegExp ( `^${ this . escapeRegex ( val ) } ` , 'i' ) } ) ;
767799 break ;
768800 case '$endsWith' :
769- result . $regex = new RegExp ( `${ this . escapeRegex ( val ) } $` , 'i' ) ;
801+ regexConditions . push ( { $regex : new RegExp ( `${ this . escapeRegex ( val ) } $` , 'i' ) } ) ;
770802 break ;
771803 case '$between' :
772804 if ( Array . isArray ( val ) && val . length === 2 ) {
@@ -788,6 +820,19 @@ export class InMemoryDriver implements DriverInterface {
788820 break ;
789821 }
790822 }
823+
824+ // Merge regex conditions: single → inline, multiple → wrap with $and
825+ if ( regexConditions . length === 1 ) {
826+ Object . assign ( result , regexConditions [ 0 ] ) ;
827+ } else if ( regexConditions . length > 1 ) {
828+ // Cannot have multiple $regex on one object; promote to top-level $and
829+ // The caller (normalizeFilterCondition) handles field-level objects,
830+ // so we return a sentinel that the caller will need to unwrap.
831+ // Simpler approach: just assign the first and use $and for the rest isn't
832+ // possible in flat Mingo format. Use __$and sentinel for the caller.
833+ result . __$andRegex = regexConditions ;
834+ }
835+
791836 return result ;
792837 }
793838
0 commit comments