11import { QueryAST , QueryInput } from '@objectstack/spec/data' ;
22import { DriverOptions } from '@objectstack/spec/data' ;
33import { DriverInterface , Logger , createLogger } from '@objectstack/core' ;
4+ import { match , getValueByPath } from './memory-matcher.js' ;
45
56/**
67 * Example: In-Memory Driver
@@ -36,10 +37,10 @@ export class InMemoryDriver implements DriverInterface {
3637 transactions : false ,
3738
3839 // Query Operations
39- queryFilters : false , // TODO: Not implemented - basic find() doesn't handle filters
40+ queryFilters : true , // Implemented via memory-matcher
4041 queryAggregations : false , // TODO: Not implemented - count() only returns total
41- querySorting : false , // TODO: Not implemented - find() doesn't handle sorting
42- queryPagination : true , // Basic pagination via 'limit' is implemented
42+ querySorting : true , // Implemented via JS sort
43+ queryPagination : true , // Implemented
4344 queryWindowFunctions : false , // TODO: Not implemented
4445 querySubqueries : false , // TODO: Not implemented
4546 joins : false , // TODO: Not implemented
@@ -101,13 +102,46 @@ export class InMemoryDriver implements DriverInterface {
101102 this . logger . debug ( 'Find operation' , { object, query } ) ;
102103
103104 const table = this . getTable ( object ) ;
104-
105- // 💡 Naive Implementation
106- let results = [ ...table ] ;
105+ let results = table ;
106+
107+ // 1. Filter
108+ if ( query . where ) {
109+ results = results . filter ( record => match ( record , query . where ) ) ;
110+ }
111+
112+ // 2. Sort
113+ if ( query . orderBy ) {
114+ // Normalize sort to array
115+ const sortFields = Array . isArray ( query . orderBy ) ? query . orderBy : [ query . orderBy ] ;
116+
117+ results . sort ( ( a , b ) => {
118+ for ( const { field, order } of sortFields ) {
119+ const valA = getValueByPath ( a , field ) ;
120+ const valB = getValueByPath ( b , field ) ;
121+
122+ if ( valA === valB ) continue ;
123+
124+ const comparison = valA > valB ? 1 : - 1 ;
125+ return order === 'desc' ? - comparison : comparison ;
126+ }
127+ return 0 ;
128+ } ) ;
129+ }
130+
131+ // 3. Pagination (Offset/Skip)
132+ if ( query . offset ) {
133+ results = results . slice ( query . offset ) ;
134+ } else if ( query . skip ) {
135+ // Alias for offset
136+ results = results . slice ( query . skip ) ;
137+ }
107138
108- // Simple limiting for demonstration
139+ // 4. Pagination (Limit/Top)
109140 if ( query . limit ) {
110141 results = results . slice ( 0 , query . limit ) ;
142+ } else if ( query . top ) {
143+ // Alias for limit
144+ results = results . slice ( 0 , query . top ) ;
111145 }
112146
113147 this . logger . debug ( 'Find completed' , { object, resultCount : results . length } ) ;
@@ -211,7 +245,11 @@ export class InMemoryDriver implements DriverInterface {
211245 }
212246
213247 async count ( object : string , query ?: QueryInput , options ?: DriverOptions ) {
214- const count = this . getTable ( object ) . length ;
248+ let results = this . getTable ( object ) ;
249+ if ( query ?. where ) {
250+ results = results . filter ( record => match ( record , query . where ) ) ;
251+ }
252+ const count = results . length ;
215253 this . logger . debug ( 'Count operation' , { object, count } ) ;
216254 return count ;
217255 }
@@ -226,7 +264,62 @@ export class InMemoryDriver implements DriverInterface {
226264 this . logger . debug ( 'BulkCreate completed' , { object, count : results . length } ) ;
227265 return results ;
228266 }
267+
268+ async updateMany ( object : string , query : QueryInput , data : Record < string , any > , options ?: DriverOptions ) {
269+ this . logger . debug ( 'UpdateMany operation' , { object, query } ) ;
270+
271+ const table = this . getTable ( object ) ;
272+ let targetRecords = table ;
273+
274+ if ( query && query . where ) {
275+ targetRecords = targetRecords . filter ( r => match ( r , query . where ) ) ;
276+ }
277+
278+ const count = targetRecords . length ;
279+
280+ // Update each record
281+ for ( const record of targetRecords ) {
282+ // Find index in original table
283+ const index = table . findIndex ( r => r . id === record . id ) ;
284+ if ( index !== - 1 ) {
285+ const updated = {
286+ ...table [ index ] ,
287+ ...data ,
288+ updated_at : new Date ( )
289+ } ;
290+ table [ index ] = updated ;
291+ }
292+ }
293+
294+ this . logger . debug ( 'UpdateMany completed' , { object, count } ) ;
295+ return { count } ;
296+ }
297+
298+ async deleteMany ( object : string , query : QueryInput , options ?: DriverOptions ) {
299+ this . logger . debug ( 'DeleteMany operation' , { object, query } ) ;
300+
301+ const table = this . getTable ( object ) ;
302+ const initialLength = table . length ;
303+
304+ // Filter IN PLACE or create new array?
305+ // Creating new array is safer for now.
306+
307+ const remaining = table . filter ( r => {
308+ if ( ! query || ! query . where ) return false ; // Delete all? No, standard safety implies explicit empty filter for delete all.
309+ // Wait, normally deleteMany({}) deletes all.
310+ // Let's assume if query passed, use it.
311+ const matches = match ( r , query . where ) ;
312+ return ! matches ; // Keep if it DOES NOT match
313+ } ) ;
314+
315+ this . db [ object ] = remaining ;
316+ const count = initialLength - remaining . length ;
317+
318+ this . logger . debug ( 'DeleteMany completed' , { object, count } ) ;
319+ return { count } ;
320+ }
229321
322+ // Compatibility aliases
230323 async bulkUpdate ( object : string , updates : { id : string | number , data : Record < string , any > } [ ] , options ?: DriverOptions ) {
231324 this . logger . debug ( 'BulkUpdate operation' , { object, count : updates . length } ) ;
232325 const results = await Promise . all ( updates . map ( u => this . update ( object , u . id , u . data , options ) ) ) ;
0 commit comments