babymongo 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,712 @@
1
+ # BabyMongo
2
+
3
+ A JavaScript implementation of the MongoDB query API with persistent storage using the Origin Private File System (OPFS). BabyMongo runs database operations in a Web Worker (browser) or Worker Thread (Node.js).
4
+
5
+ **Key Features:**
6
+ - 🔄 **MongoDB-Compatible API:** Familiar MongoDB syntax for queries, updates, aggregations, and change streams
7
+ - 💾 **OPFS Persistent Storage:** Automatic persistence using the Origin Private File System (browser) with a Node.js polyfill for development
8
+ - 📦 **Zero Configuration:** Works out of the box in both browser and Node.js environments
9
+ - 🚀 **Web Worker Architecture:** Database operations run in a separate thread, keeping your UI responsive
10
+ - 🔍 **Advanced Features:** Indexes, text search, geospatial queries, aggregation pipelines, and real-time change streams
11
+
12
+ [![Tests](https://github.com/belteshazzar/babymongo/actions/workflows/test.yml/badge.svg)](https://github.com/belteshazzar/babymongo/actions/workflows/test.yml) [![codecov](https://codecov.io/gh/belteshazzar/babymongo/branch/master/graph/badge.svg)](https://codecov.io/gh/belteshazzar/babymongo)
13
+
14
+ ## In Node.js
15
+
16
+ ### Installation
17
+
18
+ `npm install babymongo`
19
+
20
+ ### Usage
21
+
22
+ BabyMongo uses a web worker architecture to keep database operations off the main thread. The `WorkerBridge` manages communication between your application and the database worker:
23
+
24
+ ```javascript
25
+ import { MongoClient, WorkerBridge } from 'babymongo';
26
+
27
+ async function main() {
28
+ // Step 1: Create the worker bridge
29
+ const bridge = await WorkerBridge.create();
30
+
31
+ // Step 2: Connect to database with the bridge
32
+ const client = new MongoClient('mongodb://localhost:27017/myapp', {
33
+ workerBridge: bridge
34
+ });
35
+ await client.connect();
36
+
37
+ const db = client.db('myapp');
38
+
39
+ // Insert documents (async)
40
+ await db.sample.insertOne({ age: 4, legs: 0 });
41
+ await db.sample.insertMany([
42
+ { age: 4, legs: 5 },
43
+ { age: 54, legs: 2 }
44
+ ]);
45
+
46
+ // Query documents with toArray() (async)
47
+ const results = await db.sample.find({ age: 54 }).toArray();
48
+ console.log(results);
49
+
50
+ // Or use async iteration
51
+ for await (const doc of db.sample.find({ legs: 2 })) {
52
+ console.log(doc);
53
+ }
54
+
55
+ // Update (async)
56
+ await db.sample.updateOne({ age: 4 }, { $set: { legs: 4 } });
57
+
58
+ // Delete (async)
59
+ await db.sample.deleteOne({ age: 54 });
60
+
61
+ // Count (async)
62
+ const count = await db.sample.count();
63
+ console.log(`Total documents: ${count}`);
64
+
65
+ // Close connection
66
+ await client.close();
67
+ }
68
+
69
+ main().catch(console.error);
70
+ ```
71
+
72
+ **Why Web Workers?**
73
+ - ✅ Non-blocking operations keep your UI responsive
74
+ - ✅ Efficient isolation of database logic
75
+ - ✅ Better performance for large datasets
76
+ - ✅ Works seamlessly in both browser and Node.js
77
+ - ✅ Uses synchronous file access which is only available in Web Workers.
78
+
79
+ ### Using Indexes
80
+
81
+ Indexes can significantly improve query performance for large collections:
82
+
83
+ ```javascript
84
+ import { MongoClient, WorkerBridge } from 'babymongo';
85
+
86
+ async function main() {
87
+ const bridge = await WorkerBridge.create();
88
+ const client = new MongoClient('mongodb://localhost:27017/myapp', {
89
+ workerBridge: bridge
90
+ });
91
+ await client.connect();
92
+ const db = client.db('myapp');
93
+
94
+ // Insert some data
95
+ await db.users.insertMany([
96
+ { name: "Alice", age: 30, city: "NYC" },
97
+ { name: "Bob", age: 25, city: "LA" },
98
+ { name: "Charlie", age: 30, city: "SF" }
99
+ ]);
100
+
101
+ // Create an index on the age field
102
+ await db.users.createIndex({ age: 1 });
103
+
104
+ // Create a named index on city
105
+ await db.users.createIndex({ city: 1 }, { name: "city_index" });
106
+
107
+ // Create a compound index
108
+ await db.users.createIndex({ age: 1, city: 1 });
109
+
110
+ // List all indexes
111
+ const indexes = await db.users.getIndexes();
112
+
113
+ // Queries will automatically use indexes when possible
114
+ const results = await db.users.find({ age: 30 }).toArray();
115
+
116
+ await client.close();
117
+ }
118
+
119
+ main().catch(console.error);
120
+ ```
121
+
122
+ The query planner automatically uses indexes for simple equality queries. For complex queries, it combines index lookups with full collection scans to ensure complete and correct results.
123
+
124
+ ### Persistent Storage with OPFS
125
+
126
+ BabyMongo uses the **Origin Private File System (OPFS)** for automatic data persistence. OPFS is a modern web standard that provides fast, private storage for web applications.
127
+
128
+ **In the Browser:**
129
+
130
+ Data is automatically persisted to the OPFS when you use the WorkerBridge pattern. No additional configuration needed!
131
+
132
+ ```javascript
133
+ import { MongoClient, WorkerBridge } from 'babymongo';
134
+
135
+ async function main() {
136
+ const bridge = await WorkerBridge.create();
137
+ const client = new MongoClient('mongodb://localhost:27017/myapp', {
138
+ workerBridge: bridge
139
+ });
140
+ await client.connect();
141
+ const db = client.db('myapp');
142
+
143
+ // Data is automatically persisted to OPFS
144
+ await db.users.insertOne({ name: 'Alice', age: 30 });
145
+ await db.users.createIndex({ age: 1 });
146
+
147
+ // On page reload, your data and indexes are automatically restored!
148
+
149
+ await client.close();
150
+ }
151
+
152
+ main().catch(console.error);
153
+ ```
154
+
155
+ **In Node.js:**
156
+
157
+ BabyMongo includes the [node-opfs](https://github.com/belteshazzar/node-opfs) polyfill, which implements the OPFS API using the file system. Data is stored in a `.opfs` directory in your project root.
158
+
159
+ ```javascript
160
+ // Same code works in Node.js!
161
+ import { MongoClient, WorkerBridge } from 'babymongo';
162
+
163
+ async function main() {
164
+ const bridge = await WorkerBridge.create();
165
+ const client = new MongoClient('mongodb://localhost:27017/myapp', {
166
+ workerBridge: bridge
167
+ });
168
+ await client.connect();
169
+ const db = client.db('myapp');
170
+
171
+ // Data is persisted to .opfs/babymongo/myapp/
172
+ await db.products.insertMany([
173
+ { name: 'Widget', price: 10.99 },
174
+ { name: 'Gadget', price: 24.99 }
175
+ ]);
176
+
177
+ await client.close();
178
+ }
179
+
180
+ main().catch(console.error);
181
+ ```
182
+
183
+ **Storage Location:**
184
+ - **Browser:** Data stored in OPFS (private to your origin, not accessible via DevTools)
185
+ - **Node.js:** Data stored in `.opfs/babymongo/{database-name}/{collection-name}/` directory
186
+ - **Format:** Binary JSON (BJSON) with B+ tree indexing for efficient queries
187
+
188
+ **Benefits of OPFS:**
189
+ - ✅ **Fast:** Synchronous file access in workers for better performance
190
+ - ✅ **Private:** Data is isolated and secure
191
+ - ✅ **Automatic:** No manual save/load calls needed
192
+ - ✅ **Cross-platform:** Works identically in browser and Node.js (via polyfill)
193
+ - ✅ **Versioned:** Supports compaction and versioning to prevent data corruption
194
+
195
+ ### Aggregation Pipelines
196
+
197
+ BabyMongo supports MongoDB's powerful aggregation framework for data transformation and analysis:
198
+
199
+ ```javascript
200
+ import { MongoClient, WorkerBridge } from 'babymongo';
201
+
202
+ async function main() {
203
+ const bridge = await WorkerBridge.create();
204
+ const client = new MongoClient('mongodb://localhost:27017/myapp', {
205
+ workerBridge: bridge
206
+ });
207
+ await client.connect();
208
+ const db = client.db('myapp');
209
+
210
+ // Sample data
211
+ await db.sales.insertMany([
212
+ { product: 'Widget', price: 10, quantity: 5, category: 'Tools' },
213
+ { product: 'Gadget', price: 20, quantity: 3, category: 'Electronics' },
214
+ { product: 'Doohickey', price: 15, quantity: 2, category: 'Tools' }
215
+ ]);
216
+
217
+ // Aggregation pipeline
218
+ const results = await db.sales.aggregate([
219
+ { $match: { category: 'Tools' } },
220
+ { $addFields: { total: { $multiply: ['$price', '$quantity'] } } },
221
+ { $group: {
222
+ _id: '$category',
223
+ totalSales: { $sum: '$total' },
224
+ avgPrice: { $avg: '$price' }
225
+ }},
226
+ { $sort: { totalSales: -1 } }
227
+ ]).toArray();
228
+
229
+ console.log(results);
230
+ // [{ _id: 'Tools', totalSales: 80, avgPrice: 12.5 }]
231
+
232
+ await client.close();
233
+ }
234
+
235
+ main().catch(console.error);
236
+ ```
237
+
238
+ **Supported Stages:**
239
+ - `$match` - Filter documents
240
+ - `$project` - Reshape documents with expressions
241
+ - `$addFields` / `$set` - Add computed fields
242
+ - `$unset` - Remove fields
243
+ - `$group` - Group and aggregate
244
+ - `$sort` - Sort results
245
+ - `$limit` / `$skip` - Pagination
246
+ - `$lookup` - Join collections
247
+ - `$graphLookup` - Recursive joins
248
+ - `$facet` - Multiple pipelines
249
+ - `$bucket` / `$bucketAuto` - Histogram grouping
250
+ - `$sortByCount` - Group and count
251
+ - `$geoNear` - Geospatial aggregation
252
+ - `$merge` - Write results to collection
253
+
254
+ **Aggregation Expressions:**
255
+ Supports arithmetic (`$add`, `$multiply`), comparison (`$eq`, `$gt`), logical (`$and`, `$or`), string (`$concat`, `$substr`), date (`$year`, `$month`), and array operators (`$size`, `$filter`, `$map`).
256
+
257
+ ### Tests
258
+
259
+ `npm test`
260
+
261
+ ### MongoDB Comparison Testing
262
+
263
+ BabyMongo includes a test harness for comparing behavior against real MongoDB to ensure API compatibility. This helps verify that babymongo behaves identically to MongoDB.
264
+
265
+ **Quick Start:**
266
+
267
+ 1. Start MongoDB (using Docker):
268
+ ```bash
269
+ docker run -d -p 27017:27017 --name mongodb mongo:latest
270
+ ```
271
+
272
+ 2. Install MongoDB driver:
273
+ ```bash
274
+ npm install mongodb
275
+ ```
276
+
277
+ 3. Run comparison tests:
278
+ ```bash
279
+ npm run test:comparison
280
+ ```
281
+
282
+ **What's tested:**
283
+ - ✅ CRUD operations (insert, find, update, delete)
284
+ - ✅ Query operators ($gt, $lt, $in, $and, $or, etc.)
285
+ - ✅ Update operators ($set, $inc, $push, $unset, etc.)
286
+ - ✅ Aggregation pipelines ($match, $group, $sort, $project, etc.)
287
+ - ✅ Complex queries (nested fields, arrays, $elemMatch, etc.)
288
+
289
+ For detailed documentation, see:
290
+ - [MongoDB Comparison Quick Start](docs/MONGODB_COMPARISON_QUICKSTART.md)
291
+ - [Complete Documentation](docs/MONGODB_COMPARISON.md)
292
+
293
+ ## In the Browser
294
+
295
+ BabyMongo works seamlessly in modern browsers with automatic OPFS persistence.
296
+
297
+ ### Installation
298
+
299
+ You can use BabyMongo from a CDN or build it yourself:
300
+
301
+ **Option 1: Use pre-built files**
302
+
303
+ Download the built files:
304
+ - Client: https://raw.githubusercontent.com/belteshazzar/babymongo/master/build/babymongo-client.js
305
+ - Worker: https://raw.githubusercontent.com/belteshazzar/babymongo/master/build/babymongo-server-worker.js
306
+
307
+ **Option 2: Build from source**
308
+
309
+ ```bash
310
+ npm install
311
+ npm run build
312
+ ```
313
+
314
+ ### Usage in Browser
315
+
316
+ ```html
317
+ <!DOCTYPE html>
318
+ <html>
319
+ <head>
320
+ <title>BabyMongo Browser Example</title>
321
+ </head>
322
+ <body>
323
+ <h1>BabyMongo in Browser</h1>
324
+ <button id="insert">Insert Data</button>
325
+ <button id="query">Query Data</button>
326
+ <pre id="output"></pre>
327
+
328
+ <script type="module">
329
+ import { MongoClient, WorkerBridge } from './build/babymongo-client.js';
330
+
331
+ async function main() {
332
+ // Create worker bridge
333
+ const bridge = await WorkerBridge.create();
334
+
335
+ // Connect to database
336
+ const client = new MongoClient('mongodb://localhost:27017/browserdb', {
337
+ workerBridge: bridge
338
+ });
339
+ await client.connect();
340
+ const db = client.db('browserdb');
341
+
342
+ const output = document.getElementById('output');
343
+
344
+ // Insert button handler
345
+ document.getElementById('insert').addEventListener('click', async () => {
346
+ await db.items.insertOne({
347
+ name: 'Item ' + Date.now(),
348
+ timestamp: new Date()
349
+ });
350
+ output.textContent = 'Data inserted and persisted to OPFS!';
351
+ });
352
+
353
+ // Query button handler
354
+ document.getElementById('query').addEventListener('click', async () => {
355
+ const items = await db.items.find({}).toArray();
356
+ output.textContent = JSON.stringify(items, null, 2);
357
+ });
358
+ }
359
+
360
+ main().catch(console.error);
361
+ </script>
362
+ </body>
363
+ </html>
364
+ ```
365
+
366
+ ### Browser Features
367
+
368
+ - **Automatic Persistence:** All data is saved to OPFS and survives page reloads
369
+ - **Web Worker:** Database operations run in a worker thread for responsive UI
370
+ - **Full MongoDB API:** Query, update, aggregate, indexes, change streams
371
+ - **Developer-Friendly:** Check `.opfs` directory in Node.js for debugging
372
+
373
+ ## Architecture
374
+
375
+ ### Web Worker Design
376
+
377
+ BabyMongo uses a **dual-thread architecture** for optimal performance:
378
+
379
+ ```
380
+ ┌─────────────────────────────────┐
381
+ │ Main Thread (Your App) │
382
+ │ ┌───────────────────────────┐ │
383
+ │ │ MongoClient │ │
384
+ │ │ ProxyDB │ │
385
+ │ │ ProxyCollection │ │
386
+ │ └───────────┬───────────────┘ │
387
+ │ │ WorkerBridge │
388
+ └──────────────┼──────────────────┘
389
+ │ postMessage()
390
+ ┌──────────────┼──────────────────┐
391
+ │ ▼ │
392
+ │ ┌───────────────────────────┐ │
393
+ │ │ Server │ │
394
+ │ │ DB │ │
395
+ │ │ Collection │ │
396
+ │ │ Indexes │ │
397
+ │ │ OPFS Storage │ │
398
+ │ └───────────────────────────┘ │
399
+ │ Worker Thread (Web Worker/ │
400
+ │ Worker Thread in Node.js) │
401
+ └─────────────────────────────────┘
402
+ ```
403
+
404
+ **How It Works:**
405
+
406
+ 1. **Main Thread:** Your application code runs here. All MongoDB operations are proxied through `WorkerBridge`
407
+ 2. **Worker Thread:** The actual database engine runs here with direct OPFS access
408
+ 3. **Communication:** WorkerBridge serializes/deserializes messages between threads
409
+ 4. **Benefits:**
410
+ - UI stays responsive during heavy database operations
411
+ - OPFS synchronous APIs available in worker context (fast!)
412
+ - Clean separation of concerns
413
+
414
+ ### Storage with OPFS
415
+
416
+ The **Origin Private File System** provides fast, persistent storage:
417
+
418
+ **File Structure:**
419
+ ```
420
+ .opfs/ # Root (Node.js) or OPFS root (Browser)
421
+ └── babymongo/ # Base folder
422
+ └── {database}/ # Database name
423
+ └── {collection}/ # Collection name
424
+ ├── documents.bj # B+ tree of documents
425
+ ├── documents.bj.version.json # Version metadata
426
+ ├── documents.bj.v1 # Old version (during compaction)
427
+ └── {index-name}.bj # Index B+ trees
428
+ ```
429
+
430
+ **Key Features:**
431
+
432
+ - **Binary JSON (BJSON):** Efficient binary format with ObjectId and Date support
433
+ - **B+ Tree Indexes:** Self-balancing trees for fast queries
434
+ - **Versioning:** Safe compaction without data loss
435
+ - **Reference Counting:** Old versions kept until all readers close
436
+ - **Automatic Cleanup:** Old versions deleted when no longer needed
437
+
438
+ **Example Storage:**
439
+
440
+ ```javascript
441
+ // After this code runs:
442
+ const db = client.db('store');
443
+ await db.products.insertOne({ name: 'Widget', price: 10.99 });
444
+ await db.products.createIndex({ name: 1 });
445
+
446
+ // Storage structure:
447
+ // .opfs/babymongo/store/products/documents.bj
448
+ // .opfs/babymongo/store/products/name_1.bj
449
+ ```
450
+
451
+ ### Multiple Clients Sharing a Worker
452
+
453
+ You can create multiple clients that share the same worker:
454
+
455
+ ```javascript
456
+ const bridge = await WorkerBridge.create();
457
+
458
+ const client1 = new MongoClient('mongodb://localhost/app1', { workerBridge: bridge });
459
+ const client2 = new MongoClient('mongodb://localhost/app2', { workerBridge: bridge });
460
+
461
+ await client1.connect();
462
+ await client2.connect();
463
+
464
+ // Both clients use the same worker thread
465
+ // Changes in one client are visible to the other
466
+ ```
467
+
468
+ # API Status
469
+
470
+ The following table summarises the API implementation status.
471
+
472
+ ## Database Methods
473
+
474
+ | Name | Implemented |
475
+ |------------------------------|-----------------|
476
+ | db.cloneCollection | no |
477
+ | db.cloneDatabase | no |
478
+ | db.commandHelp | no |
479
+ | db.copyDatabase | no |
480
+ | db.createCollection | Yes |
481
+ | db.currentOp | N/A |
482
+ | db.dropDatabase | Yes |
483
+ | db.eval | N/A |
484
+ | db.fsyncLock | N/A |
485
+ | db.fsyncUnlock | N/A |
486
+ | db.getCollection | no |
487
+ | db.getCollectionInfos | no |
488
+ | db.getCollectionNames | Yes |
489
+ | db.getLastError | no |
490
+ | db.getLastErrorObj | no |
491
+ | db.getLogComponents | N/A |
492
+ | db.getMongo | N/A |
493
+ | db.getName | no |
494
+ | db.getPrevError | no |
495
+ | db.getProfilingLevel | N/A |
496
+ | db.getProfilingStatus | N/A |
497
+ | db.getReplicationInfo | N/A |
498
+ | db.getSiblingDB | N/A |
499
+ | db.help | Yes |
500
+ | db.hostInfo | N/A |
501
+ | db.isMaster | N/A |
502
+ | db.killOp | N/A |
503
+ | db.listCommands | N/A |
504
+ | db.loadServerScripts | N/A |
505
+ | db.printCollectionStats | N/A |
506
+ | db.printReplicationInfo | N/A |
507
+ | db.printShardingStatus | N/A |
508
+ | db.printSlaveReplicationInfo | N/A |
509
+ | db.repairDatabase | N/A |
510
+ | db.resetError | N/A |
511
+ | db.runCommand | N/A |
512
+ | db.serverBuildInfo | N/A |
513
+ | db.serverCmdLineOpts | N/A |
514
+ | db.serverStatus | N/A |
515
+ | db.setLogLevel | N/A |
516
+ | db.setProfilingLevel | N/A |
517
+ | db.shutdownServer | N/A |
518
+ | db.stats | no |
519
+ | db.version | no |
520
+ | db.upgradeCheck | N/A |
521
+
522
+ ## Collection Methods
523
+
524
+ | Name | Implemented |
525
+ |------------------------------------|-------------|
526
+ | db.collection.aggregate | yes |
527
+ | db.collection.bulkWrite | no |
528
+ | db.collection.count | yes |
529
+ | db.collection.copyTo | yes |
530
+ | db.collection.createIndex | yes |
531
+ | db.collection.dataSize | no |
532
+ | db.collection.deleteOne | yes |
533
+ | db.collection.deleteMany | yes |
534
+ | db.collection.distinct | yes |
535
+ | db.collection.drop | yes |
536
+ | db.collection.dropIndex | no |
537
+ | db.collection.dropIndexes | no |
538
+ | db.collection.ensureIndex | no |
539
+ | db.collection.explain | no |
540
+ | db.collection.find | yes |
541
+ | db.collection.findAndModify | no |
542
+ | db.collection.findOne | yes |
543
+ | db.collection.findOneAndDelete | yes |
544
+ | db.collection.findOneAndReplace | yes |
545
+ | db.collection.findOneAndUpdate | yes |
546
+ | db.collection.getIndexes | yes |
547
+ | db.collection.getShardDistribution | N/A |
548
+ | db.collection.getShardVersion | N/A |
549
+ | db.collection.group | no |
550
+ | db.collection.insert | yes |
551
+ | db.collection.insertOne | yes |
552
+ | db.collection.insertMany | yes |
553
+ | db.collection.isCapped | no |
554
+ | db.collection.mapReduce | no |
555
+ | db.collection.reIndex | no |
556
+ | db.collection.replaceOne | yes |
557
+ | db.collection.remove | yes |
558
+ | db.collection.renameCollection | no |
559
+ | db.collection.save | no |
560
+ | db.collection.stats | no |
561
+ | db.collection.storageSize | no |
562
+ | db.collection.totalSize | no |
563
+ | db.collection.totalIndexSize | no |
564
+ | db.collection.update | yes |
565
+ | db.collection.updateOne | yes |
566
+ | db.collection.updateMany | yes |
567
+ | db.collection.validate | no |
568
+ | db.collection.watch | yes |
569
+
570
+ ## Change Streams
571
+
572
+ **NEW:** BabyMongo supports change streams for reactive programming! Watch for real-time data changes at the collection, database, or client level.
573
+
574
+ ```javascript
575
+ import { MongoClient, WorkerBridge } from 'babymongo';
576
+
577
+ async function main() {
578
+ const bridge = await WorkerBridge.create();
579
+ const client = new MongoClient('mongodb://localhost:27017/myapp', {
580
+ workerBridge: bridge
581
+ });
582
+ await client.connect();
583
+ const db = client.db('myapp');
584
+ const collection = db.collection('users');
585
+
586
+ // Watch for changes to a collection (synchronous, returns immediately)
587
+ const changeStream = collection.watch();
588
+
589
+ changeStream.on('change', (change) => {
590
+ console.log('Change detected:', change.operationType);
591
+ console.log('Document:', change.fullDocument);
592
+ });
593
+
594
+ // Make changes - they'll trigger the change stream
595
+ await collection.insertOne({ name: 'Alice', age: 30 });
596
+ await collection.updateOne({ name: 'Alice' }, { $set: { age: 31 } });
597
+ await collection.deleteOne({ name: 'Alice' });
598
+
599
+ // Filter changes using aggregation pipelines (also synchronous)
600
+ const filtered = collection.watch([
601
+ { $match: { 'fullDocument.age': { $gte: 30 } } }
602
+ ]);
603
+
604
+ // Watch at database level (async, requires await)
605
+ const dbStream = await db.watch();
606
+
607
+ // Close when done
608
+ changeStream.close();
609
+
610
+ await client.close();
611
+ }
612
+
613
+ main().catch(console.error);
614
+ ```
615
+
616
+ See [CHANGE-STREAMS.md](CHANGE-STREAMS.md) for complete documentation, examples, and browser reactivity patterns.
617
+
618
+ ## Filtered Positional Operator with arrayFilters
619
+
620
+ **NEW:** BabyMongo supports the filtered positional operator `$[<identifier>]` with `arrayFilters`, allowing you to update specific array elements that match filter conditions!
621
+
622
+ ```javascript
623
+ import { MongoClient, WorkerBridge } from 'babymongo';
624
+
625
+ async function main() {
626
+ const bridge = await WorkerBridge.create();
627
+ const client = new MongoClient('mongodb://localhost:27017/myapp', {
628
+ workerBridge: bridge
629
+ });
630
+ await client.connect();
631
+ const db = client.db('myapp');
632
+
633
+ // Update items with quantity <= 5
634
+ await db.products.insertOne({
635
+ _id: 1,
636
+ items: [
637
+ { name: 'apple', quantity: 5 },
638
+ { name: 'banana', quantity: 0 },
639
+ { name: 'orange', quantity: 10 }
640
+ ]
641
+ });
642
+
643
+ await db.products.updateOne(
644
+ { _id: 1 },
645
+ { $set: { 'items.$[elem].quantity': 100 } },
646
+ { arrayFilters: [{ 'elem.quantity': { $lte: 5 } }] }
647
+ );
648
+ // Result: apple and banana quantity set to 100, orange remains 10
649
+
650
+ // Update simple arrays
651
+ await db.scores.updateOne(
652
+ { _id: 1 },
653
+ { $set: { 'scores.$[score]': 90 } },
654
+ { arrayFilters: [{ 'score': { $lt: 90 } }] }
655
+ );
656
+
657
+ // Nested arrays with multiple filters
658
+ await db.students.updateOne(
659
+ { _id: 1 },
660
+ { $inc: { 'students.$[student].grades.$[grade].score': 5 } },
661
+ {
662
+ arrayFilters: [
663
+ { 'student.name': 'Alice' },
664
+ { 'grade.score': { $lt: 90 } }
665
+ ]
666
+ }
667
+ );
668
+
669
+ await client.close();
670
+ }
671
+
672
+ main().catch(console.error);
673
+ ```
674
+
675
+ See [docs/ARRAY-FILTERS.md](docs/ARRAY-FILTERS.md) for complete documentation and examples.
676
+
677
+ ## Cursor Methods
678
+
679
+ | Name | Implemented |
680
+ |-------------------------|-----------------|
681
+ | cursor.batchSize | N/A |
682
+ | cursor.close | N/A |
683
+ | cursor.comment | no |
684
+ | cursor.count | yes |
685
+ | cursor.explain | N/A |
686
+ | cursor.forEach | yes |
687
+ | cursor.hasNext | yes |
688
+ | cursor.hint | N/A |
689
+ | cursor.itcount | no |
690
+ | cursor.limit | yes |
691
+ | cursor.map | yes |
692
+ | cursor.maxScan | N/A |
693
+ | cursor.maxTimeMS | N/A |
694
+ | cursor.max | no |
695
+ | cursor.min | no |
696
+ | cursor.next | yes |
697
+ | cursor.noCursorTimeout | N/A |
698
+ | cursor.objsLeftInBatch | N/A |
699
+ | cursor.pretty | no |
700
+ | cursor.readConcern | N/A |
701
+ | cursor.readPref | N/A |
702
+ | cursor.returnKey | N/A |
703
+ | cursor.showRecordId | N/A |
704
+ | cursor.size | no |
705
+ | cursor.skip | yes |
706
+ | cursor.snapshot | no |
707
+ | cursor.sort | yes |
708
+ | cursor.tailable | no |
709
+ | cursor.toArray | yes |
710
+ | cursor.next() | yes |
711
+
712
+