@@ -174,24 +174,64 @@ class FileStore extends StoreAdapter {
174174 }
175175
176176 /**
177- * Check if a document matches the query
177+ * Deep equality, mirroring NeDB's areThingsEqual: primitives by ===, Dates by
178+ * timestamp, arrays and plain objects by recursive value comparison. An array
179+ * never equals a non-array.
180+ */
181+ deepEqual ( a , b ) {
182+ if ( a === b ) return true
183+ if ( a === null || b === null || typeof a !== 'object' || typeof b !== 'object' ) return false
184+
185+ const aIsArray = Array . isArray ( a )
186+ const bIsArray = Array . isArray ( b )
187+ if ( aIsArray !== bIsArray ) return false
188+
189+ if ( a instanceof Date || b instanceof Date ) {
190+ return a instanceof Date && b instanceof Date && a . getTime ( ) === b . getTime ( )
191+ }
192+
193+ const aKeys = Object . keys ( a )
194+ const bKeys = Object . keys ( b )
195+ if ( aKeys . length !== bKeys . length ) return false
196+ for ( const k of aKeys ) {
197+ if ( ! Object . prototype . hasOwnProperty . call ( b , k ) ) return false
198+ if ( ! this . deepEqual ( a [ k ] , b [ k ] ) ) return false
199+ }
200+ return true
201+ }
202+
203+ /**
204+ * Check if a document matches the query (NeDB-compatible semantics).
205+ * An object query value is treated as an operator object only when every key
206+ * starts with `$`; otherwise (plain object or array) it is deep-compared, so
207+ * `{ config: { mode: 'x' } }` or `{ tags: ['a'] }` does not silently match all
208+ * records. A RegExp query value is matched like `$regex`.
178209 */
179210 matchesQuery ( doc , query ) {
180211 if ( ! query || Object . keys ( query ) . length === 0 ) {
181212 return true
182213 }
183214
184215 for ( const [ key , value ] of Object . entries ( query ) ) {
185- if ( typeof value === 'object' && value !== null ) {
186- if ( value . $regex && ! value . $regex . test ( doc [ key ] ) ) return false
187- if ( value . $ne !== undefined && doc [ key ] === value . $ne ) return false
188- if ( value . $in !== undefined && ! value . $in . includes ( doc [ key ] ) ) return false
189- if ( value . $nin !== undefined && value . $nin . includes ( doc [ key ] ) ) return false
190- if ( value . $gt !== undefined && ! ( doc [ key ] > value . $gt ) ) return false
191- if ( value . $gte !== undefined && ! ( doc [ key ] >= value . $gte ) ) return false
192- if ( value . $lt !== undefined && ! ( doc [ key ] < value . $lt ) ) return false
193- if ( value . $lte !== undefined && ! ( doc [ key ] <= value . $lte ) ) return false
194-
216+ if ( value !== null && typeof value === 'object' && ! ( value instanceof RegExp ) ) {
217+ const keys = Object . keys ( value )
218+ const isOperatorObject = keys . length > 0 && keys . every ( ( k ) => k . startsWith ( '$' ) )
219+
220+ if ( isOperatorObject ) {
221+ if ( value . $regex && ! value . $regex . test ( doc [ key ] ) ) return false
222+ if ( value . $ne !== undefined && doc [ key ] === value . $ne ) return false
223+ if ( value . $in !== undefined && ! value . $in . includes ( doc [ key ] ) ) return false
224+ if ( value . $nin !== undefined && value . $nin . includes ( doc [ key ] ) ) return false
225+ if ( value . $gt !== undefined && ! ( doc [ key ] > value . $gt ) ) return false
226+ if ( value . $gte !== undefined && ! ( doc [ key ] >= value . $gte ) ) return false
227+ if ( value . $lt !== undefined && ! ( doc [ key ] < value . $lt ) ) return false
228+ if ( value . $lte !== undefined && ! ( doc [ key ] <= value . $lte ) ) return false
229+ } else if ( ! this . deepEqual ( doc [ key ] , value ) ) {
230+ // Plain object or array query value: deep-compare (NeDB areThingsEqual)
231+ return false
232+ }
233+ } else if ( value instanceof RegExp ) {
234+ if ( ! value . test ( doc [ key ] ) ) return false
195235 } else if ( doc [ key ] !== value ) {
196236 // Simple equality check
197237 return false
0 commit comments