mongodb-livedata-server 0.1.3 → 0.1.4
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/dist/livedata_server.d.ts +4 -4
- package/dist/livedata_server.js +11 -11
- package/dist/meteor/binary-heap/max_heap.d.ts +31 -31
- package/dist/meteor/binary-heap/max_heap.js +186 -186
- package/dist/meteor/binary-heap/min_heap.d.ts +6 -6
- package/dist/meteor/binary-heap/min_heap.js +17 -17
- package/dist/meteor/binary-heap/min_max_heap.d.ts +11 -11
- package/dist/meteor/binary-heap/min_max_heap.js +48 -48
- package/dist/meteor/callback-hook/hook.d.ts +11 -11
- package/dist/meteor/callback-hook/hook.js +78 -78
- package/dist/meteor/ddp/crossbar.d.ts +15 -15
- package/dist/meteor/ddp/crossbar.js +136 -136
- package/dist/meteor/ddp/heartbeat.d.ts +19 -19
- package/dist/meteor/ddp/heartbeat.js +77 -77
- package/dist/meteor/ddp/livedata_server.d.ts +141 -142
- package/dist/meteor/ddp/livedata_server.js +403 -403
- package/dist/meteor/ddp/method-invocation.d.ts +35 -35
- package/dist/meteor/ddp/method-invocation.js +72 -72
- package/dist/meteor/ddp/random-stream.d.ts +8 -8
- package/dist/meteor/ddp/random-stream.js +100 -100
- package/dist/meteor/ddp/session-collection-view.d.ts +20 -20
- package/dist/meteor/ddp/session-collection-view.js +106 -106
- package/dist/meteor/ddp/session-document-view.d.ts +8 -8
- package/dist/meteor/ddp/session-document-view.js +82 -82
- package/dist/meteor/ddp/session.d.ts +75 -75
- package/dist/meteor/ddp/session.js +590 -590
- package/dist/meteor/ddp/stream_server.d.ts +20 -21
- package/dist/meteor/ddp/stream_server.js +181 -181
- package/dist/meteor/ddp/subscription.d.ts +94 -94
- package/dist/meteor/ddp/subscription.js +370 -370
- package/dist/meteor/ddp/utils.d.ts +8 -8
- package/dist/meteor/ddp/utils.js +104 -104
- package/dist/meteor/ddp/writefence.d.ts +20 -20
- package/dist/meteor/ddp/writefence.js +111 -111
- package/dist/meteor/diff-sequence/diff.d.ts +17 -17
- package/dist/meteor/diff-sequence/diff.js +257 -257
- package/dist/meteor/ejson/ejson.d.ts +82 -82
- package/dist/meteor/ejson/ejson.js +568 -569
- package/dist/meteor/ejson/stringify.d.ts +2 -2
- package/dist/meteor/ejson/stringify.js +119 -119
- package/dist/meteor/ejson/utils.d.ts +12 -12
- package/dist/meteor/ejson/utils.js +42 -42
- package/dist/meteor/mongo/caching_change_observer.d.ts +16 -16
- package/dist/meteor/mongo/caching_change_observer.js +63 -63
- package/dist/meteor/mongo/doc_fetcher.d.ts +7 -7
- package/dist/meteor/mongo/doc_fetcher.js +53 -53
- package/dist/meteor/mongo/geojson_utils.d.ts +3 -3
- package/dist/meteor/mongo/geojson_utils.js +40 -41
- package/dist/meteor/mongo/live_connection.d.ts +28 -28
- package/dist/meteor/mongo/live_connection.js +264 -264
- package/dist/meteor/mongo/live_cursor.d.ts +25 -25
- package/dist/meteor/mongo/live_cursor.js +60 -60
- package/dist/meteor/mongo/minimongo_common.d.ts +84 -84
- package/dist/meteor/mongo/minimongo_common.js +1998 -1998
- package/dist/meteor/mongo/minimongo_matcher.d.ts +23 -23
- package/dist/meteor/mongo/minimongo_matcher.js +283 -283
- package/dist/meteor/mongo/minimongo_sorter.d.ts +16 -16
- package/dist/meteor/mongo/minimongo_sorter.js +268 -268
- package/dist/meteor/mongo/observe_driver_utils.d.ts +9 -9
- package/dist/meteor/mongo/observe_driver_utils.js +72 -73
- package/dist/meteor/mongo/observe_multiplexer.d.ts +46 -46
- package/dist/meteor/mongo/observe_multiplexer.js +203 -203
- package/dist/meteor/mongo/oplog-observe-driver.d.ts +68 -68
- package/dist/meteor/mongo/oplog-observe-driver.js +918 -918
- package/dist/meteor/mongo/oplog_tailing.d.ts +35 -35
- package/dist/meteor/mongo/oplog_tailing.js +352 -352
- package/dist/meteor/mongo/oplog_v2_converter.d.ts +1 -1
- package/dist/meteor/mongo/oplog_v2_converter.js +125 -126
- package/dist/meteor/mongo/polling_observe_driver.d.ts +30 -30
- package/dist/meteor/mongo/polling_observe_driver.js +216 -221
- package/dist/meteor/mongo/synchronous-cursor.d.ts +17 -17
- package/dist/meteor/mongo/synchronous-cursor.js +261 -261
- package/dist/meteor/mongo/synchronous-queue.d.ts +13 -13
- package/dist/meteor/mongo/synchronous-queue.js +110 -110
- package/dist/meteor/ordered-dict/ordered_dict.d.ts +31 -31
- package/dist/meteor/ordered-dict/ordered_dict.js +198 -198
- package/dist/meteor/random/AbstractRandomGenerator.d.ts +42 -42
- package/dist/meteor/random/AbstractRandomGenerator.js +92 -92
- package/dist/meteor/random/AleaRandomGenerator.d.ts +13 -13
- package/dist/meteor/random/AleaRandomGenerator.js +90 -90
- package/dist/meteor/random/NodeRandomGenerator.d.ts +16 -16
- package/dist/meteor/random/NodeRandomGenerator.js +42 -42
- package/dist/meteor/random/createAleaGenerator.d.ts +2 -2
- package/dist/meteor/random/createAleaGenerator.js +32 -32
- package/dist/meteor/random/createRandom.d.ts +1 -1
- package/dist/meteor/random/createRandom.js +22 -22
- package/dist/meteor/random/main.d.ts +1 -1
- package/dist/meteor/random/main.js +12 -12
- package/dist/meteor/types.d.ts +1 -1
- package/dist/meteor/types.js +2 -2
- package/package.json +5 -5
|
@@ -1,1998 +1,1998 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
exports.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
$
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return value =>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
},
|
|
82
|
-
},
|
|
83
|
-
$
|
|
84
|
-
// {a: [5]} must
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
'
|
|
110
|
-
'
|
|
111
|
-
'
|
|
112
|
-
'
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
$
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
//
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
$
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return
|
|
320
|
-
},
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
if (
|
|
334
|
-
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
//
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
//
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
//
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
}
|
|
578
|
-
//
|
|
579
|
-
//
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
//
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
operand.
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
//
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
function
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
//
|
|
716
|
-
//
|
|
717
|
-
//
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
//
|
|
771
|
-
//
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
//
|
|
781
|
-
//
|
|
782
|
-
//
|
|
783
|
-
//
|
|
784
|
-
//
|
|
785
|
-
//
|
|
786
|
-
//
|
|
787
|
-
//
|
|
788
|
-
//
|
|
789
|
-
//
|
|
790
|
-
//
|
|
791
|
-
//
|
|
792
|
-
//
|
|
793
|
-
//
|
|
794
|
-
//
|
|
795
|
-
//
|
|
796
|
-
//
|
|
797
|
-
//
|
|
798
|
-
//
|
|
799
|
-
//
|
|
800
|
-
//
|
|
801
|
-
//
|
|
802
|
-
//
|
|
803
|
-
//
|
|
804
|
-
//
|
|
805
|
-
//
|
|
806
|
-
//
|
|
807
|
-
//
|
|
808
|
-
//
|
|
809
|
-
//
|
|
810
|
-
//
|
|
811
|
-
//
|
|
812
|
-
//
|
|
813
|
-
//
|
|
814
|
-
//
|
|
815
|
-
//
|
|
816
|
-
//
|
|
817
|
-
//
|
|
818
|
-
//
|
|
819
|
-
//
|
|
820
|
-
//
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
if (
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
//
|
|
862
|
-
|
|
863
|
-
//
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
//
|
|
872
|
-
//
|
|
873
|
-
//
|
|
874
|
-
//
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
//
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
//
|
|
899
|
-
//
|
|
900
|
-
//
|
|
901
|
-
|
|
902
|
-
//
|
|
903
|
-
//
|
|
904
|
-
//
|
|
905
|
-
//
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
}
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
//
|
|
1009
|
-
//
|
|
1010
|
-
//
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
//
|
|
1015
|
-
//
|
|
1016
|
-
//
|
|
1017
|
-
//
|
|
1018
|
-
//
|
|
1019
|
-
//
|
|
1020
|
-
// -
|
|
1021
|
-
// -
|
|
1022
|
-
// -
|
|
1023
|
-
//
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
}
|
|
1083
|
-
|
|
1084
|
-
//
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
//
|
|
1099
|
-
//
|
|
1100
|
-
//
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
//
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
//
|
|
1125
|
-
//
|
|
1126
|
-
//
|
|
1127
|
-
//
|
|
1128
|
-
//
|
|
1129
|
-
//
|
|
1130
|
-
//
|
|
1131
|
-
//
|
|
1132
|
-
//
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
//
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
//
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
}
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
_type(
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
//
|
|
1229
|
-
|
|
1230
|
-
//
|
|
1231
|
-
//
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
//
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
if (
|
|
1287
|
-
|
|
1288
|
-
}
|
|
1289
|
-
if
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
//
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
//
|
|
1368
|
-
//
|
|
1369
|
-
//
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
throw
|
|
1378
|
-
}
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
throw MinimongoError('Minimongo doesn\'t support
|
|
1387
|
-
}
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
}
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
//
|
|
1399
|
-
//
|
|
1400
|
-
//
|
|
1401
|
-
//
|
|
1402
|
-
//
|
|
1403
|
-
//
|
|
1404
|
-
//
|
|
1405
|
-
//
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
if
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
const
|
|
1430
|
-
if (
|
|
1431
|
-
throw MinimongoError(
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
}
|
|
1469
|
-
|
|
1470
|
-
;
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
if (
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
if (target[field]
|
|
1502
|
-
|
|
1503
|
-
}
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
}
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
if (
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
if (target[field]
|
|
1518
|
-
|
|
1519
|
-
}
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
}
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
if (
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
target[field]
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
target[field]
|
|
1537
|
-
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
},
|
|
1556
|
-
$
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
}
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
if (
|
|
1573
|
-
|
|
1574
|
-
}
|
|
1575
|
-
if (!(
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
// XXX
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
target[field] =
|
|
1646
|
-
}
|
|
1647
|
-
else {
|
|
1648
|
-
target[field] = target[field].slice(
|
|
1649
|
-
}
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
}
|
|
1664
|
-
else {
|
|
1665
|
-
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
}
|
|
1686
|
-
else {
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
toAdd.
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
if (
|
|
1704
|
-
|
|
1705
|
-
}
|
|
1706
|
-
if (
|
|
1707
|
-
|
|
1708
|
-
}
|
|
1709
|
-
|
|
1710
|
-
toPop.
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
if (
|
|
1722
|
-
|
|
1723
|
-
}
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
//
|
|
1730
|
-
//
|
|
1731
|
-
//
|
|
1732
|
-
//
|
|
1733
|
-
//
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
out = toPull.filter(element => !
|
|
1739
|
-
}
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
if (
|
|
1747
|
-
|
|
1748
|
-
}
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
if (
|
|
1754
|
-
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
if (
|
|
1764
|
-
throw MinimongoError('$rename source
|
|
1765
|
-
}
|
|
1766
|
-
if (
|
|
1767
|
-
throw MinimongoError('$rename
|
|
1768
|
-
}
|
|
1769
|
-
if (arg
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
}
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
const
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
}
|
|
1784
|
-
target2
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
//
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
$
|
|
1803
|
-
$
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
//
|
|
1820
|
-
//
|
|
1821
|
-
//
|
|
1822
|
-
//
|
|
1823
|
-
// options.noCreate is
|
|
1824
|
-
//
|
|
1825
|
-
//
|
|
1826
|
-
//
|
|
1827
|
-
//
|
|
1828
|
-
//
|
|
1829
|
-
//
|
|
1830
|
-
//
|
|
1831
|
-
//
|
|
1832
|
-
// the
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
if (
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
if (
|
|
1855
|
-
throw MinimongoError('
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
}
|
|
1864
|
-
else {
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
}
|
|
1873
|
-
if (
|
|
1874
|
-
|
|
1875
|
-
}
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
}
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
if (
|
|
1896
|
-
|
|
1897
|
-
}
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
}
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
const
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
}
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
//
|
|
1950
|
-
//
|
|
1951
|
-
//
|
|
1952
|
-
// @
|
|
1953
|
-
//
|
|
1954
|
-
//
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
const
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
}
|
|
1998
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports._f = exports.ELEMENT_OPERATORS = exports.hasOwn = void 0;
|
|
7
|
+
exports.compileDocumentSelector = compileDocumentSelector;
|
|
8
|
+
exports.equalityElementMatcher = equalityElementMatcher;
|
|
9
|
+
exports.expandArraysInBranches = expandArraysInBranches;
|
|
10
|
+
exports.isIndexable = isIndexable;
|
|
11
|
+
exports.isNumericKey = isNumericKey;
|
|
12
|
+
exports.isOperatorObject = isOperatorObject;
|
|
13
|
+
exports.makeLookupFunction = makeLookupFunction;
|
|
14
|
+
exports.nothingMatcher = nothingMatcher;
|
|
15
|
+
exports.pathsToTree = pathsToTree;
|
|
16
|
+
exports.populateDocumentWithQueryFields = populateDocumentWithQueryFields;
|
|
17
|
+
exports.projectionDetails = projectionDetails;
|
|
18
|
+
exports.regexpElementMatcher = regexpElementMatcher;
|
|
19
|
+
exports._checkSupportedProjection = _checkSupportedProjection;
|
|
20
|
+
exports._modify = _modify;
|
|
21
|
+
exports.combineImportantPathsIntoProjection = combineImportantPathsIntoProjection;
|
|
22
|
+
exports._pathsElidingNumericKeys = _pathsElidingNumericKeys;
|
|
23
|
+
exports._compileProjection = _compileProjection;
|
|
24
|
+
const mongodb_1 = require("mongodb");
|
|
25
|
+
const ejson_1 = require("../ejson/ejson");
|
|
26
|
+
const geojson_utils_1 = require("./geojson_utils");
|
|
27
|
+
const minimongo_matcher_1 = require("./minimongo_matcher");
|
|
28
|
+
const minimongo_sorter_1 = __importDefault(require("./minimongo_sorter"));
|
|
29
|
+
exports.hasOwn = Object.prototype.hasOwnProperty;
|
|
30
|
+
// Each element selector contains:
|
|
31
|
+
// - compileElementSelector, a function with args:
|
|
32
|
+
// - operand - the "right hand side" of the operator
|
|
33
|
+
// - valueSelector - the "context" for the operator (so that $regex can find
|
|
34
|
+
// $options)
|
|
35
|
+
// - matcher - the Matcher this is going into (so that $elemMatch can compile
|
|
36
|
+
// more things)
|
|
37
|
+
// returning a function mapping a single value to bool.
|
|
38
|
+
// - dontExpandLeafArrays, a bool which prevents expandArraysInBranches from
|
|
39
|
+
// being called
|
|
40
|
+
// - dontIncludeLeafArrays, a bool which causes an argument to be passed to
|
|
41
|
+
// expandArraysInBranches if it is called
|
|
42
|
+
exports.ELEMENT_OPERATORS = {
|
|
43
|
+
$lt: makeInequality(cmpValue => cmpValue < 0),
|
|
44
|
+
$gt: makeInequality(cmpValue => cmpValue > 0),
|
|
45
|
+
$lte: makeInequality(cmpValue => cmpValue <= 0),
|
|
46
|
+
$gte: makeInequality(cmpValue => cmpValue >= 0),
|
|
47
|
+
$mod: {
|
|
48
|
+
compileElementSelector(operand) {
|
|
49
|
+
if (!(Array.isArray(operand) && operand.length === 2
|
|
50
|
+
&& typeof operand[0] === 'number'
|
|
51
|
+
&& typeof operand[1] === 'number')) {
|
|
52
|
+
throw Error('argument to $mod must be an array of two numbers');
|
|
53
|
+
}
|
|
54
|
+
// XXX could require to be ints or round or something
|
|
55
|
+
const divisor = operand[0];
|
|
56
|
+
const remainder = operand[1];
|
|
57
|
+
return value => (typeof value === 'number' && value % divisor === remainder);
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
$in: {
|
|
61
|
+
compileElementSelector(operand) {
|
|
62
|
+
if (!Array.isArray(operand)) {
|
|
63
|
+
throw Error('$in needs an array');
|
|
64
|
+
}
|
|
65
|
+
const elementMatchers = operand.map(option => {
|
|
66
|
+
if (option instanceof RegExp) {
|
|
67
|
+
return regexpElementMatcher(option);
|
|
68
|
+
}
|
|
69
|
+
if (isOperatorObject(option)) {
|
|
70
|
+
throw Error('cannot nest $ under $in');
|
|
71
|
+
}
|
|
72
|
+
return equalityElementMatcher(option);
|
|
73
|
+
});
|
|
74
|
+
return value => {
|
|
75
|
+
// Allow {a: {$in: [null]}} to match when 'a' does not exist.
|
|
76
|
+
if (value === undefined) {
|
|
77
|
+
value = null;
|
|
78
|
+
}
|
|
79
|
+
return elementMatchers.some(matcher => matcher(value));
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
$size: {
|
|
84
|
+
// {a: [[5, 5]]} must match {a: {$size: 1}} but not {a: {$size: 2}}, so we
|
|
85
|
+
// don't want to consider the element [5,5] in the leaf array [[5,5]] as a
|
|
86
|
+
// possible value.
|
|
87
|
+
dontExpandLeafArrays: true,
|
|
88
|
+
compileElementSelector(operand) {
|
|
89
|
+
if (typeof operand === 'string') {
|
|
90
|
+
// Don't ask me why, but by experimentation, this seems to be what Mongo
|
|
91
|
+
// does.
|
|
92
|
+
operand = 0;
|
|
93
|
+
}
|
|
94
|
+
else if (typeof operand !== 'number') {
|
|
95
|
+
throw Error('$size needs a number');
|
|
96
|
+
}
|
|
97
|
+
return value => Array.isArray(value) && value.length === operand;
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
$type: {
|
|
101
|
+
// {a: [5]} must not match {a: {$type: 4}} (4 means array), but it should
|
|
102
|
+
// match {a: {$type: 1}} (1 means number), and {a: [[5]]} must match {$a:
|
|
103
|
+
// {$type: 4}}. Thus, when we see a leaf array, we *should* expand it but
|
|
104
|
+
// should *not* include it itself.
|
|
105
|
+
dontIncludeLeafArrays: true,
|
|
106
|
+
compileElementSelector(operand) {
|
|
107
|
+
if (typeof operand === 'string') {
|
|
108
|
+
const operandAliasMap = {
|
|
109
|
+
'double': 1,
|
|
110
|
+
'string': 2,
|
|
111
|
+
'object': 3,
|
|
112
|
+
'array': 4,
|
|
113
|
+
'binData': 5,
|
|
114
|
+
'undefined': 6,
|
|
115
|
+
'objectId': 7,
|
|
116
|
+
'bool': 8,
|
|
117
|
+
'date': 9,
|
|
118
|
+
'null': 10,
|
|
119
|
+
'regex': 11,
|
|
120
|
+
'dbPointer': 12,
|
|
121
|
+
'javascript': 13,
|
|
122
|
+
'symbol': 14,
|
|
123
|
+
'javascriptWithScope': 15,
|
|
124
|
+
'int': 16,
|
|
125
|
+
'timestamp': 17,
|
|
126
|
+
'long': 18,
|
|
127
|
+
'decimal': 19,
|
|
128
|
+
'minKey': -1,
|
|
129
|
+
'maxKey': 127,
|
|
130
|
+
};
|
|
131
|
+
if (!exports.hasOwn.call(operandAliasMap, operand)) {
|
|
132
|
+
throw Error(`unknown string alias for $type: ${operand}`);
|
|
133
|
+
}
|
|
134
|
+
operand = operandAliasMap[operand];
|
|
135
|
+
}
|
|
136
|
+
else if (typeof operand === 'number') {
|
|
137
|
+
if (operand === 0 || operand < -1
|
|
138
|
+
|| (operand > 19 && operand !== 127)) {
|
|
139
|
+
throw Error(`Invalid numerical $type code: ${operand}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
throw Error('argument to $type is not a number or a string');
|
|
144
|
+
}
|
|
145
|
+
return value => (value !== undefined && exports._f._type(value) === operand);
|
|
146
|
+
},
|
|
147
|
+
},
|
|
148
|
+
$bitsAllSet: {
|
|
149
|
+
compileElementSelector(operand) {
|
|
150
|
+
const mask = getOperandBitmask(operand, '$bitsAllSet');
|
|
151
|
+
return value => {
|
|
152
|
+
const bitmask = getValueBitmask(value, mask.length);
|
|
153
|
+
return bitmask && mask.every((byte, i) => (bitmask[i] & byte) === byte);
|
|
154
|
+
};
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
$bitsAnySet: {
|
|
158
|
+
compileElementSelector(operand) {
|
|
159
|
+
const mask = getOperandBitmask(operand, '$bitsAnySet');
|
|
160
|
+
return value => {
|
|
161
|
+
const bitmask = getValueBitmask(value, mask.length);
|
|
162
|
+
return bitmask && mask.some((byte, i) => (~bitmask[i] & byte) !== byte);
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
$bitsAllClear: {
|
|
167
|
+
compileElementSelector(operand) {
|
|
168
|
+
const mask = getOperandBitmask(operand, '$bitsAllClear');
|
|
169
|
+
return value => {
|
|
170
|
+
const bitmask = getValueBitmask(value, mask.length);
|
|
171
|
+
return bitmask && mask.every((byte, i) => !(bitmask[i] & byte));
|
|
172
|
+
};
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
$bitsAnyClear: {
|
|
176
|
+
compileElementSelector(operand) {
|
|
177
|
+
const mask = getOperandBitmask(operand, '$bitsAnyClear');
|
|
178
|
+
return value => {
|
|
179
|
+
const bitmask = getValueBitmask(value, mask.length);
|
|
180
|
+
return bitmask && mask.some((byte, i) => (bitmask[i] & byte) !== byte);
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
$regex: {
|
|
185
|
+
compileElementSelector(operand, valueSelector) {
|
|
186
|
+
if (!(typeof operand === 'string' || operand instanceof RegExp)) {
|
|
187
|
+
throw Error('$regex has to be a string or RegExp');
|
|
188
|
+
}
|
|
189
|
+
let regexp;
|
|
190
|
+
if (valueSelector.$options !== undefined) {
|
|
191
|
+
// Options passed in $options (even the empty string) always overrides
|
|
192
|
+
// options in the RegExp object itself.
|
|
193
|
+
// Be clear that we only support the JS-supported options, not extended
|
|
194
|
+
// ones (eg, Mongo supports x and s). Ideally we would implement x and s
|
|
195
|
+
// by transforming the regexp, but not today...
|
|
196
|
+
if (/[^gim]/.test(valueSelector.$options)) {
|
|
197
|
+
throw new Error('Only the i, m, and g regexp options are supported');
|
|
198
|
+
}
|
|
199
|
+
const source = operand instanceof RegExp ? operand.source : operand;
|
|
200
|
+
regexp = new RegExp(source, valueSelector.$options);
|
|
201
|
+
}
|
|
202
|
+
else if (operand instanceof RegExp) {
|
|
203
|
+
regexp = operand;
|
|
204
|
+
}
|
|
205
|
+
else {
|
|
206
|
+
regexp = new RegExp(operand);
|
|
207
|
+
}
|
|
208
|
+
return regexpElementMatcher(regexp);
|
|
209
|
+
},
|
|
210
|
+
},
|
|
211
|
+
$elemMatch: {
|
|
212
|
+
dontExpandLeafArrays: true,
|
|
213
|
+
compileElementSelector(operand, valueSelector, matcher) {
|
|
214
|
+
if (!_isPlainObject(operand)) {
|
|
215
|
+
throw Error('$elemMatch need an object');
|
|
216
|
+
}
|
|
217
|
+
const isDocMatcher = !isOperatorObject(Object.keys(operand)
|
|
218
|
+
.filter(key => !exports.hasOwn.call(LOGICAL_OPERATORS, key))
|
|
219
|
+
.reduce((a, b) => Object.assign(a, { [b]: operand[b] }), {}), true);
|
|
220
|
+
let subMatcher;
|
|
221
|
+
if (isDocMatcher) {
|
|
222
|
+
// This is NOT the same as compileValueSelector(operand), and not just
|
|
223
|
+
// because of the slightly different calling convention.
|
|
224
|
+
// {$elemMatch: {x: 3}} means "an element has a field x:3", not
|
|
225
|
+
// "consists only of a field x:3". Also, regexps and sub-$ are allowed.
|
|
226
|
+
subMatcher =
|
|
227
|
+
compileDocumentSelector(operand, matcher, { inElemMatch: true });
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
subMatcher = compileValueSelector(operand, matcher);
|
|
231
|
+
}
|
|
232
|
+
return value => {
|
|
233
|
+
if (!Array.isArray(value)) {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
for (let i = 0; i < value.length; ++i) {
|
|
237
|
+
const arrayElement = value[i];
|
|
238
|
+
let arg;
|
|
239
|
+
if (isDocMatcher) {
|
|
240
|
+
// We can only match {$elemMatch: {b: 3}} against objects.
|
|
241
|
+
// (We can also match against arrays, if there's numeric indices,
|
|
242
|
+
// eg {$elemMatch: {'0.b': 3}} or {$elemMatch: {0: 3}}.)
|
|
243
|
+
if (!isIndexable(arrayElement)) {
|
|
244
|
+
return false;
|
|
245
|
+
}
|
|
246
|
+
arg = arrayElement;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
// dontIterate ensures that {a: {$elemMatch: {$gt: 5}}} matches
|
|
250
|
+
// {a: [8]} but not {a: [[8]]}
|
|
251
|
+
arg = [{ value: arrayElement, dontIterate: true }];
|
|
252
|
+
}
|
|
253
|
+
// XXX support $near in $elemMatch by propagating $distance?
|
|
254
|
+
if (subMatcher(arg).result) {
|
|
255
|
+
return i; // specially understood to mean "use as arrayIndices"
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return false;
|
|
259
|
+
};
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
// Operators that appear at the top level of a document selector.
|
|
264
|
+
const LOGICAL_OPERATORS = {
|
|
265
|
+
$and(subSelector, matcher, inElemMatch) {
|
|
266
|
+
return andDocumentMatchers(compileArrayOfDocumentSelectors(subSelector, matcher, inElemMatch));
|
|
267
|
+
},
|
|
268
|
+
$or(subSelector, matcher, inElemMatch) {
|
|
269
|
+
const matchers = compileArrayOfDocumentSelectors(subSelector, matcher, inElemMatch);
|
|
270
|
+
// Special case: if there is only one matcher, use it directly, *preserving*
|
|
271
|
+
// any arrayIndices it returns.
|
|
272
|
+
if (matchers.length === 1) {
|
|
273
|
+
return matchers[0];
|
|
274
|
+
}
|
|
275
|
+
return doc => {
|
|
276
|
+
const result = matchers.some(fn => fn(doc).result);
|
|
277
|
+
// $or does NOT set arrayIndices when it has multiple
|
|
278
|
+
// sub-expressions. (Tested against MongoDB.)
|
|
279
|
+
return { result };
|
|
280
|
+
};
|
|
281
|
+
},
|
|
282
|
+
$nor(subSelector, matcher, inElemMatch) {
|
|
283
|
+
const matchers = compileArrayOfDocumentSelectors(subSelector, matcher, inElemMatch);
|
|
284
|
+
return doc => {
|
|
285
|
+
const result = matchers.every(fn => !fn(doc).result);
|
|
286
|
+
// Never set arrayIndices, because we only match if nothing in particular
|
|
287
|
+
// 'matched' (and because this is consistent with MongoDB).
|
|
288
|
+
return { result };
|
|
289
|
+
};
|
|
290
|
+
},
|
|
291
|
+
$where(selectorValue, matcher) {
|
|
292
|
+
// Record that *any* path may be used.
|
|
293
|
+
matcher._recordPathUsed('');
|
|
294
|
+
matcher._hasWhere = true;
|
|
295
|
+
if (!(selectorValue instanceof Function)) {
|
|
296
|
+
// XXX MongoDB seems to have more complex logic to decide where or or not
|
|
297
|
+
// to add 'return'; not sure exactly what it is.
|
|
298
|
+
selectorValue = Function('obj', `return ${selectorValue}`);
|
|
299
|
+
}
|
|
300
|
+
// We make the document available as both `this` and `obj`.
|
|
301
|
+
// // XXX not sure what we should do if this throws
|
|
302
|
+
return doc => ({ result: selectorValue.call(doc, doc) });
|
|
303
|
+
},
|
|
304
|
+
// This is just used as a comment in the query (in MongoDB, it also ends up in
|
|
305
|
+
// query logs); it has no effect on the actual selection.
|
|
306
|
+
$comment() {
|
|
307
|
+
return () => ({ result: true });
|
|
308
|
+
},
|
|
309
|
+
};
|
|
310
|
+
// Operators that (unlike LOGICAL_OPERATORS) pertain to individual paths in a
|
|
311
|
+
// document, but (unlike ELEMENT_OPERATORS) do not have a simple definition as
|
|
312
|
+
// "match each branched value independently and combine with
|
|
313
|
+
// convertElementMatcherToBranchedMatcher".
|
|
314
|
+
const VALUE_OPERATORS = {
|
|
315
|
+
$eq(operand) {
|
|
316
|
+
return convertElementMatcherToBranchedMatcher(equalityElementMatcher(operand));
|
|
317
|
+
},
|
|
318
|
+
$not(operand, valueSelector, matcher) {
|
|
319
|
+
return invertBranchedMatcher(compileValueSelector(operand, matcher));
|
|
320
|
+
},
|
|
321
|
+
$ne(operand) {
|
|
322
|
+
return invertBranchedMatcher(convertElementMatcherToBranchedMatcher(equalityElementMatcher(operand)));
|
|
323
|
+
},
|
|
324
|
+
$nin(operand) {
|
|
325
|
+
return invertBranchedMatcher(convertElementMatcherToBranchedMatcher(exports.ELEMENT_OPERATORS.$in.compileElementSelector(operand)));
|
|
326
|
+
},
|
|
327
|
+
$exists(operand) {
|
|
328
|
+
const exists = convertElementMatcherToBranchedMatcher(value => value !== undefined);
|
|
329
|
+
return operand ? exists : invertBranchedMatcher(exists);
|
|
330
|
+
},
|
|
331
|
+
// $options just provides options for $regex; its logic is inside $regex
|
|
332
|
+
$options(operand, valueSelector) {
|
|
333
|
+
if (!exports.hasOwn.call(valueSelector, '$regex')) {
|
|
334
|
+
throw Error('$options needs a $regex');
|
|
335
|
+
}
|
|
336
|
+
return everythingMatcher;
|
|
337
|
+
},
|
|
338
|
+
// $maxDistance is basically an argument to $near
|
|
339
|
+
$maxDistance(operand, valueSelector) {
|
|
340
|
+
if (!valueSelector.$near) {
|
|
341
|
+
throw Error('$maxDistance needs a $near');
|
|
342
|
+
}
|
|
343
|
+
return everythingMatcher;
|
|
344
|
+
},
|
|
345
|
+
$all(operand, valueSelector, matcher) {
|
|
346
|
+
if (!Array.isArray(operand)) {
|
|
347
|
+
throw Error('$all requires array');
|
|
348
|
+
}
|
|
349
|
+
// Not sure why, but this seems to be what MongoDB does.
|
|
350
|
+
if (operand.length === 0) {
|
|
351
|
+
return nothingMatcher;
|
|
352
|
+
}
|
|
353
|
+
const branchedMatchers = operand.map(criterion => {
|
|
354
|
+
// XXX handle $all/$elemMatch combination
|
|
355
|
+
if (isOperatorObject(criterion)) {
|
|
356
|
+
throw Error('no $ expressions in $all');
|
|
357
|
+
}
|
|
358
|
+
// This is always a regexp or equality selector.
|
|
359
|
+
return compileValueSelector(criterion, matcher);
|
|
360
|
+
});
|
|
361
|
+
// andBranchedMatchers does NOT require all selectors to return true on the
|
|
362
|
+
// SAME branch.
|
|
363
|
+
return andBranchedMatchers(branchedMatchers);
|
|
364
|
+
},
|
|
365
|
+
$near(operand, valueSelector, matcher, isRoot) {
|
|
366
|
+
if (!isRoot) {
|
|
367
|
+
throw Error('$near can\'t be inside another $ operator');
|
|
368
|
+
}
|
|
369
|
+
matcher._hasGeoQuery = true;
|
|
370
|
+
// There are two kinds of geodata in MongoDB: legacy coordinate pairs and
|
|
371
|
+
// GeoJSON. They use different distance metrics, too. GeoJSON queries are
|
|
372
|
+
// marked with a $geometry property, though legacy coordinates can be
|
|
373
|
+
// matched using $geometry.
|
|
374
|
+
let maxDistance, point, distance;
|
|
375
|
+
if (_isPlainObject(operand) && exports.hasOwn.call(operand, '$geometry')) {
|
|
376
|
+
// GeoJSON "2dsphere" mode.
|
|
377
|
+
maxDistance = operand.$maxDistance;
|
|
378
|
+
point = operand.$geometry;
|
|
379
|
+
distance = value => {
|
|
380
|
+
// XXX: for now, we don't calculate the actual distance between, say,
|
|
381
|
+
// polygon and circle. If people care about this use-case it will get
|
|
382
|
+
// a priority.
|
|
383
|
+
if (!value) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
if (!value.type) {
|
|
387
|
+
return (0, geojson_utils_1.pointDistance)(point, { type: 'Point', coordinates: pointToArray(value) });
|
|
388
|
+
}
|
|
389
|
+
if (value.type === 'Point') {
|
|
390
|
+
return (0, geojson_utils_1.pointDistance)(point, value);
|
|
391
|
+
}
|
|
392
|
+
return (0, geojson_utils_1.geometryWithinRadius)(value, point, maxDistance)
|
|
393
|
+
? 0
|
|
394
|
+
: maxDistance + 1;
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
maxDistance = valueSelector.$maxDistance;
|
|
399
|
+
if (!isIndexable(operand)) {
|
|
400
|
+
throw Error('$near argument must be coordinate pair or GeoJSON');
|
|
401
|
+
}
|
|
402
|
+
point = pointToArray(operand);
|
|
403
|
+
distance = value => {
|
|
404
|
+
if (!isIndexable(value)) {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
return distanceCoordinatePairs(point, value);
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return branchedValues => {
|
|
411
|
+
// There might be multiple points in the document that match the given
|
|
412
|
+
// field. Only one of them needs to be within $maxDistance, but we need to
|
|
413
|
+
// evaluate all of them and use the nearest one for the implicit sort
|
|
414
|
+
// specifier. (That's why we can't just use ELEMENT_OPERATORS here.)
|
|
415
|
+
//
|
|
416
|
+
// Note: This differs from MongoDB's implementation, where a document will
|
|
417
|
+
// actually show up *multiple times* in the result set, with one entry for
|
|
418
|
+
// each within-$maxDistance branching point.
|
|
419
|
+
const result = { result: false, distance: undefined, arrayIndices: undefined };
|
|
420
|
+
expandArraysInBranches(branchedValues).every(branch => {
|
|
421
|
+
// if operation is an update, don't skip branches, just return the first
|
|
422
|
+
// one (#3599)
|
|
423
|
+
let curDistance;
|
|
424
|
+
if (!matcher._isUpdate) {
|
|
425
|
+
if (!(typeof branch.value === 'object')) {
|
|
426
|
+
return true;
|
|
427
|
+
}
|
|
428
|
+
curDistance = distance(branch.value);
|
|
429
|
+
// Skip branches that aren't real points or are too far away.
|
|
430
|
+
if (curDistance === null || curDistance > maxDistance) {
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
// Skip anything that's a tie.
|
|
434
|
+
if (result.distance !== undefined && result.distance <= curDistance) {
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
result.result = true;
|
|
439
|
+
result.distance = curDistance;
|
|
440
|
+
if (branch.arrayIndices) {
|
|
441
|
+
result.arrayIndices = branch.arrayIndices;
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
delete result.arrayIndices;
|
|
445
|
+
}
|
|
446
|
+
return !matcher._isUpdate;
|
|
447
|
+
});
|
|
448
|
+
return result;
|
|
449
|
+
};
|
|
450
|
+
},
|
|
451
|
+
};
|
|
452
|
+
// NB: We are cheating and using this function to implement 'AND' for both
|
|
453
|
+
// 'document matchers' and 'branched matchers'. They both return result objects
|
|
454
|
+
// but the argument is different: for the former it's a whole doc, whereas for
|
|
455
|
+
// the latter it's an array of 'branched values'.
|
|
456
|
+
function andSomeMatchers(subMatchers) {
|
|
457
|
+
if (subMatchers.length === 0) {
|
|
458
|
+
return everythingMatcher;
|
|
459
|
+
}
|
|
460
|
+
if (subMatchers.length === 1) {
|
|
461
|
+
return subMatchers[0];
|
|
462
|
+
}
|
|
463
|
+
return docOrBranches => {
|
|
464
|
+
const match = {};
|
|
465
|
+
match.result = subMatchers.every(fn => {
|
|
466
|
+
const subResult = fn(docOrBranches);
|
|
467
|
+
// Copy a 'distance' number out of the first sub-matcher that has
|
|
468
|
+
// one. Yes, this means that if there are multiple $near fields in a
|
|
469
|
+
// query, something arbitrary happens; this appears to be consistent with
|
|
470
|
+
// Mongo.
|
|
471
|
+
if (subResult.result &&
|
|
472
|
+
subResult.distance !== undefined &&
|
|
473
|
+
match.distance === undefined) {
|
|
474
|
+
match.distance = subResult.distance;
|
|
475
|
+
}
|
|
476
|
+
// Similarly, propagate arrayIndices from sub-matchers... but to match
|
|
477
|
+
// MongoDB behavior, this time the *last* sub-matcher with arrayIndices
|
|
478
|
+
// wins.
|
|
479
|
+
if (subResult.result && subResult.arrayIndices) {
|
|
480
|
+
match.arrayIndices = subResult.arrayIndices;
|
|
481
|
+
}
|
|
482
|
+
return subResult.result;
|
|
483
|
+
});
|
|
484
|
+
// If we didn't actually match, forget any extra metadata we came up with.
|
|
485
|
+
if (!match.result) {
|
|
486
|
+
delete match.distance;
|
|
487
|
+
delete match.arrayIndices;
|
|
488
|
+
}
|
|
489
|
+
return match;
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
const andDocumentMatchers = andSomeMatchers;
|
|
493
|
+
const andBranchedMatchers = andSomeMatchers;
|
|
494
|
+
function compileArrayOfDocumentSelectors(selectors, matcher, inElemMatch) {
|
|
495
|
+
if (!Array.isArray(selectors) || selectors.length === 0) {
|
|
496
|
+
throw Error('$and/$or/$nor must be nonempty array');
|
|
497
|
+
}
|
|
498
|
+
return selectors.map(subSelector => {
|
|
499
|
+
if (!_isPlainObject(subSelector)) {
|
|
500
|
+
throw Error('$or/$and/$nor entries need to be full objects');
|
|
501
|
+
}
|
|
502
|
+
return compileDocumentSelector(subSelector, matcher, { inElemMatch });
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
// Takes in a selector that could match a full document (eg, the original
|
|
506
|
+
// selector). Returns a function mapping document->result object.
|
|
507
|
+
//
|
|
508
|
+
// matcher is the Matcher object we are compiling.
|
|
509
|
+
//
|
|
510
|
+
// If this is the root document selector (ie, not wrapped in $and or the like),
|
|
511
|
+
// then isRoot is true. (This is used by $near.)
|
|
512
|
+
function compileDocumentSelector(docSelector, matcher, options = {}) {
|
|
513
|
+
const docMatchers = Object.keys(docSelector).map(key => {
|
|
514
|
+
const subSelector = docSelector[key];
|
|
515
|
+
if (key.substr(0, 1) === '$') {
|
|
516
|
+
// Outer operators are either logical operators (they recurse back into
|
|
517
|
+
// this function), or $where.
|
|
518
|
+
if (!exports.hasOwn.call(LOGICAL_OPERATORS, key)) {
|
|
519
|
+
throw new Error(`Unrecognized logical operator: ${key}`);
|
|
520
|
+
}
|
|
521
|
+
matcher._isSimple = false;
|
|
522
|
+
return LOGICAL_OPERATORS[key](subSelector, matcher, options.inElemMatch);
|
|
523
|
+
}
|
|
524
|
+
// Record this path, but only if we aren't in an elemMatcher, since in an
|
|
525
|
+
// elemMatch this is a path inside an object in an array, not in the doc
|
|
526
|
+
// root.
|
|
527
|
+
if (!options.inElemMatch) {
|
|
528
|
+
matcher._recordPathUsed(key);
|
|
529
|
+
}
|
|
530
|
+
// Don't add a matcher if subSelector is a function -- this is to match
|
|
531
|
+
// the behavior of Meteor on the server (inherited from the node mongodb
|
|
532
|
+
// driver), which is to ignore any part of a selector which is a function.
|
|
533
|
+
if (typeof subSelector === 'function') {
|
|
534
|
+
return undefined;
|
|
535
|
+
}
|
|
536
|
+
const lookUpByIndex = makeLookupFunction(key);
|
|
537
|
+
const valueMatcher = compileValueSelector(subSelector, matcher, options.isRoot);
|
|
538
|
+
return doc => valueMatcher(lookUpByIndex(doc));
|
|
539
|
+
}).filter(Boolean);
|
|
540
|
+
return andDocumentMatchers(docMatchers);
|
|
541
|
+
}
|
|
542
|
+
// Takes in a selector that could match a key-indexed value in a document; eg,
|
|
543
|
+
// {$gt: 5, $lt: 9}, or a regular expression, or any non-expression object (to
|
|
544
|
+
// indicate equality). Returns a branched matcher: a function mapping
|
|
545
|
+
// [branched value]->result object.
|
|
546
|
+
function compileValueSelector(valueSelector, matcher, isRoot) {
|
|
547
|
+
if (valueSelector instanceof RegExp) {
|
|
548
|
+
matcher._isSimple = false;
|
|
549
|
+
return convertElementMatcherToBranchedMatcher(regexpElementMatcher(valueSelector));
|
|
550
|
+
}
|
|
551
|
+
if (isOperatorObject(valueSelector)) {
|
|
552
|
+
return operatorBranchedMatcher(valueSelector, matcher, isRoot);
|
|
553
|
+
}
|
|
554
|
+
return convertElementMatcherToBranchedMatcher(equalityElementMatcher(valueSelector));
|
|
555
|
+
}
|
|
556
|
+
// Given an element matcher (which evaluates a single value), returns a branched
|
|
557
|
+
// value (which evaluates the element matcher on all the branches and returns a
|
|
558
|
+
// more structured return value possibly including arrayIndices).
|
|
559
|
+
function convertElementMatcherToBranchedMatcher(elementMatcher, options = {}) {
|
|
560
|
+
return (branches) => {
|
|
561
|
+
const expanded = options.dontExpandLeafArrays
|
|
562
|
+
? branches
|
|
563
|
+
: expandArraysInBranches(branches, options.dontIncludeLeafArrays);
|
|
564
|
+
const match = { result: false, arrayIndices: [] };
|
|
565
|
+
match.result = expanded.some(element => {
|
|
566
|
+
let matched = elementMatcher(element.value);
|
|
567
|
+
// Special case for $elemMatch: it means "true, and use this as an array
|
|
568
|
+
// index if I didn't already have one".
|
|
569
|
+
if (typeof matched === 'number') {
|
|
570
|
+
// XXX This code dates from when we only stored a single array index
|
|
571
|
+
// (for the outermost array). Should we be also including deeper array
|
|
572
|
+
// indices from the $elemMatch match?
|
|
573
|
+
if (!element.arrayIndices) {
|
|
574
|
+
element.arrayIndices = [matched];
|
|
575
|
+
}
|
|
576
|
+
matched = true;
|
|
577
|
+
}
|
|
578
|
+
// If some element matched, and it's tagged with array indices, include
|
|
579
|
+
// those indices in our result object.
|
|
580
|
+
if (matched && element.arrayIndices) {
|
|
581
|
+
match.arrayIndices = element.arrayIndices;
|
|
582
|
+
}
|
|
583
|
+
return matched;
|
|
584
|
+
});
|
|
585
|
+
return match;
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
// Helpers for $near.
|
|
589
|
+
function distanceCoordinatePairs(a, b) {
|
|
590
|
+
const pointA = pointToArray(a);
|
|
591
|
+
const pointB = pointToArray(b);
|
|
592
|
+
return Math.hypot(pointA[0] - pointB[0], pointA[1] - pointB[1]);
|
|
593
|
+
}
|
|
594
|
+
// Takes something that is not an operator object and returns an element matcher
|
|
595
|
+
// for equality with that thing.
|
|
596
|
+
function equalityElementMatcher(elementSelector) {
|
|
597
|
+
if (isOperatorObject(elementSelector)) {
|
|
598
|
+
throw Error('Can\'t create equalityValueSelector for operator object');
|
|
599
|
+
}
|
|
600
|
+
// Special-case: null and undefined are equal (if you got undefined in there
|
|
601
|
+
// somewhere, or if you got it due to some branch being non-existent in the
|
|
602
|
+
// weird special case), even though they aren't with EJSON.equals.
|
|
603
|
+
// undefined or null
|
|
604
|
+
if (elementSelector == null) {
|
|
605
|
+
return value => value == null;
|
|
606
|
+
}
|
|
607
|
+
return value => exports._f._equal(elementSelector, value);
|
|
608
|
+
}
|
|
609
|
+
function everythingMatcher(docOrBranchedValues) {
|
|
610
|
+
return { result: true };
|
|
611
|
+
}
|
|
612
|
+
function expandArraysInBranches(branches, skipTheArrays) {
|
|
613
|
+
const branchesOut = [];
|
|
614
|
+
branches.forEach(branch => {
|
|
615
|
+
const thisIsArray = Array.isArray(branch.value);
|
|
616
|
+
// We include the branch itself, *UNLESS* we it's an array that we're going
|
|
617
|
+
// to iterate and we're told to skip arrays. (That's right, we include some
|
|
618
|
+
// arrays even skipTheArrays is true: these are arrays that were found via
|
|
619
|
+
// explicit numerical indices.)
|
|
620
|
+
if (!(skipTheArrays && thisIsArray && !branch.dontIterate)) {
|
|
621
|
+
branchesOut.push({ arrayIndices: branch.arrayIndices, value: branch.value });
|
|
622
|
+
}
|
|
623
|
+
if (thisIsArray && !branch.dontIterate) {
|
|
624
|
+
branch.value.forEach((value, i) => {
|
|
625
|
+
branchesOut.push({
|
|
626
|
+
arrayIndices: (branch.arrayIndices || []).concat(i),
|
|
627
|
+
value
|
|
628
|
+
});
|
|
629
|
+
});
|
|
630
|
+
}
|
|
631
|
+
});
|
|
632
|
+
return branchesOut;
|
|
633
|
+
}
|
|
634
|
+
// Helpers for $bitsAllSet/$bitsAnySet/$bitsAllClear/$bitsAnyClear.
|
|
635
|
+
function getOperandBitmask(operand, selector) {
|
|
636
|
+
// numeric bitmask
|
|
637
|
+
// You can provide a numeric bitmask to be matched against the operand field.
|
|
638
|
+
// It must be representable as a non-negative 32-bit signed integer.
|
|
639
|
+
// Otherwise, $bitsAllSet will return an error.
|
|
640
|
+
if (Number.isInteger(operand) && operand >= 0) {
|
|
641
|
+
return new Uint8Array(new Int32Array([operand]).buffer);
|
|
642
|
+
}
|
|
643
|
+
// bindata bitmask
|
|
644
|
+
// You can also use an arbitrarily large BinData instance as a bitmask.
|
|
645
|
+
if ((0, ejson_1.isBinary)(operand)) {
|
|
646
|
+
return new Uint8Array(operand.buffer);
|
|
647
|
+
}
|
|
648
|
+
// position list
|
|
649
|
+
// If querying a list of bit positions, each <position> must be a non-negative
|
|
650
|
+
// integer. Bit positions start at 0 from the least significant bit.
|
|
651
|
+
if (Array.isArray(operand) &&
|
|
652
|
+
operand.every(x => Number.isInteger(x) && x >= 0)) {
|
|
653
|
+
const buffer = new ArrayBuffer((Math.max(...operand) >> 3) + 1);
|
|
654
|
+
const view = new Uint8Array(buffer);
|
|
655
|
+
operand.forEach(x => {
|
|
656
|
+
view[x >> 3] |= 1 << (x & 0x7);
|
|
657
|
+
});
|
|
658
|
+
return view;
|
|
659
|
+
}
|
|
660
|
+
// bad operand
|
|
661
|
+
throw Error(`operand to ${selector} must be a numeric bitmask (representable as a ` +
|
|
662
|
+
'non-negative 32-bit signed integer), a bindata bitmask or an array with ' +
|
|
663
|
+
'bit positions (non-negative integers)');
|
|
664
|
+
}
|
|
665
|
+
function getValueBitmask(value, length) {
|
|
666
|
+
// The field value must be either numerical or a BinData instance. Otherwise,
|
|
667
|
+
// $bits... will not match the current document.
|
|
668
|
+
// numerical
|
|
669
|
+
if (Number.isSafeInteger(value)) {
|
|
670
|
+
// $bits... will not match numerical values that cannot be represented as a
|
|
671
|
+
// signed 64-bit integer. This can be the case if a value is either too
|
|
672
|
+
// large or small to fit in a signed 64-bit integer, or if it has a
|
|
673
|
+
// fractional component.
|
|
674
|
+
const buffer = new ArrayBuffer(Math.max(length, 2 * Uint32Array.BYTES_PER_ELEMENT));
|
|
675
|
+
let view32 = new Uint32Array(buffer, 0, 2);
|
|
676
|
+
view32[0] = value % ((1 << 16) * (1 << 16)) | 0;
|
|
677
|
+
view32[1] = value / ((1 << 16) * (1 << 16)) | 0;
|
|
678
|
+
// sign extension
|
|
679
|
+
if (value < 0) {
|
|
680
|
+
const view8 = new Uint8Array(buffer, 2);
|
|
681
|
+
view8.forEach((byte, i) => {
|
|
682
|
+
view8[i] = 0xff;
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
return new Uint8Array(buffer);
|
|
686
|
+
}
|
|
687
|
+
// bindata
|
|
688
|
+
if ((0, ejson_1.isBinary)(value)) {
|
|
689
|
+
return new Uint8Array(value.buffer);
|
|
690
|
+
}
|
|
691
|
+
// no match
|
|
692
|
+
return false;
|
|
693
|
+
}
|
|
694
|
+
// Actually inserts a key value into the selector document
|
|
695
|
+
// However, this checks there is no ambiguity in setting
|
|
696
|
+
// the value for the given key, throws otherwise
|
|
697
|
+
function insertIntoDocument(document, key, value) {
|
|
698
|
+
Object.keys(document).forEach(existingKey => {
|
|
699
|
+
if ((existingKey.length > key.length && existingKey.indexOf(`${key}.`) === 0) ||
|
|
700
|
+
(key.length > existingKey.length && key.indexOf(`${existingKey}.`) === 0)) {
|
|
701
|
+
throw new Error(`cannot infer query fields to set, both paths '${existingKey}' and ` +
|
|
702
|
+
`'${key}' are matched`);
|
|
703
|
+
}
|
|
704
|
+
else if (existingKey === key) {
|
|
705
|
+
throw new Error(`cannot infer query fields to set, path '${key}' is matched twice`);
|
|
706
|
+
}
|
|
707
|
+
});
|
|
708
|
+
document[key] = value;
|
|
709
|
+
}
|
|
710
|
+
// Returns a branched matcher that matches iff the given matcher does not.
|
|
711
|
+
// Note that this implicitly "deMorganizes" the wrapped function. ie, it
|
|
712
|
+
// means that ALL branch values need to fail to match innerBranchedMatcher.
|
|
713
|
+
function invertBranchedMatcher(branchedMatcher) {
|
|
714
|
+
return branchValues => {
|
|
715
|
+
// We explicitly choose to strip arrayIndices here: it doesn't make sense to
|
|
716
|
+
// say "update the array element that does not match something", at least
|
|
717
|
+
// in mongo-land.
|
|
718
|
+
return { result: !branchedMatcher(branchValues).result };
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function isIndexable(obj) {
|
|
722
|
+
return Array.isArray(obj) || _isPlainObject(obj);
|
|
723
|
+
}
|
|
724
|
+
function isNumericKey(s) {
|
|
725
|
+
return /^[0-9]+$/.test(s);
|
|
726
|
+
}
|
|
727
|
+
// Returns true if this is an object with at least one key and all keys begin
|
|
728
|
+
// with $. Unless inconsistentOK is set, throws if some keys begin with $ and
|
|
729
|
+
// others don't.
|
|
730
|
+
function isOperatorObject(valueSelector, inconsistentOK) {
|
|
731
|
+
if (!_isPlainObject(valueSelector)) {
|
|
732
|
+
return false;
|
|
733
|
+
}
|
|
734
|
+
let theseAreOperators = undefined;
|
|
735
|
+
Object.keys(valueSelector).forEach(selKey => {
|
|
736
|
+
const thisIsOperator = selKey.substr(0, 1) === '$' || selKey === 'diff';
|
|
737
|
+
if (theseAreOperators === undefined) {
|
|
738
|
+
theseAreOperators = thisIsOperator;
|
|
739
|
+
}
|
|
740
|
+
else if (theseAreOperators !== thisIsOperator) {
|
|
741
|
+
if (!inconsistentOK) {
|
|
742
|
+
throw new Error(`Inconsistent operator: ${JSON.stringify(valueSelector)}`);
|
|
743
|
+
}
|
|
744
|
+
theseAreOperators = false;
|
|
745
|
+
}
|
|
746
|
+
});
|
|
747
|
+
return !!theseAreOperators; // {} has no operators
|
|
748
|
+
}
|
|
749
|
+
// Helper for $lt/$gt/$lte/$gte.
|
|
750
|
+
function makeInequality(cmpValueComparator) {
|
|
751
|
+
return {
|
|
752
|
+
compileElementSelector(operand) {
|
|
753
|
+
// Arrays never compare false with non-arrays for any inequality.
|
|
754
|
+
// XXX This was behavior we observed in pre-release MongoDB 2.5, but
|
|
755
|
+
// it seems to have been reverted.
|
|
756
|
+
// See https://jira.mongodb.org/browse/SERVER-11444
|
|
757
|
+
if (Array.isArray(operand)) {
|
|
758
|
+
return () => false;
|
|
759
|
+
}
|
|
760
|
+
// Special case: consider undefined and null the same (so true with
|
|
761
|
+
// $gte/$lte).
|
|
762
|
+
if (operand === undefined) {
|
|
763
|
+
operand = null;
|
|
764
|
+
}
|
|
765
|
+
const operandType = exports._f._type(operand);
|
|
766
|
+
return value => {
|
|
767
|
+
if (value === undefined) {
|
|
768
|
+
value = null;
|
|
769
|
+
}
|
|
770
|
+
// Comparisons are never true among things of different type (except
|
|
771
|
+
// null vs undefined).
|
|
772
|
+
if (exports._f._type(value) !== operandType) {
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
return cmpValueComparator(exports._f._cmp(value, operand));
|
|
776
|
+
};
|
|
777
|
+
},
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
// makeLookupFunction(key) returns a lookup function.
|
|
781
|
+
//
|
|
782
|
+
// A lookup function takes in a document and returns an array of matching
|
|
783
|
+
// branches. If no arrays are found while looking up the key, this array will
|
|
784
|
+
// have exactly one branches (possibly 'undefined', if some segment of the key
|
|
785
|
+
// was not found).
|
|
786
|
+
//
|
|
787
|
+
// If arrays are found in the middle, this can have more than one element, since
|
|
788
|
+
// we 'branch'. When we 'branch', if there are more key segments to look up,
|
|
789
|
+
// then we only pursue branches that are plain objects (not arrays or scalars).
|
|
790
|
+
// This means we can actually end up with no branches!
|
|
791
|
+
//
|
|
792
|
+
// We do *NOT* branch on arrays that are found at the end (ie, at the last
|
|
793
|
+
// dotted member of the key). We just return that array; if you want to
|
|
794
|
+
// effectively 'branch' over the array's values, post-process the lookup
|
|
795
|
+
// function with expandArraysInBranches.
|
|
796
|
+
//
|
|
797
|
+
// Each branch is an object with keys:
|
|
798
|
+
// - value: the value at the branch
|
|
799
|
+
// - dontIterate: an optional bool; if true, it means that 'value' is an array
|
|
800
|
+
// that expandArraysInBranches should NOT expand. This specifically happens
|
|
801
|
+
// when there is a numeric index in the key, and ensures the
|
|
802
|
+
// perhaps-surprising MongoDB behavior where {'a.0': 5} does NOT
|
|
803
|
+
// match {a: [[5]]}.
|
|
804
|
+
// - arrayIndices: if any array indexing was done during lookup (either due to
|
|
805
|
+
// explicit numeric indices or implicit branching), this will be an array of
|
|
806
|
+
// the array indices used, from outermost to innermost; it is falsey or
|
|
807
|
+
// absent if no array index is used. If an explicit numeric index is used,
|
|
808
|
+
// the index will be followed in arrayIndices by the string 'x'.
|
|
809
|
+
//
|
|
810
|
+
// Note: arrayIndices is used for two purposes. First, it is used to
|
|
811
|
+
// implement the '$' modifier feature, which only ever looks at its first
|
|
812
|
+
// element.
|
|
813
|
+
//
|
|
814
|
+
// Second, it is used for sort key generation, which needs to be able to tell
|
|
815
|
+
// the difference between different paths. Moreover, it needs to
|
|
816
|
+
// differentiate between explicit and implicit branching, which is why
|
|
817
|
+
// there's the somewhat hacky 'x' entry: this means that explicit and
|
|
818
|
+
// implicit array lookups will have different full arrayIndices paths. (That
|
|
819
|
+
// code only requires that different paths have different arrayIndices; it
|
|
820
|
+
// doesn't actually 'parse' arrayIndices. As an alternative, arrayIndices
|
|
821
|
+
// could contain objects with flags like 'implicit', but I think that only
|
|
822
|
+
// makes the code surrounding them more complex.)
|
|
823
|
+
//
|
|
824
|
+
// (By the way, this field ends up getting passed around a lot without
|
|
825
|
+
// cloning, so never mutate any arrayIndices field/var in this package!)
|
|
826
|
+
//
|
|
827
|
+
//
|
|
828
|
+
// At the top level, you may only pass in a plain object or array.
|
|
829
|
+
//
|
|
830
|
+
// See the test 'minimongo - lookup' for some examples of what lookup functions
|
|
831
|
+
// return.
|
|
832
|
+
function makeLookupFunction(key, options = {}) {
|
|
833
|
+
const parts = key.split('.');
|
|
834
|
+
const firstPart = parts.length ? parts[0] : '';
|
|
835
|
+
const lookupRest = (parts.length > 1 &&
|
|
836
|
+
makeLookupFunction(parts.slice(1).join('.'), options));
|
|
837
|
+
const omitUnnecessaryFields = result => {
|
|
838
|
+
if (!result.dontIterate) {
|
|
839
|
+
delete result.dontIterate;
|
|
840
|
+
}
|
|
841
|
+
if (result.arrayIndices && !result.arrayIndices.length) {
|
|
842
|
+
delete result.arrayIndices;
|
|
843
|
+
}
|
|
844
|
+
return result;
|
|
845
|
+
};
|
|
846
|
+
// Doc will always be a plain object or an array.
|
|
847
|
+
// apply an explicit numeric index, an array.
|
|
848
|
+
return (doc, arrayIndices = []) => {
|
|
849
|
+
if (Array.isArray(doc)) {
|
|
850
|
+
// If we're being asked to do an invalid lookup into an array (non-integer
|
|
851
|
+
// or out-of-bounds), return no results (which is different from returning
|
|
852
|
+
// a single undefined result, in that `null` equality checks won't match).
|
|
853
|
+
if (!(isNumericKey(firstPart) && firstPart < doc.length)) {
|
|
854
|
+
return [];
|
|
855
|
+
}
|
|
856
|
+
// Remember that we used this array index. Include an 'x' to indicate that
|
|
857
|
+
// the previous index came from being considered as an explicit array
|
|
858
|
+
// index (not branching).
|
|
859
|
+
arrayIndices = arrayIndices.concat(+firstPart, 'x');
|
|
860
|
+
}
|
|
861
|
+
// Do our first lookup.
|
|
862
|
+
const firstLevel = doc[firstPart];
|
|
863
|
+
// If there is no deeper to dig, return what we found.
|
|
864
|
+
//
|
|
865
|
+
// If what we found is an array, most value selectors will choose to treat
|
|
866
|
+
// the elements of the array as matchable values in their own right, but
|
|
867
|
+
// that's done outside of the lookup function. (Exceptions to this are $size
|
|
868
|
+
// and stuff relating to $elemMatch. eg, {a: {$size: 2}} does not match {a:
|
|
869
|
+
// [[1, 2]]}.)
|
|
870
|
+
//
|
|
871
|
+
// That said, if we just did an *explicit* array lookup (on doc) to find
|
|
872
|
+
// firstLevel, and firstLevel is an array too, we do NOT want value
|
|
873
|
+
// selectors to iterate over it. eg, {'a.0': 5} does not match {a: [[5]]}.
|
|
874
|
+
// So in that case, we mark the return value as 'don't iterate'.
|
|
875
|
+
if (!lookupRest) {
|
|
876
|
+
return [omitUnnecessaryFields({
|
|
877
|
+
arrayIndices,
|
|
878
|
+
dontIterate: Array.isArray(doc) && Array.isArray(firstLevel),
|
|
879
|
+
value: firstLevel
|
|
880
|
+
})];
|
|
881
|
+
}
|
|
882
|
+
// We need to dig deeper. But if we can't, because what we've found is not
|
|
883
|
+
// an array or plain object, we're done. If we just did a numeric index into
|
|
884
|
+
// an array, we return nothing here (this is a change in Mongo 2.5 from
|
|
885
|
+
// Mongo 2.4, where {'a.0.b': null} stopped matching {a: [5]}). Otherwise,
|
|
886
|
+
// return a single `undefined` (which can, for example, match via equality
|
|
887
|
+
// with `null`).
|
|
888
|
+
if (!isIndexable(firstLevel)) {
|
|
889
|
+
if (Array.isArray(doc)) {
|
|
890
|
+
return [];
|
|
891
|
+
}
|
|
892
|
+
return [omitUnnecessaryFields({ arrayIndices, value: undefined })];
|
|
893
|
+
}
|
|
894
|
+
const result = [];
|
|
895
|
+
const appendToResult = more => {
|
|
896
|
+
result.push(...more);
|
|
897
|
+
};
|
|
898
|
+
// Dig deeper: look up the rest of the parts on whatever we've found.
|
|
899
|
+
// (lookupRest is smart enough to not try to do invalid lookups into
|
|
900
|
+
// firstLevel if it's an array.)
|
|
901
|
+
appendToResult(lookupRest(firstLevel, arrayIndices));
|
|
902
|
+
// If we found an array, then in *addition* to potentially treating the next
|
|
903
|
+
// part as a literal integer lookup, we should also 'branch': try to look up
|
|
904
|
+
// the rest of the parts on each array element in parallel.
|
|
905
|
+
//
|
|
906
|
+
// In this case, we *only* dig deeper into array elements that are plain
|
|
907
|
+
// objects. (Recall that we only got this far if we have further to dig.)
|
|
908
|
+
// This makes sense: we certainly don't dig deeper into non-indexable
|
|
909
|
+
// objects. And it would be weird to dig into an array: it's simpler to have
|
|
910
|
+
// a rule that explicit integer indexes only apply to an outer array, not to
|
|
911
|
+
// an array you find after a branching search.
|
|
912
|
+
//
|
|
913
|
+
// In the special case of a numeric part in a *sort selector* (not a query
|
|
914
|
+
// selector), we skip the branching: we ONLY allow the numeric part to mean
|
|
915
|
+
// 'look up this index' in that case, not 'also look up this index in all
|
|
916
|
+
// the elements of the array'.
|
|
917
|
+
if (Array.isArray(firstLevel) &&
|
|
918
|
+
!(isNumericKey(parts[1]) && options.forSort)) {
|
|
919
|
+
firstLevel.forEach((branch, arrayIndex) => {
|
|
920
|
+
if (_isPlainObject(branch)) {
|
|
921
|
+
appendToResult(lookupRest(branch, arrayIndices.concat(arrayIndex)));
|
|
922
|
+
}
|
|
923
|
+
});
|
|
924
|
+
}
|
|
925
|
+
return result;
|
|
926
|
+
};
|
|
927
|
+
}
|
|
928
|
+
function MinimongoError(message, options = {}) {
|
|
929
|
+
if (typeof message === 'string' && options.field) {
|
|
930
|
+
message += ` for field '${options.field}'`;
|
|
931
|
+
}
|
|
932
|
+
const error = new Error(message);
|
|
933
|
+
error.name = 'MinimongoError';
|
|
934
|
+
return error;
|
|
935
|
+
}
|
|
936
|
+
function nothingMatcher(docOrBranchedValues) {
|
|
937
|
+
return { result: false };
|
|
938
|
+
}
|
|
939
|
+
// Takes an operator object (an object with $ keys) and returns a branched
|
|
940
|
+
// matcher for it.
|
|
941
|
+
function operatorBranchedMatcher(valueSelector, matcher, isRoot) {
|
|
942
|
+
// Each valueSelector works separately on the various branches. So one
|
|
943
|
+
// operator can match one branch and another can match another branch. This
|
|
944
|
+
// is OK.
|
|
945
|
+
const operatorMatchers = Object.keys(valueSelector).map(operator => {
|
|
946
|
+
const operand = valueSelector[operator];
|
|
947
|
+
const simpleRange = (['$lt', '$lte', '$gt', '$gte'].includes(operator) &&
|
|
948
|
+
typeof operand === 'number');
|
|
949
|
+
const simpleEquality = (['$ne', '$eq'].includes(operator) &&
|
|
950
|
+
operand !== Object(operand));
|
|
951
|
+
const simpleInclusion = (['$in', '$nin'].includes(operator)
|
|
952
|
+
&& Array.isArray(operand)
|
|
953
|
+
&& !operand.some(x => x === Object(x)));
|
|
954
|
+
if (!(simpleRange || simpleInclusion || simpleEquality)) {
|
|
955
|
+
matcher._isSimple = false;
|
|
956
|
+
}
|
|
957
|
+
if (exports.hasOwn.call(VALUE_OPERATORS, operator)) {
|
|
958
|
+
return VALUE_OPERATORS[operator](operand, valueSelector, matcher, isRoot);
|
|
959
|
+
}
|
|
960
|
+
if (exports.hasOwn.call(exports.ELEMENT_OPERATORS, operator)) {
|
|
961
|
+
const options = exports.ELEMENT_OPERATORS[operator];
|
|
962
|
+
return convertElementMatcherToBranchedMatcher(options.compileElementSelector(operand, valueSelector, matcher), options);
|
|
963
|
+
}
|
|
964
|
+
throw new Error(`Unrecognized operator: ${operator}`);
|
|
965
|
+
});
|
|
966
|
+
return andBranchedMatchers(operatorMatchers);
|
|
967
|
+
}
|
|
968
|
+
// paths - Array: list of mongo style paths
|
|
969
|
+
// newLeafFn - Function: of form function(path) should return a scalar value to
|
|
970
|
+
// put into list created for that path
|
|
971
|
+
// conflictFn - Function: of form function(node, path, fullPath) is called
|
|
972
|
+
// when building a tree path for 'fullPath' node on
|
|
973
|
+
// 'path' was already a leaf with a value. Must return a
|
|
974
|
+
// conflict resolution.
|
|
975
|
+
// initial tree - Optional Object: starting tree.
|
|
976
|
+
// @returns - Object: tree represented as a set of nested objects
|
|
977
|
+
function pathsToTree(paths, newLeafFn, conflictFn, root = {}) {
|
|
978
|
+
paths.forEach(path => {
|
|
979
|
+
const pathArray = path.split('.');
|
|
980
|
+
let tree = root;
|
|
981
|
+
// use .every just for iteration with break
|
|
982
|
+
const success = pathArray.slice(0, -1).every((key, i) => {
|
|
983
|
+
if (!exports.hasOwn.call(tree, key)) {
|
|
984
|
+
tree[key] = {};
|
|
985
|
+
}
|
|
986
|
+
else if (tree[key] !== Object(tree[key])) {
|
|
987
|
+
tree[key] = conflictFn(tree[key], pathArray.slice(0, i + 1).join('.'), path);
|
|
988
|
+
// break out of loop if we are failing for this path
|
|
989
|
+
if (tree[key] !== Object(tree[key])) {
|
|
990
|
+
return false;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
tree = tree[key];
|
|
994
|
+
return true;
|
|
995
|
+
});
|
|
996
|
+
if (success) {
|
|
997
|
+
const lastKey = pathArray[pathArray.length - 1];
|
|
998
|
+
if (exports.hasOwn.call(tree, lastKey)) {
|
|
999
|
+
tree[lastKey] = conflictFn(tree[lastKey], path, path);
|
|
1000
|
+
}
|
|
1001
|
+
else {
|
|
1002
|
+
tree[lastKey] = newLeafFn(path);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
});
|
|
1006
|
+
return root;
|
|
1007
|
+
}
|
|
1008
|
+
// Makes sure we get 2 elements array and assume the first one to be x and
|
|
1009
|
+
// the second one to y no matter what user passes.
|
|
1010
|
+
// In case user passes { lon: x, lat: y } returns [x, y]
|
|
1011
|
+
function pointToArray(point) {
|
|
1012
|
+
return Array.isArray(point) ? point.slice() : [point.x, point.y];
|
|
1013
|
+
}
|
|
1014
|
+
// Creating a document from an upsert is quite tricky.
|
|
1015
|
+
// E.g. this selector: {"$or": [{"b.foo": {"$all": ["bar"]}}]}, should result
|
|
1016
|
+
// in: {"b.foo": "bar"}
|
|
1017
|
+
// But this selector: {"$or": [{"b": {"foo": {"$all": ["bar"]}}}]} should throw
|
|
1018
|
+
// an error
|
|
1019
|
+
// Some rules (found mainly with trial & error, so there might be more):
|
|
1020
|
+
// - handle all childs of $and (or implicit $and)
|
|
1021
|
+
// - handle $or nodes with exactly 1 child
|
|
1022
|
+
// - ignore $or nodes with more than 1 child
|
|
1023
|
+
// - ignore $nor and $not nodes
|
|
1024
|
+
// - throw when a value can not be set unambiguously
|
|
1025
|
+
// - every value for $all should be dealt with as separate $eq-s
|
|
1026
|
+
// - threat all children of $all as $eq setters (=> set if $all.length === 1,
|
|
1027
|
+
// otherwise throw error)
|
|
1028
|
+
// - you can not mix '$'-prefixed keys and non-'$'-prefixed keys
|
|
1029
|
+
// - you can only have dotted keys on a root-level
|
|
1030
|
+
// - you can not have '$'-prefixed keys more than one-level deep in an object
|
|
1031
|
+
// Handles one key/value pair to put in the selector document
|
|
1032
|
+
function populateDocumentWithKeyValue(document, key, value) {
|
|
1033
|
+
if (value && Object.getPrototypeOf(value) === Object.prototype) {
|
|
1034
|
+
populateDocumentWithObject(document, key, value);
|
|
1035
|
+
}
|
|
1036
|
+
else if (!(value instanceof RegExp)) {
|
|
1037
|
+
insertIntoDocument(document, key, value);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
// Handles a key, value pair to put in the selector document
|
|
1041
|
+
// if the value is an object
|
|
1042
|
+
function populateDocumentWithObject(document, key, value) {
|
|
1043
|
+
const keys = Object.keys(value);
|
|
1044
|
+
const unprefixedKeys = keys.filter(op => op[0] !== '$');
|
|
1045
|
+
if (unprefixedKeys.length > 0 || !keys.length) {
|
|
1046
|
+
// Literal (possibly empty) object ( or empty object )
|
|
1047
|
+
// Don't allow mixing '$'-prefixed with non-'$'-prefixed fields
|
|
1048
|
+
if (keys.length !== unprefixedKeys.length) {
|
|
1049
|
+
throw new Error(`unknown operator: ${unprefixedKeys[0]}`);
|
|
1050
|
+
}
|
|
1051
|
+
validateObject(value, key);
|
|
1052
|
+
insertIntoDocument(document, key, value);
|
|
1053
|
+
}
|
|
1054
|
+
else {
|
|
1055
|
+
Object.keys(value).forEach(op => {
|
|
1056
|
+
const object = value[op];
|
|
1057
|
+
if (op === '$eq') {
|
|
1058
|
+
populateDocumentWithKeyValue(document, key, object);
|
|
1059
|
+
}
|
|
1060
|
+
else if (op === '$all') {
|
|
1061
|
+
// every value for $all should be dealt with as separate $eq-s
|
|
1062
|
+
object.forEach(element => populateDocumentWithKeyValue(document, key, element));
|
|
1063
|
+
}
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
// Fills a document with certain fields from an upsert selector
|
|
1068
|
+
function populateDocumentWithQueryFields(query, document = {}) {
|
|
1069
|
+
if (Object.getPrototypeOf(query) === Object.prototype) {
|
|
1070
|
+
// handle implicit $and
|
|
1071
|
+
Object.keys(query).forEach(key => {
|
|
1072
|
+
const value = query[key];
|
|
1073
|
+
if (key === '$and') {
|
|
1074
|
+
// handle explicit $and
|
|
1075
|
+
value.forEach(element => populateDocumentWithQueryFields(element, document));
|
|
1076
|
+
}
|
|
1077
|
+
else if (key === '$or') {
|
|
1078
|
+
// handle $or nodes with exactly 1 child
|
|
1079
|
+
if (value.length === 1) {
|
|
1080
|
+
populateDocumentWithQueryFields(value[0], document);
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
else if (key[0] !== '$') {
|
|
1084
|
+
// Ignore other '$'-prefixed logical selectors
|
|
1085
|
+
populateDocumentWithKeyValue(document, key, value);
|
|
1086
|
+
}
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
return document;
|
|
1090
|
+
}
|
|
1091
|
+
// Traverses the keys of passed projection and constructs a tree where all
|
|
1092
|
+
// leaves are either all True or all False
|
|
1093
|
+
// @returns Object:
|
|
1094
|
+
// - tree - Object - tree representation of keys involved in projection
|
|
1095
|
+
// (exception for '_id' as it is a special case handled separately)
|
|
1096
|
+
// - including - Boolean - "take only certain fields" type of projection
|
|
1097
|
+
function projectionDetails(fields) {
|
|
1098
|
+
// Find the non-_id keys (_id is handled specially because it is included
|
|
1099
|
+
// unless explicitly excluded). Sort the keys, so that our code to detect
|
|
1100
|
+
// overlaps like 'foo' and 'foo.bar' can assume that 'foo' comes first.
|
|
1101
|
+
let fieldsKeys = Object.keys(fields).sort();
|
|
1102
|
+
// If _id is the only field in the projection, do not remove it, since it is
|
|
1103
|
+
// required to determine if this is an exclusion or exclusion. Also keep an
|
|
1104
|
+
// inclusive _id, since inclusive _id follows the normal rules about mixing
|
|
1105
|
+
// inclusive and exclusive fields. If _id is not the only field in the
|
|
1106
|
+
// projection and is exclusive, remove it so it can be handled later by a
|
|
1107
|
+
// special case, since exclusive _id is always allowed.
|
|
1108
|
+
if (!(fieldsKeys.length === 1 && fieldsKeys[0] === '_id') &&
|
|
1109
|
+
!(fieldsKeys.includes('_id') && fields._id)) {
|
|
1110
|
+
fieldsKeys = fieldsKeys.filter(key => key !== '_id');
|
|
1111
|
+
}
|
|
1112
|
+
let including = null; // Unknown
|
|
1113
|
+
fieldsKeys.forEach(keyPath => {
|
|
1114
|
+
const rule = !!fields[keyPath];
|
|
1115
|
+
if (including === null) {
|
|
1116
|
+
including = rule;
|
|
1117
|
+
}
|
|
1118
|
+
// This error message is copied from MongoDB shell
|
|
1119
|
+
if (including !== rule) {
|
|
1120
|
+
throw MinimongoError('You cannot currently mix including and excluding fields.');
|
|
1121
|
+
}
|
|
1122
|
+
});
|
|
1123
|
+
const projectionRulesTree = pathsToTree(fieldsKeys, path => including, (node, path, fullPath) => {
|
|
1124
|
+
// Check passed projection fields' keys: If you have two rules such as
|
|
1125
|
+
// 'foo.bar' and 'foo.bar.baz', then the result becomes ambiguous. If
|
|
1126
|
+
// that happens, there is a probability you are doing something wrong,
|
|
1127
|
+
// framework should notify you about such mistake earlier on cursor
|
|
1128
|
+
// compilation step than later during runtime. Note, that real mongo
|
|
1129
|
+
// doesn't do anything about it and the later rule appears in projection
|
|
1130
|
+
// project, more priority it takes.
|
|
1131
|
+
//
|
|
1132
|
+
// Example, assume following in mongo shell:
|
|
1133
|
+
// > db.coll.insert({ a: { b: 23, c: 44 } })
|
|
1134
|
+
// > db.coll.find({}, { 'a': 1, 'a.b': 1 })
|
|
1135
|
+
// {"_id": ObjectId("520bfe456024608e8ef24af3"), "a": {"b": 23}}
|
|
1136
|
+
// > db.coll.find({}, { 'a.b': 1, 'a': 1 })
|
|
1137
|
+
// {"_id": ObjectId("520bfe456024608e8ef24af3"), "a": {"b": 23, "c": 44}}
|
|
1138
|
+
//
|
|
1139
|
+
// Note, how second time the return set of keys is different.
|
|
1140
|
+
const currentPath = fullPath;
|
|
1141
|
+
const anotherPath = path;
|
|
1142
|
+
throw MinimongoError(`both ${currentPath} and ${anotherPath} found in fields option, ` +
|
|
1143
|
+
'using both of them may trigger unexpected behavior. Did you mean to ' +
|
|
1144
|
+
'use only one of them?');
|
|
1145
|
+
});
|
|
1146
|
+
return { including, tree: projectionRulesTree };
|
|
1147
|
+
}
|
|
1148
|
+
// Takes a RegExp object and returns an element matcher.
|
|
1149
|
+
function regexpElementMatcher(regexp) {
|
|
1150
|
+
return value => {
|
|
1151
|
+
if (value instanceof RegExp) {
|
|
1152
|
+
return value.toString() === regexp.toString();
|
|
1153
|
+
}
|
|
1154
|
+
// Regexps only work against strings.
|
|
1155
|
+
if (typeof value !== 'string') {
|
|
1156
|
+
return false;
|
|
1157
|
+
}
|
|
1158
|
+
// Reset regexp's state to avoid inconsistent matching for objects with the
|
|
1159
|
+
// same value on consecutive calls of regexp.test. This happens only if the
|
|
1160
|
+
// regexp has the 'g' flag. Also note that ES6 introduces a new flag 'y' for
|
|
1161
|
+
// which we should *not* change the lastIndex but MongoDB doesn't support
|
|
1162
|
+
// either of these flags.
|
|
1163
|
+
regexp.lastIndex = 0;
|
|
1164
|
+
return regexp.test(value);
|
|
1165
|
+
};
|
|
1166
|
+
}
|
|
1167
|
+
// Validates the key in a path.
|
|
1168
|
+
// Objects that are nested more then 1 level cannot have dotted fields
|
|
1169
|
+
// or fields starting with '$'
|
|
1170
|
+
function validateKeyInPath(key, path) {
|
|
1171
|
+
if (key.includes('.')) {
|
|
1172
|
+
throw new Error(`The dotted field '${key}' in '${path}.${key} is not valid for storage.`);
|
|
1173
|
+
}
|
|
1174
|
+
if (key[0] === '$') {
|
|
1175
|
+
throw new Error(`The dollar ($) prefixed field '${path}.${key} is not valid for storage.`);
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
// Recursively validates an object that is nested more than one level deep
|
|
1179
|
+
function validateObject(object, path) {
|
|
1180
|
+
if (object && Object.getPrototypeOf(object) === Object.prototype) {
|
|
1181
|
+
Object.keys(object).forEach(key => {
|
|
1182
|
+
validateKeyInPath(key, path);
|
|
1183
|
+
validateObject(object[key], path + '.' + key);
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
function _isPlainObject(x) {
|
|
1188
|
+
return x && exports._f._type(x) === 3;
|
|
1189
|
+
}
|
|
1190
|
+
// helpers used by compiled selector code
|
|
1191
|
+
exports._f = {
|
|
1192
|
+
// XXX for _all and _in, consider building 'inquery' at compile time..
|
|
1193
|
+
_type(v) {
|
|
1194
|
+
if (typeof v === 'number') {
|
|
1195
|
+
return 1;
|
|
1196
|
+
}
|
|
1197
|
+
if (typeof v === 'string') {
|
|
1198
|
+
return 2;
|
|
1199
|
+
}
|
|
1200
|
+
if (typeof v === 'boolean') {
|
|
1201
|
+
return 8;
|
|
1202
|
+
}
|
|
1203
|
+
if (Array.isArray(v)) {
|
|
1204
|
+
return 4;
|
|
1205
|
+
}
|
|
1206
|
+
if (v === null) {
|
|
1207
|
+
return 10;
|
|
1208
|
+
}
|
|
1209
|
+
// note that typeof(/x/) === "object"
|
|
1210
|
+
if (v instanceof RegExp) {
|
|
1211
|
+
return 11;
|
|
1212
|
+
}
|
|
1213
|
+
if (typeof v === 'function') {
|
|
1214
|
+
return 13;
|
|
1215
|
+
}
|
|
1216
|
+
if (v instanceof Date) {
|
|
1217
|
+
return 9;
|
|
1218
|
+
}
|
|
1219
|
+
if ((0, ejson_1.isBinary)(v)) {
|
|
1220
|
+
return 5;
|
|
1221
|
+
}
|
|
1222
|
+
if (v instanceof mongodb_1.ObjectId) {
|
|
1223
|
+
return 7;
|
|
1224
|
+
}
|
|
1225
|
+
if (v instanceof mongodb_1.Decimal128) {
|
|
1226
|
+
return 1;
|
|
1227
|
+
}
|
|
1228
|
+
// object
|
|
1229
|
+
return 3;
|
|
1230
|
+
// XXX support some/all of these:
|
|
1231
|
+
// 14, symbol
|
|
1232
|
+
// 15, javascript code with scope
|
|
1233
|
+
// 16, 18: 32-bit/64-bit integer
|
|
1234
|
+
// 17, timestamp
|
|
1235
|
+
// 255, minkey
|
|
1236
|
+
// 127, maxkey
|
|
1237
|
+
},
|
|
1238
|
+
// deep equality test: use for literal document and array matches
|
|
1239
|
+
_equal(a, b) {
|
|
1240
|
+
return (0, ejson_1.equals)(a, b, { keyOrderSensitive: true });
|
|
1241
|
+
},
|
|
1242
|
+
// maps a type code to a value that can be used to sort values of different
|
|
1243
|
+
// types
|
|
1244
|
+
_typeorder(t) {
|
|
1245
|
+
// http://www.mongodb.org/display/DOCS/What+is+the+Compare+Order+for+BSON+Types
|
|
1246
|
+
// XXX what is the correct sort position for Javascript code?
|
|
1247
|
+
// ('100' in the matrix below)
|
|
1248
|
+
// XXX minkey/maxkey
|
|
1249
|
+
return [
|
|
1250
|
+
-1, // (not a type)
|
|
1251
|
+
1, // number
|
|
1252
|
+
2, // string
|
|
1253
|
+
3, // object
|
|
1254
|
+
4, // array
|
|
1255
|
+
5, // binary
|
|
1256
|
+
-1, // deprecated
|
|
1257
|
+
6, // ObjectID
|
|
1258
|
+
7, // bool
|
|
1259
|
+
8, // Date
|
|
1260
|
+
0, // null
|
|
1261
|
+
9, // RegExp
|
|
1262
|
+
-1, // deprecated
|
|
1263
|
+
100, // JS code
|
|
1264
|
+
2, // deprecated (symbol)
|
|
1265
|
+
100, // JS code
|
|
1266
|
+
1, // 32-bit int
|
|
1267
|
+
8, // Mongo timestamp
|
|
1268
|
+
1 // 64-bit int
|
|
1269
|
+
][t];
|
|
1270
|
+
},
|
|
1271
|
+
// compare two values of unknown type according to BSON ordering
|
|
1272
|
+
// semantics. (as an extension, consider 'undefined' to be less than
|
|
1273
|
+
// any other value.) return negative if a is less, positive if b is
|
|
1274
|
+
// less, or 0 if equal
|
|
1275
|
+
_cmp(a, b) {
|
|
1276
|
+
if (a === undefined) {
|
|
1277
|
+
return b === undefined ? 0 : -1;
|
|
1278
|
+
}
|
|
1279
|
+
if (b === undefined) {
|
|
1280
|
+
return 1;
|
|
1281
|
+
}
|
|
1282
|
+
let ta = exports._f._type(a);
|
|
1283
|
+
let tb = exports._f._type(b);
|
|
1284
|
+
const oa = exports._f._typeorder(ta);
|
|
1285
|
+
const ob = exports._f._typeorder(tb);
|
|
1286
|
+
if (oa !== ob) {
|
|
1287
|
+
return oa < ob ? -1 : 1;
|
|
1288
|
+
}
|
|
1289
|
+
// XXX need to implement this if we implement Symbol or integers, or
|
|
1290
|
+
// Timestamp
|
|
1291
|
+
if (ta !== tb) {
|
|
1292
|
+
throw Error('Missing type coercion logic in _cmp');
|
|
1293
|
+
}
|
|
1294
|
+
if (ta === 7) { // ObjectID
|
|
1295
|
+
// Convert to string.
|
|
1296
|
+
ta = tb = 2;
|
|
1297
|
+
a = a.toHexString();
|
|
1298
|
+
b = b.toHexString();
|
|
1299
|
+
}
|
|
1300
|
+
if (ta === 9) { // Date
|
|
1301
|
+
// Convert to millis.
|
|
1302
|
+
ta = tb = 1;
|
|
1303
|
+
a = isNaN(a) ? 0 : a.getTime();
|
|
1304
|
+
b = isNaN(b) ? 0 : b.getTime();
|
|
1305
|
+
}
|
|
1306
|
+
if (ta === 1) { // double
|
|
1307
|
+
if (a instanceof mongodb_1.Decimal128) {
|
|
1308
|
+
return mongodb_1.Decimal128.fromString((BigInt(a.toString()) - BigInt(b.toString())).toString());
|
|
1309
|
+
}
|
|
1310
|
+
else {
|
|
1311
|
+
return a - b;
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
if (tb === 2) // string
|
|
1315
|
+
return a < b ? -1 : a === b ? 0 : 1;
|
|
1316
|
+
if (ta === 3) { // Object
|
|
1317
|
+
// this could be much more efficient in the expected case ...
|
|
1318
|
+
const toArray = object => {
|
|
1319
|
+
const result = [];
|
|
1320
|
+
Object.keys(object).forEach(key => {
|
|
1321
|
+
result.push(key, object[key]);
|
|
1322
|
+
});
|
|
1323
|
+
return result;
|
|
1324
|
+
};
|
|
1325
|
+
return exports._f._cmp(toArray(a), toArray(b));
|
|
1326
|
+
}
|
|
1327
|
+
if (ta === 4) { // Array
|
|
1328
|
+
for (let i = 0;; i++) {
|
|
1329
|
+
if (i === a.length) {
|
|
1330
|
+
return i === b.length ? 0 : -1;
|
|
1331
|
+
}
|
|
1332
|
+
if (i === b.length) {
|
|
1333
|
+
return 1;
|
|
1334
|
+
}
|
|
1335
|
+
const s = exports._f._cmp(a[i], b[i]);
|
|
1336
|
+
if (s !== 0) {
|
|
1337
|
+
return s;
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
}
|
|
1341
|
+
if (ta === 5) { // binary
|
|
1342
|
+
// Surprisingly, a small binary blob is always less than a large one in
|
|
1343
|
+
// Mongo.
|
|
1344
|
+
if (a.length !== b.length) {
|
|
1345
|
+
return a.length - b.length;
|
|
1346
|
+
}
|
|
1347
|
+
for (let i = 0; i < a.length; i++) {
|
|
1348
|
+
if (a[i] < b[i]) {
|
|
1349
|
+
return -1;
|
|
1350
|
+
}
|
|
1351
|
+
if (a[i] > b[i]) {
|
|
1352
|
+
return 1;
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
return 0;
|
|
1356
|
+
}
|
|
1357
|
+
if (ta === 8) { // boolean
|
|
1358
|
+
if (a) {
|
|
1359
|
+
return b ? 0 : 1;
|
|
1360
|
+
}
|
|
1361
|
+
return b ? -1 : 0;
|
|
1362
|
+
}
|
|
1363
|
+
if (ta === 10) // null
|
|
1364
|
+
return 0;
|
|
1365
|
+
if (ta === 11) // regexp
|
|
1366
|
+
throw Error('Sorting not supported on regular expression'); // XXX
|
|
1367
|
+
// 13: javascript code
|
|
1368
|
+
// 14: symbol
|
|
1369
|
+
// 15: javascript code with scope
|
|
1370
|
+
// 16: 32-bit integer
|
|
1371
|
+
// 17: timestamp
|
|
1372
|
+
// 18: 64-bit integer
|
|
1373
|
+
// 255: minkey
|
|
1374
|
+
// 127: maxkey
|
|
1375
|
+
if (ta === 13) // javascript code
|
|
1376
|
+
throw Error('Sorting not supported on Javascript code'); // XXX
|
|
1377
|
+
throw Error('Unknown type to sort');
|
|
1378
|
+
},
|
|
1379
|
+
};
|
|
1380
|
+
function _checkSupportedProjection(fields) {
|
|
1381
|
+
if (fields !== Object(fields) || Array.isArray(fields)) {
|
|
1382
|
+
throw MinimongoError('fields option must be an object');
|
|
1383
|
+
}
|
|
1384
|
+
Object.keys(fields).forEach(keyPath => {
|
|
1385
|
+
if (keyPath.split('.').includes('$')) {
|
|
1386
|
+
throw MinimongoError('Minimongo doesn\'t support $ operator in projections yet.');
|
|
1387
|
+
}
|
|
1388
|
+
const value = fields[keyPath];
|
|
1389
|
+
if (typeof value === 'object' &&
|
|
1390
|
+
['$elemMatch', '$meta', '$slice'].some(key => exports.hasOwn.call(value, key))) {
|
|
1391
|
+
throw MinimongoError('Minimongo doesn\'t support operators in projections yet.');
|
|
1392
|
+
}
|
|
1393
|
+
if (![1, 0, true, false].includes(value)) {
|
|
1394
|
+
throw MinimongoError('Projection values should be one of 1, 0, true, or false');
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
}
|
|
1398
|
+
// XXX need a strategy for passing the binding of $ into this
|
|
1399
|
+
// function, from the compiled selector
|
|
1400
|
+
//
|
|
1401
|
+
// maybe just {key.up.to.just.before.dollarsign: array_index}
|
|
1402
|
+
//
|
|
1403
|
+
// XXX atomicity: if one modification fails, do we roll back the whole
|
|
1404
|
+
// change?
|
|
1405
|
+
//
|
|
1406
|
+
// options:
|
|
1407
|
+
// - isInsert is set when _modify is being called to compute the document to
|
|
1408
|
+
// insert as part of an upsert operation. We use this primarily to figure
|
|
1409
|
+
// out when to set the fields in $setOnInsert, if present.
|
|
1410
|
+
function _modify(doc, modifier, options = {}) {
|
|
1411
|
+
if (!_isPlainObject(modifier)) {
|
|
1412
|
+
throw MinimongoError('Modifier must be an object');
|
|
1413
|
+
}
|
|
1414
|
+
// Make sure the caller can't mutate our data structures.
|
|
1415
|
+
modifier = (0, ejson_1.clone)(modifier);
|
|
1416
|
+
const isModifier = isOperatorObject(modifier);
|
|
1417
|
+
const newDoc = isModifier ? (0, ejson_1.clone)(doc) : modifier;
|
|
1418
|
+
if (isModifier) {
|
|
1419
|
+
// apply modifiers to the doc.
|
|
1420
|
+
Object.keys(modifier).forEach(operator => {
|
|
1421
|
+
// Treat $setOnInsert as $set if this is an insert.
|
|
1422
|
+
const setOnInsert = options.isInsert && operator === '$setOnInsert';
|
|
1423
|
+
const modFunc = MODIFIERS[setOnInsert ? '$set' : operator];
|
|
1424
|
+
const operand = modifier[operator];
|
|
1425
|
+
if (!modFunc) {
|
|
1426
|
+
throw MinimongoError(`Invalid modifier specified ${operator}`);
|
|
1427
|
+
}
|
|
1428
|
+
Object.keys(operand).forEach(keypath => {
|
|
1429
|
+
const arg = operand[keypath];
|
|
1430
|
+
if (keypath === '') {
|
|
1431
|
+
throw MinimongoError('An empty update path is not valid.');
|
|
1432
|
+
}
|
|
1433
|
+
const keyparts = keypath.split('.');
|
|
1434
|
+
if (!keyparts.every(Boolean)) {
|
|
1435
|
+
throw MinimongoError(`The update path '${keypath}' contains an empty field name, ` +
|
|
1436
|
+
'which is not allowed.');
|
|
1437
|
+
}
|
|
1438
|
+
const target = findModTarget(newDoc, keyparts, {
|
|
1439
|
+
arrayIndices: options.arrayIndices,
|
|
1440
|
+
forbidArray: operator === '$rename',
|
|
1441
|
+
noCreate: NO_CREATE_MODIFIERS[operator]
|
|
1442
|
+
});
|
|
1443
|
+
modFunc(target, keyparts.pop(), arg, keypath, newDoc);
|
|
1444
|
+
});
|
|
1445
|
+
});
|
|
1446
|
+
if (doc._id && !(0, ejson_1.equals)(doc._id, newDoc._id)) {
|
|
1447
|
+
throw MinimongoError(`After applying the update to the document {_id: "${doc._id}", ...},` +
|
|
1448
|
+
' the (immutable) field \'_id\' was found to have been altered to ' +
|
|
1449
|
+
`_id: "${newDoc._id}"`);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
else {
|
|
1453
|
+
if (doc._id && modifier._id && !(0, ejson_1.equals)(doc._id, modifier._id)) {
|
|
1454
|
+
throw MinimongoError(`The _id field cannot be changed from {_id: "${doc._id}"} to ` +
|
|
1455
|
+
`{_id: "${modifier._id}"}`);
|
|
1456
|
+
}
|
|
1457
|
+
// replace the whole document
|
|
1458
|
+
assertHasValidFieldNames(modifier);
|
|
1459
|
+
}
|
|
1460
|
+
// move new document into place.
|
|
1461
|
+
Object.keys(doc).forEach(key => {
|
|
1462
|
+
// Note: this used to be for (var key in doc) however, this does not
|
|
1463
|
+
// work right in Opera. Deleting from a doc while iterating over it
|
|
1464
|
+
// would sometimes cause opera to skip some keys.
|
|
1465
|
+
if (key !== '_id') {
|
|
1466
|
+
delete doc[key];
|
|
1467
|
+
}
|
|
1468
|
+
});
|
|
1469
|
+
Object.keys(newDoc).forEach(key => {
|
|
1470
|
+
doc[key] = newDoc[key];
|
|
1471
|
+
});
|
|
1472
|
+
}
|
|
1473
|
+
;
|
|
1474
|
+
// checks if all field names in an object are valid
|
|
1475
|
+
function assertHasValidFieldNames(doc) {
|
|
1476
|
+
if (doc && typeof doc === 'object') {
|
|
1477
|
+
JSON.stringify(doc, (key, value) => {
|
|
1478
|
+
assertIsValidFieldName(key);
|
|
1479
|
+
return value;
|
|
1480
|
+
});
|
|
1481
|
+
}
|
|
1482
|
+
}
|
|
1483
|
+
const MODIFIERS = {
|
|
1484
|
+
$currentDate(target, field, arg) {
|
|
1485
|
+
if (typeof arg === 'object' && exports.hasOwn.call(arg, '$type')) {
|
|
1486
|
+
if (arg.$type !== 'date') {
|
|
1487
|
+
throw MinimongoError('Minimongo does currently only support the date type in ' +
|
|
1488
|
+
'$currentDate modifiers', { field });
|
|
1489
|
+
}
|
|
1490
|
+
}
|
|
1491
|
+
else if (arg !== true) {
|
|
1492
|
+
throw MinimongoError('Invalid $currentDate modifier', { field });
|
|
1493
|
+
}
|
|
1494
|
+
target[field] = new Date();
|
|
1495
|
+
},
|
|
1496
|
+
$min(target, field, arg) {
|
|
1497
|
+
if (typeof arg !== 'number') {
|
|
1498
|
+
throw MinimongoError('Modifier $min allowed for numbers only', { field });
|
|
1499
|
+
}
|
|
1500
|
+
if (field in target) {
|
|
1501
|
+
if (typeof target[field] !== 'number') {
|
|
1502
|
+
throw MinimongoError('Cannot apply $min modifier to non-number', { field });
|
|
1503
|
+
}
|
|
1504
|
+
if (target[field] > arg) {
|
|
1505
|
+
target[field] = arg;
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1508
|
+
else {
|
|
1509
|
+
target[field] = arg;
|
|
1510
|
+
}
|
|
1511
|
+
},
|
|
1512
|
+
$max(target, field, arg) {
|
|
1513
|
+
if (typeof arg !== 'number') {
|
|
1514
|
+
throw MinimongoError('Modifier $max allowed for numbers only', { field });
|
|
1515
|
+
}
|
|
1516
|
+
if (field in target) {
|
|
1517
|
+
if (typeof target[field] !== 'number') {
|
|
1518
|
+
throw MinimongoError('Cannot apply $max modifier to non-number', { field });
|
|
1519
|
+
}
|
|
1520
|
+
if (target[field] < arg) {
|
|
1521
|
+
target[field] = arg;
|
|
1522
|
+
}
|
|
1523
|
+
}
|
|
1524
|
+
else {
|
|
1525
|
+
target[field] = arg;
|
|
1526
|
+
}
|
|
1527
|
+
},
|
|
1528
|
+
$inc(target, field, arg) {
|
|
1529
|
+
if (typeof arg !== 'number') {
|
|
1530
|
+
throw MinimongoError('Modifier $inc allowed for numbers only', { field });
|
|
1531
|
+
}
|
|
1532
|
+
if (field in target) {
|
|
1533
|
+
if (typeof target[field] !== 'number') {
|
|
1534
|
+
throw MinimongoError('Cannot apply $inc modifier to non-number', { field });
|
|
1535
|
+
}
|
|
1536
|
+
target[field] += arg;
|
|
1537
|
+
}
|
|
1538
|
+
else {
|
|
1539
|
+
target[field] = arg;
|
|
1540
|
+
}
|
|
1541
|
+
},
|
|
1542
|
+
$set(target, field, arg) {
|
|
1543
|
+
if (target !== Object(target)) { // not an array or an object
|
|
1544
|
+
const error = MinimongoError('Cannot set property on non-object field', { field });
|
|
1545
|
+
error.setPropertyError = true;
|
|
1546
|
+
throw error;
|
|
1547
|
+
}
|
|
1548
|
+
if (target === null) {
|
|
1549
|
+
const error = MinimongoError('Cannot set property on null', { field });
|
|
1550
|
+
error.setPropertyError = true;
|
|
1551
|
+
throw error;
|
|
1552
|
+
}
|
|
1553
|
+
assertHasValidFieldNames(arg);
|
|
1554
|
+
target[field] = arg;
|
|
1555
|
+
},
|
|
1556
|
+
$setOnInsert(target, field, arg) {
|
|
1557
|
+
// converted to `$set` in `_modify`
|
|
1558
|
+
},
|
|
1559
|
+
$unset(target, field, arg) {
|
|
1560
|
+
if (target !== undefined) {
|
|
1561
|
+
if (target instanceof Array) {
|
|
1562
|
+
if (field in target) {
|
|
1563
|
+
target[field] = null;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
else {
|
|
1567
|
+
delete target[field];
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
},
|
|
1571
|
+
$push(target, field, arg) {
|
|
1572
|
+
if (target[field] === undefined) {
|
|
1573
|
+
target[field] = [];
|
|
1574
|
+
}
|
|
1575
|
+
if (!(target[field] instanceof Array)) {
|
|
1576
|
+
throw MinimongoError('Cannot apply $push modifier to non-array', { field });
|
|
1577
|
+
}
|
|
1578
|
+
if (!(arg && arg.$each)) {
|
|
1579
|
+
// Simple mode: not $each
|
|
1580
|
+
assertHasValidFieldNames(arg);
|
|
1581
|
+
target[field].push(arg);
|
|
1582
|
+
return;
|
|
1583
|
+
}
|
|
1584
|
+
// Fancy mode: $each (and maybe $slice and $sort and $position)
|
|
1585
|
+
const toPush = arg.$each;
|
|
1586
|
+
if (!(toPush instanceof Array)) {
|
|
1587
|
+
throw MinimongoError('$each must be an array', { field });
|
|
1588
|
+
}
|
|
1589
|
+
assertHasValidFieldNames(toPush);
|
|
1590
|
+
// Parse $position
|
|
1591
|
+
let position = undefined;
|
|
1592
|
+
if ('$position' in arg) {
|
|
1593
|
+
if (typeof arg.$position !== 'number') {
|
|
1594
|
+
throw MinimongoError('$position must be a numeric value', { field });
|
|
1595
|
+
}
|
|
1596
|
+
// XXX should check to make sure integer
|
|
1597
|
+
if (arg.$position < 0) {
|
|
1598
|
+
throw MinimongoError('$position in $push must be zero or positive', { field });
|
|
1599
|
+
}
|
|
1600
|
+
position = arg.$position;
|
|
1601
|
+
}
|
|
1602
|
+
// Parse $slice.
|
|
1603
|
+
let slice = undefined;
|
|
1604
|
+
if ('$slice' in arg) {
|
|
1605
|
+
if (typeof arg.$slice !== 'number') {
|
|
1606
|
+
throw MinimongoError('$slice must be a numeric value', { field });
|
|
1607
|
+
}
|
|
1608
|
+
// XXX should check to make sure integer
|
|
1609
|
+
slice = arg.$slice;
|
|
1610
|
+
}
|
|
1611
|
+
// Parse $sort.
|
|
1612
|
+
let sortFunction = undefined;
|
|
1613
|
+
if (arg.$sort) {
|
|
1614
|
+
if (slice === undefined) {
|
|
1615
|
+
throw MinimongoError('$sort requires $slice to be present', { field });
|
|
1616
|
+
}
|
|
1617
|
+
// XXX this allows us to use a $sort whose value is an array, but that's
|
|
1618
|
+
// actually an extension of the Node driver, so it won't work
|
|
1619
|
+
// server-side. Could be confusing!
|
|
1620
|
+
// XXX is it correct that we don't do geo-stuff here?
|
|
1621
|
+
sortFunction = new minimongo_sorter_1.default(arg.$sort).getComparator();
|
|
1622
|
+
toPush.forEach(element => {
|
|
1623
|
+
if (exports._f._type(element) !== 3) {
|
|
1624
|
+
throw MinimongoError('$push like modifiers using $sort require all elements to be ' +
|
|
1625
|
+
'objects', { field });
|
|
1626
|
+
}
|
|
1627
|
+
});
|
|
1628
|
+
}
|
|
1629
|
+
// Actually push.
|
|
1630
|
+
if (position === undefined) {
|
|
1631
|
+
toPush.forEach(element => {
|
|
1632
|
+
target[field].push(element);
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
else {
|
|
1636
|
+
target[field].splice(position, 0, ...toPush);
|
|
1637
|
+
}
|
|
1638
|
+
// Actually sort.
|
|
1639
|
+
if (sortFunction) {
|
|
1640
|
+
target[field].sort(sortFunction);
|
|
1641
|
+
}
|
|
1642
|
+
// Actually slice.
|
|
1643
|
+
if (slice !== undefined) {
|
|
1644
|
+
if (slice === 0) {
|
|
1645
|
+
target[field] = []; // differs from Array.slice!
|
|
1646
|
+
}
|
|
1647
|
+
else if (slice < 0) {
|
|
1648
|
+
target[field] = target[field].slice(slice);
|
|
1649
|
+
}
|
|
1650
|
+
else {
|
|
1651
|
+
target[field] = target[field].slice(0, slice);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
},
|
|
1655
|
+
$pushAll(target, field, arg) {
|
|
1656
|
+
if (!(typeof arg === 'object' && arg instanceof Array)) {
|
|
1657
|
+
throw MinimongoError('Modifier $pushAll/pullAll allowed for arrays only');
|
|
1658
|
+
}
|
|
1659
|
+
assertHasValidFieldNames(arg);
|
|
1660
|
+
const toPush = target[field];
|
|
1661
|
+
if (toPush === undefined) {
|
|
1662
|
+
target[field] = arg;
|
|
1663
|
+
}
|
|
1664
|
+
else if (!(toPush instanceof Array)) {
|
|
1665
|
+
throw MinimongoError('Cannot apply $pushAll modifier to non-array', { field });
|
|
1666
|
+
}
|
|
1667
|
+
else {
|
|
1668
|
+
toPush.push(...arg);
|
|
1669
|
+
}
|
|
1670
|
+
},
|
|
1671
|
+
$addToSet(target, field, arg) {
|
|
1672
|
+
let isEach = false;
|
|
1673
|
+
if (typeof arg === 'object') {
|
|
1674
|
+
// check if first key is '$each'
|
|
1675
|
+
const keys = Object.keys(arg);
|
|
1676
|
+
if (keys[0] === '$each') {
|
|
1677
|
+
isEach = true;
|
|
1678
|
+
}
|
|
1679
|
+
}
|
|
1680
|
+
const values = isEach ? arg.$each : [arg];
|
|
1681
|
+
assertHasValidFieldNames(values);
|
|
1682
|
+
const toAdd = target[field];
|
|
1683
|
+
if (toAdd === undefined) {
|
|
1684
|
+
target[field] = values;
|
|
1685
|
+
}
|
|
1686
|
+
else if (!(toAdd instanceof Array)) {
|
|
1687
|
+
throw MinimongoError('Cannot apply $addToSet modifier to non-array', { field });
|
|
1688
|
+
}
|
|
1689
|
+
else {
|
|
1690
|
+
values.forEach(value => {
|
|
1691
|
+
if (toAdd.some(element => exports._f._equal(value, element))) {
|
|
1692
|
+
return;
|
|
1693
|
+
}
|
|
1694
|
+
toAdd.push(value);
|
|
1695
|
+
});
|
|
1696
|
+
}
|
|
1697
|
+
},
|
|
1698
|
+
$pop(target, field, arg) {
|
|
1699
|
+
if (target === undefined) {
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
const toPop = target[field];
|
|
1703
|
+
if (toPop === undefined) {
|
|
1704
|
+
return;
|
|
1705
|
+
}
|
|
1706
|
+
if (!(toPop instanceof Array)) {
|
|
1707
|
+
throw MinimongoError('Cannot apply $pop modifier to non-array', { field });
|
|
1708
|
+
}
|
|
1709
|
+
if (typeof arg === 'number' && arg < 0) {
|
|
1710
|
+
toPop.splice(0, 1);
|
|
1711
|
+
}
|
|
1712
|
+
else {
|
|
1713
|
+
toPop.pop();
|
|
1714
|
+
}
|
|
1715
|
+
},
|
|
1716
|
+
$pull(target, field, arg) {
|
|
1717
|
+
if (target === undefined) {
|
|
1718
|
+
return;
|
|
1719
|
+
}
|
|
1720
|
+
const toPull = target[field];
|
|
1721
|
+
if (toPull === undefined) {
|
|
1722
|
+
return;
|
|
1723
|
+
}
|
|
1724
|
+
if (!(toPull instanceof Array)) {
|
|
1725
|
+
throw MinimongoError('Cannot apply $pull/pullAll modifier to non-array', { field });
|
|
1726
|
+
}
|
|
1727
|
+
let out;
|
|
1728
|
+
if (arg != null && typeof arg === 'object' && !(arg instanceof Array)) {
|
|
1729
|
+
// XXX would be much nicer to compile this once, rather than
|
|
1730
|
+
// for each document we modify.. but usually we're not
|
|
1731
|
+
// modifying that many documents, so we'll let it slide for
|
|
1732
|
+
// now
|
|
1733
|
+
// XXX Minimongo.Matcher isn't up for the job, because we need
|
|
1734
|
+
// to permit stuff like {$pull: {a: {$gt: 4}}}.. something
|
|
1735
|
+
// like {$gt: 4} is not normally a complete selector.
|
|
1736
|
+
// same issue as $elemMatch possibly?
|
|
1737
|
+
const matcher = new minimongo_matcher_1.MinimongoMatcher(arg);
|
|
1738
|
+
out = toPull.filter(element => !matcher.documentMatches(element).result);
|
|
1739
|
+
}
|
|
1740
|
+
else {
|
|
1741
|
+
out = toPull.filter(element => !exports._f._equal(element, arg));
|
|
1742
|
+
}
|
|
1743
|
+
target[field] = out;
|
|
1744
|
+
},
|
|
1745
|
+
$pullAll(target, field, arg) {
|
|
1746
|
+
if (!(typeof arg === 'object' && arg instanceof Array)) {
|
|
1747
|
+
throw MinimongoError('Modifier $pushAll/pullAll allowed for arrays only', { field });
|
|
1748
|
+
}
|
|
1749
|
+
if (target === undefined) {
|
|
1750
|
+
return;
|
|
1751
|
+
}
|
|
1752
|
+
const toPull = target[field];
|
|
1753
|
+
if (toPull === undefined) {
|
|
1754
|
+
return;
|
|
1755
|
+
}
|
|
1756
|
+
if (!(toPull instanceof Array)) {
|
|
1757
|
+
throw MinimongoError('Cannot apply $pull/pullAll modifier to non-array', { field });
|
|
1758
|
+
}
|
|
1759
|
+
target[field] = toPull.filter(object => !arg.some(element => exports._f._equal(object, element)));
|
|
1760
|
+
},
|
|
1761
|
+
$rename(target, field, arg, keypath, doc) {
|
|
1762
|
+
// no idea why mongo has this restriction..
|
|
1763
|
+
if (keypath === arg) {
|
|
1764
|
+
throw MinimongoError('$rename source must differ from target', { field });
|
|
1765
|
+
}
|
|
1766
|
+
if (target === null) {
|
|
1767
|
+
throw MinimongoError('$rename source field invalid', { field });
|
|
1768
|
+
}
|
|
1769
|
+
if (typeof arg !== 'string') {
|
|
1770
|
+
throw MinimongoError('$rename target must be a string', { field });
|
|
1771
|
+
}
|
|
1772
|
+
if (arg.includes('\0')) {
|
|
1773
|
+
// Null bytes are not allowed in Mongo field names
|
|
1774
|
+
// https://docs.mongodb.com/manual/reference/limits/#Restrictions-on-Field-Names
|
|
1775
|
+
throw MinimongoError('The \'to\' field for $rename cannot contain an embedded null byte', { field });
|
|
1776
|
+
}
|
|
1777
|
+
if (target === undefined) {
|
|
1778
|
+
return;
|
|
1779
|
+
}
|
|
1780
|
+
const object = target[field];
|
|
1781
|
+
delete target[field];
|
|
1782
|
+
const keyparts = arg.split('.');
|
|
1783
|
+
const target2 = findModTarget(doc, keyparts, { forbidArray: true });
|
|
1784
|
+
if (target2 === null) {
|
|
1785
|
+
throw MinimongoError('$rename target field invalid', { field });
|
|
1786
|
+
}
|
|
1787
|
+
target2[keyparts.pop()] = object;
|
|
1788
|
+
},
|
|
1789
|
+
$bit(target, field, arg) {
|
|
1790
|
+
// XXX mongo only supports $bit on integers, and we only support
|
|
1791
|
+
// native javascript numbers (doubles) so far, so we can't support $bit
|
|
1792
|
+
throw MinimongoError('$bit is not supported', { field });
|
|
1793
|
+
},
|
|
1794
|
+
$v() {
|
|
1795
|
+
// As discussed in https://github.com/meteor/meteor/issues/9623,
|
|
1796
|
+
// the `$v` operator is not needed by Meteor, but problems can occur if
|
|
1797
|
+
// it's not at least callable (as of Mongo >= 3.6). It's defined here as
|
|
1798
|
+
// a no-op to work around these problems.
|
|
1799
|
+
}
|
|
1800
|
+
};
|
|
1801
|
+
const NO_CREATE_MODIFIERS = {
|
|
1802
|
+
$pop: true,
|
|
1803
|
+
$pull: true,
|
|
1804
|
+
$pullAll: true,
|
|
1805
|
+
$rename: true,
|
|
1806
|
+
$unset: true
|
|
1807
|
+
};
|
|
1808
|
+
const invalidCharMsg = {
|
|
1809
|
+
$: 'start with \'$\'',
|
|
1810
|
+
'.': 'contain \'.\'',
|
|
1811
|
+
'\0': 'contain null bytes'
|
|
1812
|
+
};
|
|
1813
|
+
function assertIsValidFieldName(key) {
|
|
1814
|
+
let match;
|
|
1815
|
+
if (typeof key === 'string' && (match = key.match(/^\$|\.|\0/))) {
|
|
1816
|
+
throw MinimongoError(`Key ${key} must not ${invalidCharMsg[match[0]]}`);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
// for a.b.c.2.d.e, keyparts should be ['a', 'b', 'c', '2', 'd', 'e'],
|
|
1820
|
+
// and then you would operate on the 'e' property of the returned
|
|
1821
|
+
// object.
|
|
1822
|
+
//
|
|
1823
|
+
// if options.noCreate is falsey, creates intermediate levels of
|
|
1824
|
+
// structure as necessary, like mkdir -p (and raises an exception if
|
|
1825
|
+
// that would mean giving a non-numeric property to an array.) if
|
|
1826
|
+
// options.noCreate is true, return undefined instead.
|
|
1827
|
+
//
|
|
1828
|
+
// may modify the last element of keyparts to signal to the caller that it needs
|
|
1829
|
+
// to use a different value to index into the returned object (for example,
|
|
1830
|
+
// ['a', '01'] -> ['a', 1]).
|
|
1831
|
+
//
|
|
1832
|
+
// if forbidArray is true, return null if the keypath goes through an array.
|
|
1833
|
+
//
|
|
1834
|
+
// if options.arrayIndices is set, use its first element for the (first) '$' in
|
|
1835
|
+
// the path.
|
|
1836
|
+
function findModTarget(doc, keyparts, options = {}) {
|
|
1837
|
+
let usedArrayIndex = false;
|
|
1838
|
+
for (let i = 0; i < keyparts.length; i++) {
|
|
1839
|
+
const last = i === keyparts.length - 1;
|
|
1840
|
+
let keypart = keyparts[i];
|
|
1841
|
+
if (!isIndexable(doc)) {
|
|
1842
|
+
if (options.noCreate) {
|
|
1843
|
+
return undefined;
|
|
1844
|
+
}
|
|
1845
|
+
const error = MinimongoError(`cannot use the part '${keypart}' to traverse ${doc}`);
|
|
1846
|
+
error.setPropertyError = true;
|
|
1847
|
+
throw error;
|
|
1848
|
+
}
|
|
1849
|
+
if (doc instanceof Array) {
|
|
1850
|
+
if (options.forbidArray) {
|
|
1851
|
+
return null;
|
|
1852
|
+
}
|
|
1853
|
+
if (keypart === '$') {
|
|
1854
|
+
if (usedArrayIndex) {
|
|
1855
|
+
throw MinimongoError('Too many positional (i.e. \'$\') elements');
|
|
1856
|
+
}
|
|
1857
|
+
if (!options.arrayIndices || !options.arrayIndices.length) {
|
|
1858
|
+
throw MinimongoError('The positional operator did not find the match needed from the ' +
|
|
1859
|
+
'query');
|
|
1860
|
+
}
|
|
1861
|
+
keypart = options.arrayIndices[0];
|
|
1862
|
+
usedArrayIndex = true;
|
|
1863
|
+
}
|
|
1864
|
+
else if (isNumericKey(keypart)) {
|
|
1865
|
+
keypart = parseInt(keypart);
|
|
1866
|
+
}
|
|
1867
|
+
else {
|
|
1868
|
+
if (options.noCreate) {
|
|
1869
|
+
return undefined;
|
|
1870
|
+
}
|
|
1871
|
+
throw MinimongoError(`can't append to array using string field name [${keypart}]`);
|
|
1872
|
+
}
|
|
1873
|
+
if (last) {
|
|
1874
|
+
keyparts[i] = keypart; // handle 'a.01'
|
|
1875
|
+
}
|
|
1876
|
+
if (options.noCreate && keypart >= doc.length) {
|
|
1877
|
+
return undefined;
|
|
1878
|
+
}
|
|
1879
|
+
while (doc.length < keypart) {
|
|
1880
|
+
doc.push(null);
|
|
1881
|
+
}
|
|
1882
|
+
if (!last) {
|
|
1883
|
+
if (doc.length === keypart) {
|
|
1884
|
+
doc.push({});
|
|
1885
|
+
}
|
|
1886
|
+
else if (typeof doc[keypart] !== 'object') {
|
|
1887
|
+
throw MinimongoError(`can't modify field '${keyparts[i + 1]}' of list value ` +
|
|
1888
|
+
JSON.stringify(doc[keypart]));
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
else {
|
|
1893
|
+
assertIsValidFieldName(keypart);
|
|
1894
|
+
if (!(keypart in doc)) {
|
|
1895
|
+
if (options.noCreate) {
|
|
1896
|
+
return undefined;
|
|
1897
|
+
}
|
|
1898
|
+
if (!last) {
|
|
1899
|
+
doc[keypart] = {};
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
}
|
|
1903
|
+
if (last) {
|
|
1904
|
+
return doc;
|
|
1905
|
+
}
|
|
1906
|
+
doc = doc[keypart];
|
|
1907
|
+
}
|
|
1908
|
+
// notreached
|
|
1909
|
+
}
|
|
1910
|
+
function combineImportantPathsIntoProjection(paths, projection) {
|
|
1911
|
+
const details = projectionDetails(projection);
|
|
1912
|
+
// merge the paths to include
|
|
1913
|
+
const tree = pathsToTree(paths, path => true, (node, path, fullPath) => true, details.tree);
|
|
1914
|
+
const mergedProjection = treeToPaths(tree);
|
|
1915
|
+
if (details.including) {
|
|
1916
|
+
// both selector and projection are pointing on fields to include
|
|
1917
|
+
// so we can just return the merged tree
|
|
1918
|
+
return mergedProjection;
|
|
1919
|
+
}
|
|
1920
|
+
// selector is pointing at fields to include
|
|
1921
|
+
// projection is pointing at fields to exclude
|
|
1922
|
+
// make sure we don't exclude important paths
|
|
1923
|
+
const mergedExclProjection = {};
|
|
1924
|
+
Object.keys(mergedProjection).forEach(path => {
|
|
1925
|
+
if (!mergedProjection[path]) {
|
|
1926
|
+
mergedExclProjection[path] = false;
|
|
1927
|
+
}
|
|
1928
|
+
});
|
|
1929
|
+
return mergedExclProjection;
|
|
1930
|
+
}
|
|
1931
|
+
// Returns a set of key paths similar to
|
|
1932
|
+
// { 'foo.bar': 1, 'a.b.c': 1 }
|
|
1933
|
+
function treeToPaths(tree, prefix = '') {
|
|
1934
|
+
const result = {};
|
|
1935
|
+
Object.keys(tree).forEach(key => {
|
|
1936
|
+
const value = tree[key];
|
|
1937
|
+
if (value === Object(value)) {
|
|
1938
|
+
Object.assign(result, treeToPaths(value, `${prefix + key}.`));
|
|
1939
|
+
}
|
|
1940
|
+
else {
|
|
1941
|
+
result[prefix + key] = value;
|
|
1942
|
+
}
|
|
1943
|
+
});
|
|
1944
|
+
return result;
|
|
1945
|
+
}
|
|
1946
|
+
function _pathsElidingNumericKeys(paths) {
|
|
1947
|
+
return paths.map(path => path.split('.').filter(part => !isNumericKey(part)).join('.'));
|
|
1948
|
+
}
|
|
1949
|
+
// Knows how to compile a fields projection to a predicate function.
|
|
1950
|
+
// @returns - Function: a closure that filters out an object according to the
|
|
1951
|
+
// fields projection rules:
|
|
1952
|
+
// @param obj - Object: MongoDB-styled document
|
|
1953
|
+
// @returns - Object: a document with the fields filtered out
|
|
1954
|
+
// according to projection rules. Doesn't retain subfields
|
|
1955
|
+
// of passed argument.
|
|
1956
|
+
function _compileProjection(fields) {
|
|
1957
|
+
_checkSupportedProjection(fields);
|
|
1958
|
+
const _idProjection = fields._id === undefined ? true : fields._id;
|
|
1959
|
+
const details = projectionDetails(fields);
|
|
1960
|
+
// returns transformed doc according to ruleTree
|
|
1961
|
+
const transform = (doc, ruleTree) => {
|
|
1962
|
+
// Special case for "sets"
|
|
1963
|
+
if (Array.isArray(doc)) {
|
|
1964
|
+
return doc.map(subdoc => transform(subdoc, ruleTree));
|
|
1965
|
+
}
|
|
1966
|
+
const result = details.including ? {} : (0, ejson_1.clone)(doc);
|
|
1967
|
+
Object.keys(ruleTree).forEach(key => {
|
|
1968
|
+
if (doc == null || !exports.hasOwn.call(doc, key)) {
|
|
1969
|
+
return;
|
|
1970
|
+
}
|
|
1971
|
+
const rule = ruleTree[key];
|
|
1972
|
+
if (rule === Object(rule)) {
|
|
1973
|
+
// For sub-objects/subsets we branch
|
|
1974
|
+
if (doc[key] === Object(doc[key])) {
|
|
1975
|
+
result[key] = transform(doc[key], rule);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
else if (details.including) {
|
|
1979
|
+
// Otherwise we don't even touch this subfield
|
|
1980
|
+
result[key] = (0, ejson_1.clone)(doc[key]);
|
|
1981
|
+
}
|
|
1982
|
+
else {
|
|
1983
|
+
delete result[key];
|
|
1984
|
+
}
|
|
1985
|
+
});
|
|
1986
|
+
return doc != null ? result : doc;
|
|
1987
|
+
};
|
|
1988
|
+
return doc => {
|
|
1989
|
+
const result = transform(doc, details.tree);
|
|
1990
|
+
if (_idProjection && exports.hasOwn.call(doc, '_id')) {
|
|
1991
|
+
result._id = doc._id;
|
|
1992
|
+
}
|
|
1993
|
+
if (!_idProjection && exports.hasOwn.call(result, '_id')) {
|
|
1994
|
+
delete result._id;
|
|
1995
|
+
}
|
|
1996
|
+
return result;
|
|
1997
|
+
};
|
|
1998
|
+
}
|