@@ -321,323 +321,6 @@ export class JsonicAdapter extends DatabaseAdapter {
321321 } ;
322322 }
323323
324- createMockCollection ( name ) {
325- const self = this ;
326- return {
327- insertOne : async ( doc ) => {
328- // Performance optimization: Simple counter instead of Date.now() + Math.random()
329- const id = ++ self . idCounter ;
330- // Avoid mutation: create new object with _id
331- const enrichedDoc = { ...doc , _id : id } ;
332- self . documents . set ( id , enrichedDoc ) ;
333- self . operations ++ ;
334- return { insertedId : id } ;
335- } ,
336- insertMany : async ( docs ) => {
337- // Performance optimization: Batch operations
338- const ids = [ ] ;
339- const docsLength = docs . length ;
340-
341- // Pre-allocate IDs for better performance
342- for ( let i = 0 ; i < docsLength ; i ++ ) {
343- const id = ++ self . idCounter ;
344- // Avoid mutation: create new object with _id
345- const enrichedDoc = { ...docs [ i ] , _id : id } ;
346- self . documents . set ( id , enrichedDoc ) ;
347- ids . push ( id ) ;
348- }
349-
350- // Batch increment operations counter (move outside loop)
351- self . operations += docsLength ;
352-
353- return { insertedIds : ids } ;
354- } ,
355- find : ( query ) => {
356- const chainable = {
357- sort : ( ) => chainable ,
358- limit : ( ) => chainable ,
359- skip : ( ) => chainable ,
360- toArray : async ( ) => self . findDocuments ( query )
361- } ;
362- return chainable ;
363- } ,
364- findOne : async ( query ) => {
365- const results = self . findDocuments ( query ) ;
366- return results [ 0 ] || null ;
367- } ,
368- updateOne : async ( query , update ) => {
369- const doc = self . findDocuments ( query ) [ 0 ] ;
370- if ( doc ) {
371- self . applyUpdate ( doc , update ) ;
372- self . operations ++ ;
373- return { modifiedCount : 1 , matchedCount : 1 } ;
374- }
375- return { modifiedCount : 0 , matchedCount : 0 } ;
376- } ,
377- updateMany : async ( query , update ) => {
378- const docs = self . findDocuments ( query ) ;
379- for ( const doc of docs ) {
380- self . applyUpdate ( doc , update ) ;
381- }
382- self . operations += docs . length ;
383- return { modifiedCount : docs . length , matchedCount : docs . length } ;
384- } ,
385- deleteOne : async ( query ) => {
386- const doc = self . findDocuments ( query ) [ 0 ] ;
387- if ( doc && doc . _id ) {
388- self . documents . delete ( doc . _id ) ;
389- self . operations ++ ;
390- return { deletedCount : 1 } ;
391- }
392- return { deletedCount : 0 } ;
393- } ,
394- deleteMany : async ( query ) => {
395- const docs = self . findDocuments ( query ) ;
396- for ( const doc of docs ) {
397- if ( doc . _id ) self . documents . delete ( doc . _id ) ;
398- }
399- self . operations += docs . length ;
400- return { deletedCount : docs . length } ;
401- } ,
402- countDocuments : async ( query ) => {
403- if ( ! query || Object . keys ( query ) . length === 0 ) {
404- return self . documents . size ;
405- }
406- return self . findDocuments ( query ) . length ;
407- } ,
408- createIndex : async ( ) => true ,
409- aggregate : async ( pipeline ) => {
410- return {
411- toArray : async ( ) => self . executeAggregation ( pipeline )
412- } ;
413- } ,
414- distinct : async ( field , query = { } ) => {
415- const docs = self . findDocuments ( query ) ;
416- const values = new Set ( ) ;
417- for ( const doc of docs ) {
418- if ( doc [ field ] !== undefined ) {
419- values . add ( doc [ field ] ) ;
420- }
421- }
422- return Array . from ( values ) ;
423- } ,
424- exists : async ( query ) => {
425- return self . findDocuments ( query ) . length > 0 ;
426- }
427- } ;
428- }
429-
430- findDocuments ( query ) {
431- const results = [ ] ;
432- for ( const doc of this . documents . values ( ) ) {
433- if ( this . matchesQuery ( doc , query ) ) {
434- results . push ( doc ) ;
435- }
436- }
437- return results ;
438- }
439-
440- matchesQuery ( doc , query ) {
441- if ( ! query || Object . keys ( query ) . length === 0 ) return true ;
442-
443- for ( const [ field , condition ] of Object . entries ( query ) ) {
444- const value = doc [ field ] ;
445-
446- if ( typeof condition === 'object' && condition !== null ) {
447- for ( const [ op , opValue ] of Object . entries ( condition ) ) {
448- switch ( op ) {
449- case '$gt' :
450- if ( ! ( value > opValue ) ) return false ;
451- break ;
452- case '$gte' :
453- if ( ! ( value >= opValue ) ) return false ;
454- break ;
455- case '$lt' :
456- if ( ! ( value < opValue ) ) return false ;
457- break ;
458- case '$lte' :
459- if ( ! ( value <= opValue ) ) return false ;
460- break ;
461- case '$ne' :
462- if ( value === opValue ) return false ;
463- break ;
464- case '$in' :
465- if ( ! opValue . includes ( value ) ) return false ;
466- break ;
467- case '$nin' :
468- if ( opValue . includes ( value ) ) return false ;
469- break ;
470- case '$exists' :
471- if ( opValue && ! ( field in doc ) ) return false ;
472- if ( ! opValue && ( field in doc ) ) return false ;
473- break ;
474- }
475- }
476- } else {
477- if ( value !== condition ) return false ;
478- }
479- }
480- return true ;
481- }
482-
483- // Simple aggregation pipeline execution (Phase 2 feature)
484- executeAggregation ( pipeline ) {
485- let result = Array . from ( this . documents . values ( ) ) ;
486-
487- for ( const stage of pipeline ) {
488- const stageName = Object . keys ( stage ) [ 0 ] ;
489- const stageParams = stage [ stageName ] ;
490-
491- switch ( stageName ) {
492- case '$match' :
493- result = result . filter ( doc => this . matchesQuery ( doc , stageParams ) ) ;
494- break ;
495-
496- case '$group' :
497- const groups = { } ;
498- for ( const doc of result ) {
499- const groupKey = this . evaluateExpression ( doc , stageParams . _id ) ;
500- const key = JSON . stringify ( groupKey ) ;
501-
502- if ( ! groups [ key ] ) {
503- groups [ key ] = { _id : groupKey , _docs : [ ] } ;
504- }
505- groups [ key ] . _docs . push ( doc ) ;
506- }
507-
508- // Apply aggregation operators
509- result = Object . values ( groups ) . map ( group => {
510- const output = { _id : group . _id } ;
511- for ( const [ field , expr ] of Object . entries ( stageParams ) ) {
512- if ( field !== '_id' ) {
513- output [ field ] = this . applyAggregationOperator ( group . _docs , expr ) ;
514- }
515- }
516- return output ;
517- } ) ;
518- break ;
519-
520- case '$sort' :
521- result . sort ( ( a , b ) => {
522- for ( const [ field , direction ] of Object . entries ( stageParams ) ) {
523- const aVal = a [ field ] ;
524- const bVal = b [ field ] ;
525- const cmp = aVal < bVal ? - 1 : aVal > bVal ? 1 : 0 ;
526- if ( cmp !== 0 ) return direction > 0 ? cmp : - cmp ;
527- }
528- return 0 ;
529- } ) ;
530- break ;
531-
532- case '$limit' :
533- result = result . slice ( 0 , stageParams ) ;
534- break ;
535-
536- case '$skip' :
537- result = result . slice ( stageParams ) ;
538- break ;
539- }
540- }
541-
542- return result ;
543- }
544-
545- evaluateExpression ( doc , expr ) {
546- if ( typeof expr === 'string' && expr . startsWith ( '$' ) ) {
547- return doc [ expr . substring ( 1 ) ] ;
548- }
549- return expr ;
550- }
551-
552- applyAggregationOperator ( docs , operator ) {
553- if ( typeof operator === 'object' ) {
554- const op = Object . keys ( operator ) [ 0 ] ;
555- const field = operator [ op ] ;
556-
557- switch ( op ) {
558- case '$sum' :
559- if ( field === 1 ) return docs . length ;
560- return docs . reduce ( ( sum , doc ) => sum + ( doc [ field . substring ( 1 ) ] || 0 ) , 0 ) ;
561- case '$avg' :
562- const values = docs . map ( doc => doc [ field . substring ( 1 ) ] || 0 ) ;
563- return values . reduce ( ( a , b ) => a + b , 0 ) / values . length ;
564- case '$max' :
565- return Math . max ( ...docs . map ( doc => doc [ field . substring ( 1 ) ] || 0 ) ) ;
566- case '$min' :
567- return Math . min ( ...docs . map ( doc => doc [ field . substring ( 1 ) ] || 0 ) ) ;
568- case '$count' :
569- return docs . length ;
570- case '$addToSet' :
571- const uniqueValues = new Set ( ) ;
572- for ( const doc of docs ) {
573- uniqueValues . add ( doc [ field . substring ( 1 ) ] ) ;
574- }
575- return Array . from ( uniqueValues ) ;
576- }
577- }
578- return operator ;
579- }
580-
581- // Apply MongoDB-style update operators (Phase 2 feature)
582- applyUpdate ( doc , update ) {
583- // Handle MongoDB update operators
584- if ( update . $set ) {
585- Object . assign ( doc , update . $set ) ;
586- }
587-
588- if ( update . $unset ) {
589- for ( const field of Object . keys ( update . $unset ) ) {
590- delete doc [ field ] ;
591- }
592- }
593-
594- if ( update . $inc ) {
595- for ( const [ field , value ] of Object . entries ( update . $inc ) ) {
596- doc [ field ] = ( doc [ field ] || 0 ) + value ;
597- }
598- }
599-
600- if ( update . $push ) {
601- for ( const [ field , value ] of Object . entries ( update . $push ) ) {
602- if ( ! Array . isArray ( doc [ field ] ) ) {
603- doc [ field ] = [ ] ;
604- }
605- doc [ field ] . push ( value ) ;
606- }
607- }
608-
609- if ( update . $pull ) {
610- for ( const [ field , value ] of Object . entries ( update . $pull ) ) {
611- if ( Array . isArray ( doc [ field ] ) ) {
612- doc [ field ] = doc [ field ] . filter ( item => item !== value ) ;
613- }
614- }
615- }
616-
617- if ( update . $addToSet ) {
618- for ( const [ field , value ] of Object . entries ( update . $addToSet ) ) {
619- if ( ! Array . isArray ( doc [ field ] ) ) {
620- doc [ field ] = [ ] ;
621- }
622- if ( ! doc [ field ] . includes ( value ) ) {
623- doc [ field ] . push ( value ) ;
624- }
625- }
626- }
627-
628- // If no operators, treat as direct replacement
629- if ( ! update . $set && ! update . $unset && ! update . $inc && ! update . $push && ! update . $pull && ! update . $addToSet ) {
630- Object . assign ( doc , update ) ;
631- }
632- }
633-
634- createMockTransaction ( ) {
635- return {
636- commit : async ( ) => { } ,
637- rollback : async ( ) => { }
638- } ;
639- }
640-
641324 async cleanup ( ) {
642325 if ( this . currentTx ) {
643326 await this . currentTx . rollback ( ) ;
0 commit comments