@herdwatch/lokijs 1.5.8-dev.6 → 1.5.12-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/package.json +3 -2
- package/src/incremental-indexeddb-adapter.js +524 -179
- package/src/loki-indexed-adapter.js +18 -5
- package/src/lokijs.js +785 -568
- package/test.db +1 -0
package/src/lokijs.js
CHANGED
|
@@ -22,6 +22,36 @@
|
|
|
22
22
|
|
|
23
23
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
24
24
|
|
|
25
|
+
function deepFreeze(obj) {
|
|
26
|
+
var prop, i;
|
|
27
|
+
if (Array.isArray(obj)) {
|
|
28
|
+
for (i = 0; i < obj.length; i++) {
|
|
29
|
+
deepFreeze(obj[i]);
|
|
30
|
+
}
|
|
31
|
+
freeze(obj);
|
|
32
|
+
} else if (obj !== null && (typeof obj === 'object')) {
|
|
33
|
+
for (prop in obj) {
|
|
34
|
+
if (obj.hasOwnProperty(prop)) {
|
|
35
|
+
deepFreeze(obj[prop]);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
freeze(obj);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function freeze(obj) {
|
|
43
|
+
if (!Object.isFrozen(obj)) {
|
|
44
|
+
Object.freeze(obj);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function unFreeze(obj) {
|
|
49
|
+
if (!Object.isFrozen(obj)) {
|
|
50
|
+
return obj;
|
|
51
|
+
}
|
|
52
|
+
return clone(obj, 'shallow');
|
|
53
|
+
}
|
|
54
|
+
|
|
25
55
|
var Utils = {
|
|
26
56
|
copyProperties: function (src, dest) {
|
|
27
57
|
var prop;
|
|
@@ -100,12 +130,12 @@
|
|
|
100
130
|
return object[path];
|
|
101
131
|
}
|
|
102
132
|
|
|
103
|
-
if (typeof(path) === "string") {
|
|
133
|
+
if (typeof (path) === "string") {
|
|
104
134
|
path = path.split(".");
|
|
105
135
|
}
|
|
106
136
|
|
|
107
137
|
if (!Array.isArray(path)) {
|
|
108
|
-
throw new Error("path must be a string or array. Found " + typeof(path));
|
|
138
|
+
throw new Error("path must be a string or array. Found " + typeof (path));
|
|
109
139
|
}
|
|
110
140
|
|
|
111
141
|
var index = 0,
|
|
@@ -150,7 +180,7 @@
|
|
|
150
180
|
case false: t1 = 3; break;
|
|
151
181
|
case true: t1 = 4; break;
|
|
152
182
|
case "": t1 = 5; break;
|
|
153
|
-
default: t1 = (prop1 === prop1)?9:0; break;
|
|
183
|
+
default: t1 = (prop1 === prop1) ? 9 : 0; break;
|
|
154
184
|
}
|
|
155
185
|
|
|
156
186
|
switch (prop2) {
|
|
@@ -159,12 +189,12 @@
|
|
|
159
189
|
case false: t2 = 3; break;
|
|
160
190
|
case true: t2 = 4; break;
|
|
161
191
|
case "": t2 = 5; break;
|
|
162
|
-
default: t2 = (prop2 === prop2)?9:0; break;
|
|
192
|
+
default: t2 = (prop2 === prop2) ? 9 : 0; break;
|
|
163
193
|
}
|
|
164
194
|
|
|
165
195
|
// one or both is edge case
|
|
166
196
|
if (t1 !== 9 || t2 !== 9) {
|
|
167
|
-
return (t1===t2);
|
|
197
|
+
return (t1 === t2);
|
|
168
198
|
}
|
|
169
199
|
}
|
|
170
200
|
|
|
@@ -202,7 +232,7 @@
|
|
|
202
232
|
case true: t1 = 4; break;
|
|
203
233
|
case "": t1 = 5; break;
|
|
204
234
|
// if strict equal probably 0 so sort higher, otherwise probably NaN so sort lower than even null
|
|
205
|
-
default: t1 = (prop1 === prop1)?9:0; break;
|
|
235
|
+
default: t1 = (prop1 === prop1) ? 9 : 0; break;
|
|
206
236
|
}
|
|
207
237
|
|
|
208
238
|
switch (prop2) {
|
|
@@ -211,12 +241,12 @@
|
|
|
211
241
|
case false: t2 = 3; break;
|
|
212
242
|
case true: t2 = 4; break;
|
|
213
243
|
case "": t2 = 5; break;
|
|
214
|
-
default: t2 = (prop2 === prop2)?9:0; break;
|
|
244
|
+
default: t2 = (prop2 === prop2) ? 9 : 0; break;
|
|
215
245
|
}
|
|
216
246
|
|
|
217
247
|
// one or both is edge case
|
|
218
248
|
if (t1 !== 9 || t2 !== 9) {
|
|
219
|
-
return (t1===t2)?equal:(t1<t2);
|
|
249
|
+
return (t1 === t2) ? equal : (t1 < t2);
|
|
220
250
|
}
|
|
221
251
|
}
|
|
222
252
|
|
|
@@ -269,7 +299,7 @@
|
|
|
269
299
|
case true: t1 = 4; break;
|
|
270
300
|
case "": t1 = 5; break;
|
|
271
301
|
// NaN 0
|
|
272
|
-
default: t1 = (prop1 === prop1)?9:0; break;
|
|
302
|
+
default: t1 = (prop1 === prop1) ? 9 : 0; break;
|
|
273
303
|
}
|
|
274
304
|
|
|
275
305
|
switch (prop2) {
|
|
@@ -278,12 +308,12 @@
|
|
|
278
308
|
case false: t2 = 3; break;
|
|
279
309
|
case true: t2 = 4; break;
|
|
280
310
|
case "": t2 = 5; break;
|
|
281
|
-
default: t2 = (prop2 === prop2)?9:0; break;
|
|
311
|
+
default: t2 = (prop2 === prop2) ? 9 : 0; break;
|
|
282
312
|
}
|
|
283
313
|
|
|
284
314
|
// one or both is edge case
|
|
285
315
|
if (t1 !== 9 || t2 !== 9) {
|
|
286
|
-
return (t1===t2)?equal:(t1>t2);
|
|
316
|
+
return (t1 === t2) ? equal : (t1 > t2);
|
|
287
317
|
}
|
|
288
318
|
}
|
|
289
319
|
|
|
@@ -376,30 +406,31 @@
|
|
|
376
406
|
* @param {array} paths - array of properties to drill into
|
|
377
407
|
* @param {function} fun - evaluation function to test with
|
|
378
408
|
* @param {any} value - comparative value to also pass to (compare) fun
|
|
409
|
+
* @param {any} extra - extra arg to also pass to compare fun
|
|
379
410
|
* @param {number} poffset - index of the item in 'paths' to start the sub-scan from
|
|
380
411
|
*/
|
|
381
|
-
function dotSubScan(root, paths, fun, value, poffset) {
|
|
412
|
+
function dotSubScan(root, paths, fun, value, extra, poffset) {
|
|
382
413
|
var pathOffset = poffset || 0;
|
|
383
414
|
var path = paths[pathOffset];
|
|
384
415
|
|
|
385
416
|
var valueFound = false;
|
|
386
417
|
var element;
|
|
387
|
-
if (typeof root === 'object' && path in root) {
|
|
418
|
+
if (root !== null && typeof root === 'object' && path in root) {
|
|
388
419
|
element = root[path];
|
|
389
420
|
}
|
|
390
421
|
if (pathOffset + 1 >= paths.length) {
|
|
391
422
|
// if we have already expanded out the dot notation,
|
|
392
423
|
// then just evaluate the test function and value on the element
|
|
393
|
-
valueFound = fun(element, value);
|
|
424
|
+
valueFound = fun(element, value, extra);
|
|
394
425
|
} else if (Array.isArray(element)) {
|
|
395
426
|
for (var index = 0, len = element.length; index < len; index += 1) {
|
|
396
|
-
valueFound = dotSubScan(element[index], paths, fun, value, pathOffset + 1);
|
|
427
|
+
valueFound = dotSubScan(element[index], paths, fun, value, extra, pathOffset + 1);
|
|
397
428
|
if (valueFound === true) {
|
|
398
429
|
break;
|
|
399
430
|
}
|
|
400
431
|
}
|
|
401
432
|
} else {
|
|
402
|
-
valueFound = dotSubScan(element, paths, fun, value, pathOffset + 1);
|
|
433
|
+
valueFound = dotSubScan(element, paths, fun, value, extra, pathOffset + 1);
|
|
403
434
|
}
|
|
404
435
|
|
|
405
436
|
return valueFound;
|
|
@@ -418,10 +449,10 @@
|
|
|
418
449
|
return null;
|
|
419
450
|
}
|
|
420
451
|
|
|
421
|
-
function doQueryOp(val, op) {
|
|
452
|
+
function doQueryOp(val, op, record) {
|
|
422
453
|
for (var p in op) {
|
|
423
454
|
if (hasOwnProperty.call(op, p)) {
|
|
424
|
-
return LokiOps[p](val, op[p]);
|
|
455
|
+
return LokiOps[p](val, op[p], record);
|
|
425
456
|
}
|
|
426
457
|
}
|
|
427
458
|
return false;
|
|
@@ -503,6 +534,10 @@
|
|
|
503
534
|
return b.indexOf(a) !== -1;
|
|
504
535
|
},
|
|
505
536
|
|
|
537
|
+
$inSet: function(a, b) {
|
|
538
|
+
return b.has(a);
|
|
539
|
+
},
|
|
540
|
+
|
|
506
541
|
$nin: function (a, b) {
|
|
507
542
|
return b.indexOf(a) === -1;
|
|
508
543
|
},
|
|
@@ -553,24 +588,24 @@
|
|
|
553
588
|
|
|
554
589
|
$elemMatch: function (a, b) {
|
|
555
590
|
if (Array.isArray(a)) {
|
|
556
|
-
return a.some(function(item){
|
|
557
|
-
return Object.keys(b).every(function(property) {
|
|
591
|
+
return a.some(function (item) {
|
|
592
|
+
return Object.keys(b).every(function (property) {
|
|
558
593
|
var filter = b[property];
|
|
559
594
|
if (!(typeof filter === 'object' && filter)) {
|
|
560
595
|
filter = { $eq: filter };
|
|
561
596
|
}
|
|
562
597
|
|
|
563
598
|
if (property.indexOf('.') !== -1) {
|
|
564
|
-
return dotSubScan(item, property.split('.'), doQueryOp, b[property]);
|
|
599
|
+
return dotSubScan(item, property.split('.'), doQueryOp, b[property], item);
|
|
565
600
|
}
|
|
566
|
-
return doQueryOp(item[property], filter);
|
|
601
|
+
return doQueryOp(item[property], filter, item);
|
|
567
602
|
});
|
|
568
603
|
});
|
|
569
604
|
}
|
|
570
605
|
return false;
|
|
571
606
|
},
|
|
572
607
|
|
|
573
|
-
$type: function (a, b) {
|
|
608
|
+
$type: function (a, b, record) {
|
|
574
609
|
var type = typeof a;
|
|
575
610
|
if (type === 'object') {
|
|
576
611
|
if (Array.isArray(a)) {
|
|
@@ -579,23 +614,23 @@
|
|
|
579
614
|
type = 'date';
|
|
580
615
|
}
|
|
581
616
|
}
|
|
582
|
-
return (typeof b !== 'object') ? (type === b) : doQueryOp(type, b);
|
|
617
|
+
return (typeof b !== 'object') ? (type === b) : doQueryOp(type, b, record);
|
|
583
618
|
},
|
|
584
619
|
|
|
585
|
-
$finite: function(a, b) {
|
|
620
|
+
$finite: function (a, b) {
|
|
586
621
|
return (b === isFinite(a));
|
|
587
622
|
},
|
|
588
623
|
|
|
589
|
-
$size: function (a, b) {
|
|
624
|
+
$size: function (a, b, record) {
|
|
590
625
|
if (Array.isArray(a)) {
|
|
591
|
-
return (typeof b !== 'object') ? (a.length === b) : doQueryOp(a.length, b);
|
|
626
|
+
return (typeof b !== 'object') ? (a.length === b) : doQueryOp(a.length, b, record);
|
|
592
627
|
}
|
|
593
628
|
return false;
|
|
594
629
|
},
|
|
595
630
|
|
|
596
|
-
$len: function (a, b) {
|
|
631
|
+
$len: function (a, b, record) {
|
|
597
632
|
if (typeof a === 'string') {
|
|
598
|
-
return (typeof b !== 'object') ? (a.length === b) : doQueryOp(a.length, b);
|
|
633
|
+
return (typeof b !== 'object') ? (a.length === b) : doQueryOp(a.length, b, record);
|
|
599
634
|
}
|
|
600
635
|
return false;
|
|
601
636
|
},
|
|
@@ -608,22 +643,22 @@
|
|
|
608
643
|
// a is the value in the collection
|
|
609
644
|
// b is the nested query operation (for '$not')
|
|
610
645
|
// or an array of nested query operations (for '$and' and '$or')
|
|
611
|
-
$not: function (a, b) {
|
|
612
|
-
return !doQueryOp(a, b);
|
|
646
|
+
$not: function (a, b, record) {
|
|
647
|
+
return !doQueryOp(a, b, record);
|
|
613
648
|
},
|
|
614
649
|
|
|
615
|
-
$and: function (a, b) {
|
|
650
|
+
$and: function (a, b, record) {
|
|
616
651
|
for (var idx = 0, len = b.length; idx < len; idx += 1) {
|
|
617
|
-
if (!doQueryOp(a, b[idx])) {
|
|
652
|
+
if (!doQueryOp(a, b[idx], record)) {
|
|
618
653
|
return false;
|
|
619
654
|
}
|
|
620
655
|
}
|
|
621
656
|
return true;
|
|
622
657
|
},
|
|
623
658
|
|
|
624
|
-
$or: function (a, b) {
|
|
659
|
+
$or: function (a, b, record) {
|
|
625
660
|
for (var idx = 0, len = b.length; idx < len; idx += 1) {
|
|
626
|
-
if (doQueryOp(a, b[idx])) {
|
|
661
|
+
if (doQueryOp(a, b[idx], record)) {
|
|
627
662
|
return true;
|
|
628
663
|
}
|
|
629
664
|
}
|
|
@@ -639,6 +674,21 @@
|
|
|
639
674
|
}
|
|
640
675
|
};
|
|
641
676
|
|
|
677
|
+
// ops that can be used with { $$op: 'column-name' } syntax
|
|
678
|
+
var valueLevelOps = ['$eq', '$aeq', '$ne', '$dteq', '$gt', '$gte', '$lt', '$lte', '$jgt', '$jgte', '$jlt', '$jlte', '$type'];
|
|
679
|
+
valueLevelOps.forEach(function (op) {
|
|
680
|
+
var fun = LokiOps[op];
|
|
681
|
+
LokiOps['$' + op] = function (a, spec, record) {
|
|
682
|
+
if (typeof spec === 'string') {
|
|
683
|
+
return fun(a, record[spec]);
|
|
684
|
+
} else if (typeof spec === 'function') {
|
|
685
|
+
return fun(a, spec(record));
|
|
686
|
+
} else {
|
|
687
|
+
throw new Error('Invalid argument to $$ matcher');
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
});
|
|
691
|
+
|
|
642
692
|
// if an op is registered in this object, our 'calculateRange' can use it with our binary indices.
|
|
643
693
|
// if the op is registered to a function, we will run that function/op as a 2nd pass filter on results.
|
|
644
694
|
// those 2nd pass filter functions should be similar to LokiOps functions, accepting 2 vals to compare.
|
|
@@ -663,39 +713,39 @@
|
|
|
663
713
|
cloned;
|
|
664
714
|
|
|
665
715
|
switch (cloneMethod) {
|
|
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
|
-
|
|
716
|
+
case "parse-stringify":
|
|
717
|
+
cloned = JSON.parse(JSON.stringify(data));
|
|
718
|
+
break;
|
|
719
|
+
case "jquery-extend-deep":
|
|
720
|
+
cloned = jQuery.extend(true, {}, data);
|
|
721
|
+
break;
|
|
722
|
+
case "shallow":
|
|
723
|
+
// more compatible method for older browsers
|
|
724
|
+
cloned = Object.create(data.constructor.prototype);
|
|
725
|
+
Object.keys(data).map(function (i) {
|
|
726
|
+
cloned[i] = data[i];
|
|
727
|
+
});
|
|
728
|
+
break;
|
|
729
|
+
case "shallow-assign":
|
|
730
|
+
// should be supported by newer environments/browsers
|
|
731
|
+
cloned = Object.create(data.constructor.prototype);
|
|
732
|
+
Object.assign(cloned, data);
|
|
733
|
+
break;
|
|
734
|
+
case "shallow-recurse-objects":
|
|
735
|
+
// shallow clone top level properties
|
|
736
|
+
cloned = clone(data, "shallow");
|
|
737
|
+
var keys = Object.keys(data);
|
|
738
|
+
// for each of the top level properties which are object literals, recursively shallow copy
|
|
739
|
+
keys.forEach(function (key) {
|
|
740
|
+
if (typeof data[key] === "object" && data[key].constructor.name === "Object") {
|
|
741
|
+
cloned[key] = clone(data[key], "shallow-recurse-objects");
|
|
742
|
+
} else if (Array.isArray(data[key])) {
|
|
743
|
+
cloned[key] = cloneObjectArray(data[key], "shallow-recurse-objects");
|
|
744
|
+
}
|
|
745
|
+
});
|
|
746
|
+
break;
|
|
747
|
+
default:
|
|
748
|
+
break;
|
|
699
749
|
}
|
|
700
750
|
|
|
701
751
|
return cloned;
|
|
@@ -728,7 +778,7 @@
|
|
|
728
778
|
*
|
|
729
779
|
* @constructor LokiEventEmitter
|
|
730
780
|
*/
|
|
731
|
-
function LokiEventEmitter() {}
|
|
781
|
+
function LokiEventEmitter() { }
|
|
732
782
|
|
|
733
783
|
/**
|
|
734
784
|
* @prop {hashmap} events - a hashmap, with each property being an array of callbacks
|
|
@@ -756,7 +806,7 @@
|
|
|
756
806
|
var self = this;
|
|
757
807
|
|
|
758
808
|
if (Array.isArray(eventName)) {
|
|
759
|
-
eventName.forEach(function(currentEventName) {
|
|
809
|
+
eventName.forEach(function (currentEventName) {
|
|
760
810
|
self.on(currentEventName, listener);
|
|
761
811
|
});
|
|
762
812
|
return listener;
|
|
@@ -824,7 +874,7 @@
|
|
|
824
874
|
var self = this;
|
|
825
875
|
|
|
826
876
|
if (Array.isArray(eventName)) {
|
|
827
|
-
eventName.forEach(function(currentEventName) {
|
|
877
|
+
eventName.forEach(function (currentEventName) {
|
|
828
878
|
self.removeListener(currentEventName, listener);
|
|
829
879
|
});
|
|
830
880
|
|
|
@@ -903,8 +953,8 @@
|
|
|
903
953
|
|
|
904
954
|
var getENV = function () {
|
|
905
955
|
if (typeof global !== 'undefined' && (global.android || global.NSObject)) {
|
|
906
|
-
|
|
907
|
-
|
|
956
|
+
// If no adapter assume nativescript which needs adapter to be passed manually
|
|
957
|
+
return 'NATIVESCRIPT'; //nativescript
|
|
908
958
|
}
|
|
909
959
|
|
|
910
960
|
if (typeof window === 'undefined') {
|
|
@@ -980,11 +1030,11 @@
|
|
|
980
1030
|
*/
|
|
981
1031
|
Loki.prototype.configureOptions = function (options, initialConfig) {
|
|
982
1032
|
var defaultPersistence = {
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
1033
|
+
'NODEJS': 'fs',
|
|
1034
|
+
'BROWSER': 'localStorage',
|
|
1035
|
+
'CORDOVA': 'localStorage',
|
|
1036
|
+
'MEMORY': 'memory'
|
|
1037
|
+
},
|
|
988
1038
|
persistenceMethods = {
|
|
989
1039
|
'fs': LokiFsAdapter,
|
|
990
1040
|
'localStorage': LokiLocalStorageAdapter,
|
|
@@ -1079,7 +1129,7 @@
|
|
|
1079
1129
|
* @param {bool} options.removeNonSerializable - nulls properties not safe for serialization.
|
|
1080
1130
|
* @memberof Loki
|
|
1081
1131
|
*/
|
|
1082
|
-
Loki.prototype.copy = function(options) {
|
|
1132
|
+
Loki.prototype.copy = function (options) {
|
|
1083
1133
|
// in case running in an environment without accurate environment detection, pass 'NA'
|
|
1084
1134
|
var databaseCopy = new Loki(this.filename, { env: "NA" });
|
|
1085
1135
|
var clen, idx;
|
|
@@ -1090,12 +1140,12 @@
|
|
|
1090
1140
|
databaseCopy.loadJSONObject(this, { retainDirtyFlags: true });
|
|
1091
1141
|
|
|
1092
1142
|
// since our JSON serializeReplacer is not invoked for reference database adapters, this will let us mimic
|
|
1093
|
-
if(options.hasOwnProperty("removeNonSerializable") && options.removeNonSerializable === true) {
|
|
1143
|
+
if (options.hasOwnProperty("removeNonSerializable") && options.removeNonSerializable === true) {
|
|
1094
1144
|
databaseCopy.autosaveHandle = null;
|
|
1095
1145
|
databaseCopy.persistenceAdapter = null;
|
|
1096
1146
|
|
|
1097
1147
|
clen = databaseCopy.collections.length;
|
|
1098
|
-
for (idx=0; idx<clen; idx++) {
|
|
1148
|
+
for (idx = 0; idx < clen; idx++) {
|
|
1099
1149
|
databaseCopy.collections[idx].constraints = null;
|
|
1100
1150
|
databaseCopy.collections[idx].ttl = null;
|
|
1101
1151
|
}
|
|
@@ -1254,18 +1304,18 @@
|
|
|
1254
1304
|
*/
|
|
1255
1305
|
Loki.prototype.serializeReplacer = function (key, value) {
|
|
1256
1306
|
switch (key) {
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1307
|
+
case 'autosaveHandle':
|
|
1308
|
+
case 'persistenceAdapter':
|
|
1309
|
+
case 'constraints':
|
|
1310
|
+
case 'ttl':
|
|
1311
|
+
return null;
|
|
1312
|
+
case 'throttledSavePending':
|
|
1313
|
+
case 'throttledCallbacks':
|
|
1314
|
+
return undefined;
|
|
1315
|
+
case 'lokiConsoleWrapper':
|
|
1316
|
+
return null;
|
|
1317
|
+
default:
|
|
1318
|
+
return value;
|
|
1269
1319
|
}
|
|
1270
1320
|
};
|
|
1271
1321
|
|
|
@@ -1282,7 +1332,7 @@
|
|
|
1282
1332
|
options.serializationMethod = this.options.serializationMethod;
|
|
1283
1333
|
}
|
|
1284
1334
|
|
|
1285
|
-
switch(options.serializationMethod) {
|
|
1335
|
+
switch (options.serializationMethod) {
|
|
1286
1336
|
case "normal": return JSON.stringify(this, this.serializeReplacer);
|
|
1287
1337
|
case "pretty": return JSON.stringify(this, this.serializeReplacer, 2);
|
|
1288
1338
|
case "destructured": return this.serializeDestructured(); // use default options
|
|
@@ -1308,7 +1358,7 @@
|
|
|
1308
1358
|
* @returns {string|array} A custom, restructured aggregation of independent serializations.
|
|
1309
1359
|
* @memberof Loki
|
|
1310
1360
|
*/
|
|
1311
|
-
Loki.prototype.serializeDestructured = function(options) {
|
|
1361
|
+
Loki.prototype.serializeDestructured = function (options) {
|
|
1312
1362
|
var idx, sidx, result, resultlen;
|
|
1313
1363
|
var reconstruct = [];
|
|
1314
1364
|
var dbcopy;
|
|
@@ -1340,7 +1390,7 @@
|
|
|
1340
1390
|
dbcopy = new Loki(this.filename);
|
|
1341
1391
|
dbcopy.loadJSONObject(this);
|
|
1342
1392
|
|
|
1343
|
-
for(idx=0; idx < dbcopy.collections.length; idx++) {
|
|
1393
|
+
for (idx = 0; idx < dbcopy.collections.length; idx++) {
|
|
1344
1394
|
dbcopy.collections[idx].data = [];
|
|
1345
1395
|
}
|
|
1346
1396
|
|
|
@@ -1355,13 +1405,13 @@
|
|
|
1355
1405
|
// at this point we must be deconstructing the entire database
|
|
1356
1406
|
// start by pushing db serialization into first array element
|
|
1357
1407
|
reconstruct.push(dbcopy.serialize({
|
|
1358
|
-
|
|
1408
|
+
serializationMethod: "normal"
|
|
1359
1409
|
}));
|
|
1360
1410
|
|
|
1361
1411
|
dbcopy = null;
|
|
1362
1412
|
|
|
1363
1413
|
// push collection data into subsequent elements
|
|
1364
|
-
for(idx=0; idx < this.collections.length; idx++) {
|
|
1414
|
+
for (idx = 0; idx < this.collections.length; idx++) {
|
|
1365
1415
|
result = this.serializeCollection({
|
|
1366
1416
|
delimited: options.delimited,
|
|
1367
1417
|
delimiter: options.delimiter,
|
|
@@ -1379,7 +1429,7 @@
|
|
|
1379
1429
|
// Hopefully this will allow g.c. to reduce memory pressure, if needed.
|
|
1380
1430
|
resultlen = result.length;
|
|
1381
1431
|
|
|
1382
|
-
for (sidx=0; sidx < resultlen; sidx++) {
|
|
1432
|
+
for (sidx = 0; sidx < resultlen; sidx++) {
|
|
1383
1433
|
reconstruct.push(result[sidx]);
|
|
1384
1434
|
result[sidx] = null;
|
|
1385
1435
|
}
|
|
@@ -1441,7 +1491,7 @@
|
|
|
1441
1491
|
* @returns {string|array} A custom, restructured aggregation of independent serializations for a single collection.
|
|
1442
1492
|
* @memberof Loki
|
|
1443
1493
|
*/
|
|
1444
|
-
Loki.prototype.serializeCollection = function(options) {
|
|
1494
|
+
Loki.prototype.serializeCollection = function (options) {
|
|
1445
1495
|
var doccount,
|
|
1446
1496
|
docidx,
|
|
1447
1497
|
resultlines = [];
|
|
@@ -1460,13 +1510,13 @@
|
|
|
1460
1510
|
|
|
1461
1511
|
resultlines = [];
|
|
1462
1512
|
|
|
1463
|
-
for(docidx=0; docidx<doccount; docidx++) {
|
|
1513
|
+
for (docidx = 0; docidx < doccount; docidx++) {
|
|
1464
1514
|
resultlines.push(JSON.stringify(this.collections[options.collectionIndex].data[docidx]));
|
|
1465
1515
|
}
|
|
1466
1516
|
|
|
1467
1517
|
// D and DA
|
|
1468
1518
|
if (options.delimited) {
|
|
1469
|
-
|
|
1519
|
+
// indicate no more documents in collection (via empty delimited string)
|
|
1470
1520
|
resultlines.push("");
|
|
1471
1521
|
|
|
1472
1522
|
return resultlines.join(options.delimiter);
|
|
@@ -1493,10 +1543,10 @@
|
|
|
1493
1543
|
* @returns {object|array} An object representation of the deserialized database, not yet applied to 'this' db or document array
|
|
1494
1544
|
* @memberof Loki
|
|
1495
1545
|
*/
|
|
1496
|
-
Loki.prototype.deserializeDestructured = function(destructuredSource, options) {
|
|
1497
|
-
var workarray=[];
|
|
1546
|
+
Loki.prototype.deserializeDestructured = function (destructuredSource, options) {
|
|
1547
|
+
var workarray = [];
|
|
1498
1548
|
var len, cdb;
|
|
1499
|
-
var idx, collIndex=0, collCount, lineIndex=1, done=false;
|
|
1549
|
+
var idx, collIndex = 0, collCount, lineIndex = 1, done = false;
|
|
1500
1550
|
var currLine, currObject;
|
|
1501
1551
|
|
|
1502
1552
|
options = options || {};
|
|
@@ -1528,15 +1578,15 @@
|
|
|
1528
1578
|
}
|
|
1529
1579
|
|
|
1530
1580
|
// single collection, return doc array
|
|
1531
|
-
return this.deserializeCollection(destructuredSource[options.partition+1], options);
|
|
1581
|
+
return this.deserializeCollection(destructuredSource[options.partition + 1], options);
|
|
1532
1582
|
}
|
|
1533
1583
|
|
|
1534
1584
|
// Otherwise we are restoring an entire partitioned db
|
|
1535
1585
|
cdb = JSON.parse(destructuredSource[0]);
|
|
1536
1586
|
collCount = cdb.collections.length;
|
|
1537
|
-
for(collIndex=0; collIndex<collCount; collIndex++) {
|
|
1587
|
+
for (collIndex = 0; collIndex < collCount; collIndex++) {
|
|
1538
1588
|
// attach each collection docarray to container collection data, add 1 to collection array index since db is at 0
|
|
1539
|
-
cdb.collections[collIndex].data = this.deserializeCollection(destructuredSource[collIndex+1], options);
|
|
1589
|
+
cdb.collections[collIndex].data = this.deserializeCollection(destructuredSource[collIndex + 1], options);
|
|
1540
1590
|
}
|
|
1541
1591
|
|
|
1542
1592
|
return cdb;
|
|
@@ -1599,8 +1649,8 @@
|
|
|
1599
1649
|
* @returns {array} an array of documents to attach to collection.data.
|
|
1600
1650
|
* @memberof Loki
|
|
1601
1651
|
*/
|
|
1602
|
-
Loki.prototype.deserializeCollection = function(destructuredSource, options) {
|
|
1603
|
-
var workarray=[];
|
|
1652
|
+
Loki.prototype.deserializeCollection = function (destructuredSource, options) {
|
|
1653
|
+
var workarray = [];
|
|
1604
1654
|
var idx, len;
|
|
1605
1655
|
|
|
1606
1656
|
options = options || {};
|
|
@@ -1626,7 +1676,7 @@
|
|
|
1626
1676
|
}
|
|
1627
1677
|
|
|
1628
1678
|
len = workarray.length;
|
|
1629
|
-
for (idx=0; idx < len; idx++) {
|
|
1679
|
+
for (idx = 0; idx < len; idx++) {
|
|
1630
1680
|
workarray[idx] = JSON.parse(workarray[idx]);
|
|
1631
1681
|
}
|
|
1632
1682
|
|
|
@@ -1646,12 +1696,13 @@
|
|
|
1646
1696
|
if (serializedDb.length === 0) {
|
|
1647
1697
|
dbObject = {};
|
|
1648
1698
|
} else {
|
|
1699
|
+
|
|
1649
1700
|
// using option defined in instantiated db not what was in serialized db
|
|
1650
1701
|
switch (this.options.serializationMethod) {
|
|
1651
1702
|
case "normal":
|
|
1652
1703
|
case "pretty": dbObject = JSON.parse(serializedDb); break;
|
|
1653
1704
|
case "destructured": dbObject = this.deserializeDestructured(serializedDb); break;
|
|
1654
|
-
default:
|
|
1705
|
+
default: dbObject = JSON.parse(serializedDb); break;
|
|
1655
1706
|
}
|
|
1656
1707
|
}
|
|
1657
1708
|
|
|
@@ -1689,11 +1740,11 @@
|
|
|
1689
1740
|
var collOptions = options[coll.name];
|
|
1690
1741
|
var inflater;
|
|
1691
1742
|
|
|
1692
|
-
if(collOptions.proto) {
|
|
1743
|
+
if (collOptions.proto) {
|
|
1693
1744
|
inflater = collOptions.inflate || Utils.copyProperties;
|
|
1694
1745
|
|
|
1695
|
-
return function(data) {
|
|
1696
|
-
var collObj = new(collOptions.proto)();
|
|
1746
|
+
return function (data) {
|
|
1747
|
+
var collObj = new (collOptions.proto)();
|
|
1697
1748
|
inflater(data, collObj);
|
|
1698
1749
|
return collObj;
|
|
1699
1750
|
};
|
|
@@ -1708,10 +1759,11 @@
|
|
|
1708
1759
|
copyColl = this.addCollection(coll.name, {
|
|
1709
1760
|
disableChangesApi: coll.disableChangesApi,
|
|
1710
1761
|
disableDeltaChangesApi: coll.disableDeltaChangesApi,
|
|
1711
|
-
disableMeta: coll.disableMeta
|
|
1762
|
+
disableMeta: coll.disableMeta,
|
|
1763
|
+
disableFreeze: coll.hasOwnProperty('disableFreeze') ? coll.disableFreeze : true
|
|
1712
1764
|
});
|
|
1713
1765
|
|
|
1714
|
-
copyColl.adaptiveBinaryIndices = coll.hasOwnProperty('adaptiveBinaryIndices')?(coll.adaptiveBinaryIndices === true): false;
|
|
1766
|
+
copyColl.adaptiveBinaryIndices = coll.hasOwnProperty('adaptiveBinaryIndices') ? (coll.adaptiveBinaryIndices === true) : false;
|
|
1715
1767
|
copyColl.transactional = coll.transactional;
|
|
1716
1768
|
copyColl.asyncListeners = coll.asyncListeners;
|
|
1717
1769
|
copyColl.cloneObjects = coll.cloneObjects;
|
|
@@ -1727,27 +1779,48 @@
|
|
|
1727
1779
|
copyColl.dirty = false;
|
|
1728
1780
|
}
|
|
1729
1781
|
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
if (options && options.hasOwnProperty(coll.name)) {
|
|
1734
|
-
loader = makeLoader(coll);
|
|
1735
|
-
|
|
1736
|
-
for (j; j < clen; j++) {
|
|
1737
|
-
collObj = loader(coll.data[j]);
|
|
1738
|
-
copyColl.data[j] = collObj;
|
|
1739
|
-
copyColl.addAutoUpdateObserver(collObj);
|
|
1782
|
+
if (coll.getData) {
|
|
1783
|
+
if ((options && options.hasOwnProperty(coll.name)) || !copyColl.disableFreeze || copyColl.autoupdate) {
|
|
1784
|
+
throw new Error("this collection cannot be loaded lazily: " + coll.name);
|
|
1740
1785
|
}
|
|
1786
|
+
copyColl.getData = coll.getData;
|
|
1787
|
+
Object.defineProperty(copyColl, 'data', {
|
|
1788
|
+
/* jshint loopfunc:true */
|
|
1789
|
+
get: function() {
|
|
1790
|
+
var data = this.getData();
|
|
1791
|
+
this.getData = null;
|
|
1792
|
+
Object.defineProperty(this, 'data', { value: data });
|
|
1793
|
+
return data;
|
|
1794
|
+
}
|
|
1795
|
+
/* jshint loopfunc:false */
|
|
1796
|
+
});
|
|
1741
1797
|
} else {
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1798
|
+
// load each element individually
|
|
1799
|
+
clen = coll.data.length;
|
|
1800
|
+
j = 0;
|
|
1801
|
+
if (options && options.hasOwnProperty(coll.name)) {
|
|
1802
|
+
loader = makeLoader(coll);
|
|
1803
|
+
|
|
1804
|
+
for (j; j < clen; j++) {
|
|
1805
|
+
collObj = loader(coll.data[j]);
|
|
1806
|
+
copyColl.data[j] = collObj;
|
|
1807
|
+
copyColl.addAutoUpdateObserver(collObj);
|
|
1808
|
+
if (!copyColl.disableFreeze) {
|
|
1809
|
+
deepFreeze(copyColl.data[j]);
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
} else {
|
|
1813
|
+
for (j; j < clen; j++) {
|
|
1814
|
+
copyColl.data[j] = coll.data[j];
|
|
1815
|
+
copyColl.addAutoUpdateObserver(copyColl.data[j]);
|
|
1816
|
+
if (!copyColl.disableFreeze) {
|
|
1817
|
+
deepFreeze(copyColl.data[j]);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1746
1820
|
}
|
|
1747
1821
|
}
|
|
1748
1822
|
|
|
1749
1823
|
copyColl.maxId = (typeof coll.maxId === 'undefined') ? 0 : coll.maxId;
|
|
1750
|
-
copyColl.idIndex = coll.idIndex;
|
|
1751
1824
|
if (typeof (coll.binaryIndices) !== 'undefined') {
|
|
1752
1825
|
copyColl.binaryIndices = coll.binaryIndices;
|
|
1753
1826
|
}
|
|
@@ -1755,15 +1828,10 @@
|
|
|
1755
1828
|
copyColl.transforms = coll.transforms;
|
|
1756
1829
|
}
|
|
1757
1830
|
|
|
1758
|
-
copyColl.ensureId();
|
|
1759
|
-
|
|
1760
1831
|
// regenerate unique indexes
|
|
1761
1832
|
copyColl.uniqueNames = [];
|
|
1762
1833
|
if (coll.hasOwnProperty("uniqueNames")) {
|
|
1763
1834
|
copyColl.uniqueNames = coll.uniqueNames;
|
|
1764
|
-
for (j = 0; j < copyColl.uniqueNames.length; j++) {
|
|
1765
|
-
copyColl.ensureUniqueIndex(copyColl.uniqueNames[j]);
|
|
1766
|
-
}
|
|
1767
1835
|
}
|
|
1768
1836
|
|
|
1769
1837
|
// in case they are loading a database created before we added dynamic views, handle undefined
|
|
@@ -1777,11 +1845,18 @@
|
|
|
1777
1845
|
dv.resultdata = colldv.resultdata;
|
|
1778
1846
|
dv.resultsdirty = colldv.resultsdirty;
|
|
1779
1847
|
dv.filterPipeline = colldv.filterPipeline;
|
|
1780
|
-
|
|
1848
|
+
dv.sortCriteriaSimple = colldv.sortCriteriaSimple;
|
|
1781
1849
|
dv.sortCriteria = colldv.sortCriteria;
|
|
1782
1850
|
dv.sortFunction = null;
|
|
1783
|
-
|
|
1784
1851
|
dv.sortDirty = colldv.sortDirty;
|
|
1852
|
+
if (!copyColl.disableFreeze) {
|
|
1853
|
+
deepFreeze(dv.filterPipeline);
|
|
1854
|
+
if (dv.sortCriteriaSimple) {
|
|
1855
|
+
deepFreeze(dv.sortCriteriaSimple);
|
|
1856
|
+
} else if (dv.sortCriteria) {
|
|
1857
|
+
deepFreeze(dv.sortCriteria);
|
|
1858
|
+
}
|
|
1859
|
+
}
|
|
1785
1860
|
dv.resultset.filteredrows = colldv.resultset.filteredrows;
|
|
1786
1861
|
dv.resultset.filterInitialized = colldv.resultset.filterInitialized;
|
|
1787
1862
|
|
|
@@ -1792,9 +1867,9 @@
|
|
|
1792
1867
|
|
|
1793
1868
|
// Upgrade Logic for binary index refactoring at version 1.5
|
|
1794
1869
|
if (dbObject.databaseVersion < 1.5) {
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1870
|
+
// rebuild all indices
|
|
1871
|
+
copyColl.ensureAllIndexes(true);
|
|
1872
|
+
copyColl.dirty = true;
|
|
1798
1873
|
}
|
|
1799
1874
|
}
|
|
1800
1875
|
};
|
|
@@ -1919,16 +1994,16 @@
|
|
|
1919
1994
|
* @memberof LokiMemoryAdapter
|
|
1920
1995
|
*/
|
|
1921
1996
|
LokiMemoryAdapter.prototype.loadDatabase = function (dbname, callback) {
|
|
1922
|
-
var self=this;
|
|
1997
|
+
var self = this;
|
|
1923
1998
|
|
|
1924
1999
|
if (this.options.asyncResponses) {
|
|
1925
|
-
setTimeout(function() {
|
|
2000
|
+
setTimeout(function () {
|
|
1926
2001
|
if (self.hashStore.hasOwnProperty(dbname)) {
|
|
1927
2002
|
callback(self.hashStore[dbname].value);
|
|
1928
2003
|
}
|
|
1929
2004
|
else {
|
|
1930
2005
|
// database doesn't exist, return falsy
|
|
1931
|
-
callback
|
|
2006
|
+
callback(null);
|
|
1932
2007
|
}
|
|
1933
2008
|
}, this.options.asyncTimeout);
|
|
1934
2009
|
}
|
|
@@ -1938,7 +2013,7 @@
|
|
|
1938
2013
|
callback(this.hashStore[dbname].value);
|
|
1939
2014
|
}
|
|
1940
2015
|
else {
|
|
1941
|
-
callback
|
|
2016
|
+
callback(null);
|
|
1942
2017
|
}
|
|
1943
2018
|
}
|
|
1944
2019
|
};
|
|
@@ -1952,15 +2027,15 @@
|
|
|
1952
2027
|
* @memberof LokiMemoryAdapter
|
|
1953
2028
|
*/
|
|
1954
2029
|
LokiMemoryAdapter.prototype.saveDatabase = function (dbname, dbstring, callback) {
|
|
1955
|
-
var self=this;
|
|
2030
|
+
var self = this;
|
|
1956
2031
|
var saveCount;
|
|
1957
2032
|
|
|
1958
2033
|
if (this.options.asyncResponses) {
|
|
1959
|
-
setTimeout(function() {
|
|
1960
|
-
saveCount = (self.hashStore.hasOwnProperty(dbname)?self.hashStore[dbname].savecount:0);
|
|
2034
|
+
setTimeout(function () {
|
|
2035
|
+
saveCount = (self.hashStore.hasOwnProperty(dbname) ? self.hashStore[dbname].savecount : 0);
|
|
1961
2036
|
|
|
1962
2037
|
self.hashStore[dbname] = {
|
|
1963
|
-
savecount: saveCount+1,
|
|
2038
|
+
savecount: saveCount + 1,
|
|
1964
2039
|
lastsave: new Date(),
|
|
1965
2040
|
value: dbstring
|
|
1966
2041
|
};
|
|
@@ -1969,10 +2044,10 @@
|
|
|
1969
2044
|
}, this.options.asyncTimeout);
|
|
1970
2045
|
}
|
|
1971
2046
|
else {
|
|
1972
|
-
saveCount = (this.hashStore.hasOwnProperty(dbname)?this.hashStore[dbname].savecount:0);
|
|
2047
|
+
saveCount = (this.hashStore.hasOwnProperty(dbname) ? this.hashStore[dbname].savecount : 0);
|
|
1973
2048
|
|
|
1974
2049
|
this.hashStore[dbname] = {
|
|
1975
|
-
savecount: saveCount+1,
|
|
2050
|
+
savecount: saveCount + 1,
|
|
1976
2051
|
lastsave: new Date(),
|
|
1977
2052
|
value: dbstring
|
|
1978
2053
|
};
|
|
@@ -1988,7 +2063,7 @@
|
|
|
1988
2063
|
* @param {function} callback - function to call when done
|
|
1989
2064
|
* @memberof LokiMemoryAdapter
|
|
1990
2065
|
*/
|
|
1991
|
-
LokiMemoryAdapter.prototype.deleteDatabase = function(dbname, callback) {
|
|
2066
|
+
LokiMemoryAdapter.prototype.deleteDatabase = function (dbname, callback) {
|
|
1992
2067
|
if (this.hashStore.hasOwnProperty(dbname)) {
|
|
1993
2068
|
delete this.hashStore[dbname];
|
|
1994
2069
|
}
|
|
@@ -2043,7 +2118,7 @@
|
|
|
2043
2118
|
|
|
2044
2119
|
// default to page size of 25 megs (can be up to your largest serialized object size larger than this)
|
|
2045
2120
|
if (!this.options.hasOwnProperty("pageSize")) {
|
|
2046
|
-
this.options.pageSize = 25*1024*1024;
|
|
2121
|
+
this.options.pageSize = 25 * 1024 * 1024;
|
|
2047
2122
|
}
|
|
2048
2123
|
|
|
2049
2124
|
if (!this.options.hasOwnProperty("delimiter")) {
|
|
@@ -2060,12 +2135,12 @@
|
|
|
2060
2135
|
* @memberof LokiPartitioningAdapter
|
|
2061
2136
|
*/
|
|
2062
2137
|
LokiPartitioningAdapter.prototype.loadDatabase = function (dbname, callback) {
|
|
2063
|
-
var self=this;
|
|
2138
|
+
var self = this;
|
|
2064
2139
|
this.dbname = dbname;
|
|
2065
2140
|
this.dbref = new Loki(dbname);
|
|
2066
2141
|
|
|
2067
2142
|
// load the db container (without data)
|
|
2068
|
-
this.adapter.loadDatabase(dbname, function(result) {
|
|
2143
|
+
this.adapter.loadDatabase(dbname, function (result) {
|
|
2069
2144
|
// empty database condition is for inner adapter return null/undefined/falsy
|
|
2070
2145
|
if (!result) {
|
|
2071
2146
|
// partition 0 not found so new database, no need to try to load other partitions.
|
|
@@ -2095,7 +2170,7 @@
|
|
|
2095
2170
|
pageIndex: 0
|
|
2096
2171
|
};
|
|
2097
2172
|
|
|
2098
|
-
self.loadNextPartition(0, function() {
|
|
2173
|
+
self.loadNextPartition(0, function () {
|
|
2099
2174
|
callback(self.dbref);
|
|
2100
2175
|
});
|
|
2101
2176
|
});
|
|
@@ -2107,9 +2182,9 @@
|
|
|
2107
2182
|
* @param {int} partition - ordinal collection position to load next
|
|
2108
2183
|
* @param {function} callback - adapter callback to return load result to caller
|
|
2109
2184
|
*/
|
|
2110
|
-
LokiPartitioningAdapter.prototype.loadNextPartition = function(partition, callback) {
|
|
2185
|
+
LokiPartitioningAdapter.prototype.loadNextPartition = function (partition, callback) {
|
|
2111
2186
|
var keyname = this.dbname + "." + partition;
|
|
2112
|
-
var self=this;
|
|
2187
|
+
var self = this;
|
|
2113
2188
|
|
|
2114
2189
|
if (this.options.paging === true) {
|
|
2115
2190
|
this.pageIterator.pageIndex = 0;
|
|
@@ -2117,7 +2192,7 @@
|
|
|
2117
2192
|
return;
|
|
2118
2193
|
}
|
|
2119
2194
|
|
|
2120
|
-
this.adapter.loadDatabase(keyname, function(result) {
|
|
2195
|
+
this.adapter.loadDatabase(keyname, function (result) {
|
|
2121
2196
|
var data = self.dbref.deserializeCollection(result, { delimited: true, collectionIndex: partition });
|
|
2122
2197
|
self.dbref.collections[partition].data = data;
|
|
2123
2198
|
|
|
@@ -2135,32 +2210,32 @@
|
|
|
2135
2210
|
*
|
|
2136
2211
|
* @param {function} callback - adapter callback to return load result to caller
|
|
2137
2212
|
*/
|
|
2138
|
-
LokiPartitioningAdapter.prototype.loadNextPage = function(callback) {
|
|
2213
|
+
LokiPartitioningAdapter.prototype.loadNextPage = function (callback) {
|
|
2139
2214
|
// calculate name for next saved page in sequence
|
|
2140
2215
|
var keyname = this.dbname + "." + this.pageIterator.collection + "." + this.pageIterator.pageIndex;
|
|
2141
|
-
var self=this;
|
|
2216
|
+
var self = this;
|
|
2142
2217
|
|
|
2143
2218
|
// load whatever page is next in sequence
|
|
2144
|
-
this.adapter.loadDatabase(keyname, function(result) {
|
|
2219
|
+
this.adapter.loadDatabase(keyname, function (result) {
|
|
2145
2220
|
var data = result.split(self.options.delimiter);
|
|
2146
2221
|
result = ""; // free up memory now that we have split it into array
|
|
2147
2222
|
var dlen = data.length;
|
|
2148
2223
|
var idx;
|
|
2149
2224
|
|
|
2150
2225
|
// detect if last page by presence of final empty string element and remove it if so
|
|
2151
|
-
var isLastPage = (data[dlen-1] === "");
|
|
2226
|
+
var isLastPage = (data[dlen - 1] === "");
|
|
2152
2227
|
if (isLastPage) {
|
|
2153
2228
|
data.pop();
|
|
2154
2229
|
dlen = data.length;
|
|
2155
2230
|
// empty collections are just a delimiter meaning two blank items
|
|
2156
|
-
if (data[dlen-1] === "" && dlen === 1) {
|
|
2231
|
+
if (data[dlen - 1] === "" && dlen === 1) {
|
|
2157
2232
|
data.pop();
|
|
2158
2233
|
dlen = data.length;
|
|
2159
2234
|
}
|
|
2160
2235
|
}
|
|
2161
2236
|
|
|
2162
2237
|
// convert stringified array elements to object instances and push to collection data
|
|
2163
|
-
for(idx=0; idx < dlen; idx++) {
|
|
2238
|
+
for (idx = 0; idx < dlen; idx++) {
|
|
2164
2239
|
self.dbref.collections[self.pageIterator.collection].data.push(JSON.parse(data[idx]));
|
|
2165
2240
|
data[idx] = null;
|
|
2166
2241
|
}
|
|
@@ -2194,8 +2269,8 @@
|
|
|
2194
2269
|
*
|
|
2195
2270
|
* @memberof LokiPartitioningAdapter
|
|
2196
2271
|
*/
|
|
2197
|
-
LokiPartitioningAdapter.prototype.exportDatabase = function(dbname, dbref, callback) {
|
|
2198
|
-
var self=this;
|
|
2272
|
+
LokiPartitioningAdapter.prototype.exportDatabase = function (dbname, dbref, callback) {
|
|
2273
|
+
var self = this;
|
|
2199
2274
|
var idx, clen = dbref.collections.length;
|
|
2200
2275
|
|
|
2201
2276
|
this.dbref = dbref;
|
|
@@ -2203,13 +2278,13 @@
|
|
|
2203
2278
|
|
|
2204
2279
|
// queue up dirty partitions to be saved
|
|
2205
2280
|
this.dirtyPartitions = [-1];
|
|
2206
|
-
for(idx=0; idx<clen; idx++) {
|
|
2281
|
+
for (idx = 0; idx < clen; idx++) {
|
|
2207
2282
|
if (dbref.collections[idx].dirty) {
|
|
2208
2283
|
this.dirtyPartitions.push(idx);
|
|
2209
2284
|
}
|
|
2210
2285
|
}
|
|
2211
2286
|
|
|
2212
|
-
this.saveNextPartition(function(err) {
|
|
2287
|
+
this.saveNextPartition(function (err) {
|
|
2213
2288
|
callback(err);
|
|
2214
2289
|
});
|
|
2215
2290
|
};
|
|
@@ -2219,10 +2294,10 @@
|
|
|
2219
2294
|
*
|
|
2220
2295
|
* @param {function} callback - adapter callback to return load result to caller
|
|
2221
2296
|
*/
|
|
2222
|
-
LokiPartitioningAdapter.prototype.saveNextPartition = function(callback) {
|
|
2223
|
-
var self=this;
|
|
2297
|
+
LokiPartitioningAdapter.prototype.saveNextPartition = function (callback) {
|
|
2298
|
+
var self = this;
|
|
2224
2299
|
var partition = this.dirtyPartitions.shift();
|
|
2225
|
-
var keyname = this.dbname + ((partition
|
|
2300
|
+
var keyname = this.dbname + ((partition === -1) ? "" : ("." + partition));
|
|
2226
2301
|
|
|
2227
2302
|
// if we are doing paging and this is collection partition
|
|
2228
2303
|
if (this.options.paging && partition !== -1) {
|
|
@@ -2233,7 +2308,7 @@
|
|
|
2233
2308
|
};
|
|
2234
2309
|
|
|
2235
2310
|
// since saveNextPage recursively calls itself until done, our callback means this whole paged partition is finished
|
|
2236
|
-
this.saveNextPage(function(err) {
|
|
2311
|
+
this.saveNextPage(function (err) {
|
|
2237
2312
|
if (self.dirtyPartitions.length === 0) {
|
|
2238
2313
|
callback(err);
|
|
2239
2314
|
}
|
|
@@ -2246,12 +2321,12 @@
|
|
|
2246
2321
|
|
|
2247
2322
|
// otherwise this is 'non-paged' partioning...
|
|
2248
2323
|
var result = this.dbref.serializeDestructured({
|
|
2249
|
-
partitioned
|
|
2324
|
+
partitioned: true,
|
|
2250
2325
|
delimited: true,
|
|
2251
2326
|
partition: partition
|
|
2252
2327
|
});
|
|
2253
2328
|
|
|
2254
|
-
this.adapter.saveDatabase(keyname, result, function(err) {
|
|
2329
|
+
this.adapter.saveDatabase(keyname, result, function (err) {
|
|
2255
2330
|
if (err) {
|
|
2256
2331
|
callback(err);
|
|
2257
2332
|
return;
|
|
@@ -2271,19 +2346,19 @@
|
|
|
2271
2346
|
*
|
|
2272
2347
|
* @param {function} callback - adapter callback to return load result to caller
|
|
2273
2348
|
*/
|
|
2274
|
-
LokiPartitioningAdapter.prototype.saveNextPage = function(callback) {
|
|
2275
|
-
var self=this;
|
|
2349
|
+
LokiPartitioningAdapter.prototype.saveNextPage = function (callback) {
|
|
2350
|
+
var self = this;
|
|
2276
2351
|
var coll = this.dbref.collections[this.pageIterator.collection];
|
|
2277
2352
|
var keyname = this.dbname + "." + this.pageIterator.collection + "." + this.pageIterator.pageIndex;
|
|
2278
|
-
var pageLen=0,
|
|
2353
|
+
var pageLen = 0,
|
|
2279
2354
|
cdlen = coll.data.length,
|
|
2280
2355
|
delimlen = this.options.delimiter.length;
|
|
2281
2356
|
var serializedObject = "",
|
|
2282
2357
|
pageBuilder = "";
|
|
2283
|
-
var doneWithPartition=false,
|
|
2284
|
-
doneWithPage=false;
|
|
2358
|
+
var doneWithPartition = false,
|
|
2359
|
+
doneWithPage = false;
|
|
2285
2360
|
|
|
2286
|
-
var pageSaveCallback = function(err) {
|
|
2361
|
+
var pageSaveCallback = function (err) {
|
|
2287
2362
|
pageBuilder = "";
|
|
2288
2363
|
|
|
2289
2364
|
if (err) {
|
|
@@ -2337,11 +2412,11 @@
|
|
|
2337
2412
|
* @constructor LokiFsAdapter
|
|
2338
2413
|
*/
|
|
2339
2414
|
function LokiFsAdapter() {
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2415
|
+
try {
|
|
2416
|
+
this.fs = require('fs');
|
|
2417
|
+
} catch (e) {
|
|
2343
2418
|
this.fs = null;
|
|
2344
|
-
|
|
2419
|
+
}
|
|
2345
2420
|
}
|
|
2346
2421
|
|
|
2347
2422
|
/**
|
|
@@ -2385,7 +2460,7 @@
|
|
|
2385
2460
|
if (err) {
|
|
2386
2461
|
callback(new Error(err));
|
|
2387
2462
|
} else {
|
|
2388
|
-
self.fs.rename(tmpdbname,dbname,callback);
|
|
2463
|
+
self.fs.rename(tmpdbname, dbname, callback);
|
|
2389
2464
|
}
|
|
2390
2465
|
});
|
|
2391
2466
|
};
|
|
@@ -2412,7 +2487,7 @@
|
|
|
2412
2487
|
* A loki persistence adapter which persists to web browser's local storage object
|
|
2413
2488
|
* @constructor LokiLocalStorageAdapter
|
|
2414
2489
|
*/
|
|
2415
|
-
function LokiLocalStorageAdapter() {}
|
|
2490
|
+
function LokiLocalStorageAdapter() { }
|
|
2416
2491
|
|
|
2417
2492
|
/**
|
|
2418
2493
|
* loadDatabase() - Load data from localstorage
|
|
@@ -2470,7 +2545,7 @@
|
|
|
2470
2545
|
* @param {int} options.recursiveWaitLimitDelay - (default: 2000) cutoff in ms to stop recursively re-draining
|
|
2471
2546
|
* @memberof Loki
|
|
2472
2547
|
*/
|
|
2473
|
-
Loki.prototype.throttledSaveDrain = function(callback, options) {
|
|
2548
|
+
Loki.prototype.throttledSaveDrain = function (callback, options) {
|
|
2474
2549
|
var self = this;
|
|
2475
2550
|
var now = (new Date()).getTime();
|
|
2476
2551
|
|
|
@@ -2497,7 +2572,7 @@
|
|
|
2497
2572
|
// if we want to wait until we are in a state where there are no pending saves at all
|
|
2498
2573
|
if (options.recursiveWait) {
|
|
2499
2574
|
// queue the following meta callback for when it completes
|
|
2500
|
-
this.throttledCallbacks.push(function() {
|
|
2575
|
+
this.throttledCallbacks.push(function () {
|
|
2501
2576
|
// if there is now another save pending...
|
|
2502
2577
|
if (self.throttledSavePending) {
|
|
2503
2578
|
// if we wish to wait only so long and we have exceeded limit of our waiting, callback with false success value
|
|
@@ -2536,10 +2611,10 @@
|
|
|
2536
2611
|
*/
|
|
2537
2612
|
Loki.prototype.loadDatabaseInternal = function (options, callback) {
|
|
2538
2613
|
var cFun = callback || function (err, data) {
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2614
|
+
if (err) {
|
|
2615
|
+
throw err;
|
|
2616
|
+
}
|
|
2617
|
+
},
|
|
2543
2618
|
self = this;
|
|
2544
2619
|
|
|
2545
2620
|
// the persistenceAdapter should be present if all is ok, but check to be sure.
|
|
@@ -2568,8 +2643,8 @@
|
|
|
2568
2643
|
|
|
2569
2644
|
// instanceof error means load faulted
|
|
2570
2645
|
if (dbString instanceof Error) {
|
|
2571
|
-
|
|
2572
|
-
|
|
2646
|
+
cFun(dbString);
|
|
2647
|
+
return;
|
|
2573
2648
|
}
|
|
2574
2649
|
|
|
2575
2650
|
// if adapter has returned an js object (other than null or error) attempt to load from JSON object
|
|
@@ -2614,7 +2689,7 @@
|
|
|
2614
2689
|
* });
|
|
2615
2690
|
*/
|
|
2616
2691
|
Loki.prototype.loadDatabase = function (options, callback) {
|
|
2617
|
-
var self=this;
|
|
2692
|
+
var self = this;
|
|
2618
2693
|
|
|
2619
2694
|
// if throttling disabled, just call internal
|
|
2620
2695
|
if (!this.throttledSaves) {
|
|
@@ -2623,12 +2698,12 @@
|
|
|
2623
2698
|
}
|
|
2624
2699
|
|
|
2625
2700
|
// try to drain any pending saves in the queue to lock it for loading
|
|
2626
|
-
this.throttledSaveDrain(function(success) {
|
|
2701
|
+
this.throttledSaveDrain(function (success) {
|
|
2627
2702
|
if (success) {
|
|
2628
2703
|
// pause/throttle saving until loading is done
|
|
2629
2704
|
self.throttledSavePending = true;
|
|
2630
2705
|
|
|
2631
|
-
self.loadDatabaseInternal(options, function(err) {
|
|
2706
|
+
self.loadDatabaseInternal(options, function (err) {
|
|
2632
2707
|
// now that we are finished loading, if no saves were throttled, disable flag
|
|
2633
2708
|
if (self.throttledCallbacks.length === 0) {
|
|
2634
2709
|
self.throttledSavePending = false;
|
|
@@ -2657,11 +2732,11 @@
|
|
|
2657
2732
|
*/
|
|
2658
2733
|
Loki.prototype.saveDatabaseInternal = function (callback) {
|
|
2659
2734
|
var cFun = callback || function (err) {
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
|
|
2664
|
-
|
|
2735
|
+
if (err) {
|
|
2736
|
+
throw err;
|
|
2737
|
+
}
|
|
2738
|
+
return;
|
|
2739
|
+
};
|
|
2665
2740
|
var self = this;
|
|
2666
2741
|
|
|
2667
2742
|
// the persistenceAdapter should be present if all is ok, but check to be sure.
|
|
@@ -2670,46 +2745,59 @@
|
|
|
2670
2745
|
return;
|
|
2671
2746
|
}
|
|
2672
2747
|
|
|
2673
|
-
// persistenceAdapter might be asynchronous, so we must clear `dirty` immediately
|
|
2674
|
-
// or autosave won't work if an update occurs between here and the callback
|
|
2675
|
-
// TODO: This should be stored and rolled back in case of DB save failure
|
|
2676
|
-
// TODO: Reference mode adapter should have the same behavior
|
|
2677
|
-
if (this.persistenceAdapter.mode !== "reference") {
|
|
2678
|
-
this.autosaveClearFlags();
|
|
2679
|
-
}
|
|
2680
|
-
|
|
2681
2748
|
// run incremental, reference, or normal mode adapters, depending on what's available
|
|
2682
2749
|
if (this.persistenceAdapter.mode === "incremental") {
|
|
2683
|
-
var
|
|
2684
|
-
|
|
2685
|
-
//
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2750
|
+
var cachedDirty;
|
|
2751
|
+
// ignore autosave until we copy loki (only then we can clear dirty flags,
|
|
2752
|
+
// but if we don't do it now, autosave will be triggered a lot unnecessarily)
|
|
2753
|
+
this.ignoreAutosave = true;
|
|
2754
|
+
this.persistenceAdapter.saveDatabase(
|
|
2755
|
+
this.filename,
|
|
2756
|
+
function getLokiCopy() {
|
|
2757
|
+
self.ignoreAutosave = false;
|
|
2758
|
+
if (cachedDirty) {
|
|
2759
|
+
cFun(new Error('adapter error - getLokiCopy called more than once'));
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
var lokiCopy = self.copy({ removeNonSerializable: true });
|
|
2693
2763
|
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
col.dirtyIds = col.dirtyIds.concat(cachedDirtyIds[i]);
|
|
2764
|
+
// remember and clear dirty ids -- we must do it before the save so that if
|
|
2765
|
+
// and update occurs between here and callback, it will get saved later
|
|
2766
|
+
cachedDirty = self.collections.map(function (collection) {
|
|
2767
|
+
return [collection.dirty, collection.dirtyIds];
|
|
2699
2768
|
});
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2769
|
+
self.collections.forEach(function (col) {
|
|
2770
|
+
col.dirty = false;
|
|
2771
|
+
col.dirtyIds = [];
|
|
2772
|
+
});
|
|
2773
|
+
return lokiCopy;
|
|
2774
|
+
},
|
|
2775
|
+
function exportDatabaseCallback(err) {
|
|
2776
|
+
self.ignoreAutosave = false;
|
|
2777
|
+
if (err && cachedDirty) {
|
|
2778
|
+
// roll back dirty IDs to be saved later
|
|
2779
|
+
self.collections.forEach(function (col, i) {
|
|
2780
|
+
var cached = cachedDirty[i];
|
|
2781
|
+
col.dirty = col.dirty || cached[0];
|
|
2782
|
+
col.dirtyIds = col.dirtyIds.concat(cached[1]);
|
|
2783
|
+
});
|
|
2784
|
+
}
|
|
2785
|
+
cFun(err);
|
|
2786
|
+
});
|
|
2704
2787
|
} else if (this.persistenceAdapter.mode === "reference" && typeof this.persistenceAdapter.exportDatabase === "function") {
|
|
2788
|
+
// TODO: dirty should be cleared here
|
|
2705
2789
|
// filename may seem redundant but loadDatabase will need to expect this same filename
|
|
2706
|
-
this.persistenceAdapter.exportDatabase(this.filename, this.copy({removeNonSerializable:true}), function exportDatabaseCallback(err) {
|
|
2790
|
+
this.persistenceAdapter.exportDatabase(this.filename, this.copy({ removeNonSerializable: true }), function exportDatabaseCallback(err) {
|
|
2707
2791
|
self.autosaveClearFlags();
|
|
2708
2792
|
cFun(err);
|
|
2709
2793
|
});
|
|
2710
2794
|
}
|
|
2711
2795
|
// otherwise just pass the serialized database to adapter
|
|
2712
2796
|
else {
|
|
2797
|
+
// persistenceAdapter might be asynchronous, so we must clear `dirty` immediately
|
|
2798
|
+
// or autosave won't work if an update occurs between here and the callback
|
|
2799
|
+
// TODO: This should be stored and rolled back in case of DB save failure
|
|
2800
|
+
this.autosaveClearFlags();
|
|
2713
2801
|
this.persistenceAdapter.saveDatabase(this.filename, this.serialize(), function saveDatabasecallback(err) {
|
|
2714
2802
|
cFun(err);
|
|
2715
2803
|
});
|
|
@@ -2752,12 +2840,12 @@
|
|
|
2752
2840
|
this.throttledSavePending = true;
|
|
2753
2841
|
|
|
2754
2842
|
var self = this;
|
|
2755
|
-
this.saveDatabaseInternal(function(err) {
|
|
2843
|
+
this.saveDatabaseInternal(function (err) {
|
|
2756
2844
|
self.throttledSavePending = false;
|
|
2757
|
-
localCallbacks.forEach(function(pcb) {
|
|
2845
|
+
localCallbacks.forEach(function (pcb) {
|
|
2758
2846
|
if (typeof pcb === 'function') {
|
|
2759
2847
|
// Queue the callbacks so we first finish this method execution
|
|
2760
|
-
setTimeout(function() {
|
|
2848
|
+
setTimeout(function () {
|
|
2761
2849
|
pcb(err);
|
|
2762
2850
|
}, 1);
|
|
2763
2851
|
}
|
|
@@ -2852,7 +2940,7 @@
|
|
|
2852
2940
|
// so next step will be to implement collection level dirty flags set on insert/update/remove
|
|
2853
2941
|
// along with loki level isdirty() function which iterates all collections to see if any are dirty
|
|
2854
2942
|
|
|
2855
|
-
if (self.autosaveDirty()) {
|
|
2943
|
+
if (self.autosaveDirty() && !self.ignoreAutosave) {
|
|
2856
2944
|
self.saveDatabase(callback);
|
|
2857
2945
|
}
|
|
2858
2946
|
}, delay);
|
|
@@ -3031,46 +3119,46 @@
|
|
|
3031
3119
|
step = transform[idx];
|
|
3032
3120
|
|
|
3033
3121
|
switch (step.type) {
|
|
3034
|
-
|
|
3035
|
-
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
|
|
3052
|
-
|
|
3053
|
-
|
|
3054
|
-
|
|
3055
|
-
|
|
3056
|
-
|
|
3057
|
-
|
|
3058
|
-
|
|
3059
|
-
|
|
3060
|
-
|
|
3122
|
+
case "find":
|
|
3123
|
+
rs.find(step.value);
|
|
3124
|
+
break;
|
|
3125
|
+
case "where":
|
|
3126
|
+
rs.where(step.value);
|
|
3127
|
+
break;
|
|
3128
|
+
case "simplesort":
|
|
3129
|
+
rs.simplesort(step.property, step.desc || step.options);
|
|
3130
|
+
break;
|
|
3131
|
+
case "compoundsort":
|
|
3132
|
+
rs.compoundsort(step.value);
|
|
3133
|
+
break;
|
|
3134
|
+
case "sort":
|
|
3135
|
+
rs.sort(step.value);
|
|
3136
|
+
break;
|
|
3137
|
+
case "limit":
|
|
3138
|
+
rs = rs.limit(step.value);
|
|
3139
|
+
break; // limit makes copy so update reference
|
|
3140
|
+
case "offset":
|
|
3141
|
+
rs = rs.offset(step.value);
|
|
3142
|
+
break; // offset makes copy so update reference
|
|
3143
|
+
case "map":
|
|
3144
|
+
rs = rs.map(step.value, step.dataOptions);
|
|
3145
|
+
break;
|
|
3146
|
+
case "eqJoin":
|
|
3147
|
+
rs = rs.eqJoin(step.joinData, step.leftJoinKey, step.rightJoinKey, step.mapFun, step.dataOptions);
|
|
3148
|
+
break;
|
|
3061
3149
|
// following cases break chain by returning array data so make any of these last in transform steps
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3150
|
+
case "mapReduce":
|
|
3151
|
+
rs = rs.mapReduce(step.mapFunction, step.reduceFunction);
|
|
3152
|
+
break;
|
|
3065
3153
|
// following cases update documents in current filtered resultset (use carefully)
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3154
|
+
case "update":
|
|
3155
|
+
rs.update(step.value);
|
|
3156
|
+
break;
|
|
3157
|
+
case "remove":
|
|
3158
|
+
rs.remove();
|
|
3159
|
+
break;
|
|
3160
|
+
default:
|
|
3161
|
+
break;
|
|
3074
3162
|
}
|
|
3075
3163
|
}
|
|
3076
3164
|
|
|
@@ -3174,7 +3262,7 @@
|
|
|
3174
3262
|
if (!options.disableIndexIntersect && hasBinaryIndex) {
|
|
3175
3263
|
|
|
3176
3264
|
// calculate filter efficiency
|
|
3177
|
-
eff = dc/frl;
|
|
3265
|
+
eff = dc / frl;
|
|
3178
3266
|
|
|
3179
3267
|
// when javascript sort fallback is enabled, you generally need more than ~17% of total docs in resultset
|
|
3180
3268
|
// before array intersect is determined to be the faster algorithm, otherwise leave at 10% for loki sort.
|
|
@@ -3185,17 +3273,17 @@
|
|
|
3185
3273
|
// anything more than ratio of 10:1 (total documents/current results) should use old sort code path
|
|
3186
3274
|
// So we will only use array intersection if you have more than 10% of total docs in your current resultset.
|
|
3187
3275
|
if (eff <= targetEff || options.forceIndexIntersect) {
|
|
3188
|
-
var idx, fr=this.filteredrows;
|
|
3276
|
+
var idx, fr = this.filteredrows;
|
|
3189
3277
|
var io = {};
|
|
3190
3278
|
// set up hashobject for simple 'inclusion test' with existing (filtered) results
|
|
3191
|
-
for(idx=0; idx<frl; idx++) {
|
|
3279
|
+
for (idx = 0; idx < frl; idx++) {
|
|
3192
3280
|
io[fr[idx]] = true;
|
|
3193
3281
|
}
|
|
3194
3282
|
// grab full sorted binary index array
|
|
3195
3283
|
var pv = this.collection.binaryIndices[propname].values;
|
|
3196
3284
|
|
|
3197
3285
|
// filter by existing results
|
|
3198
|
-
this.filteredrows = pv.filter(function(n) { return io[n]; });
|
|
3286
|
+
this.filteredrows = pv.filter(function (n) { return io[n]; });
|
|
3199
3287
|
|
|
3200
3288
|
if (options.desc) {
|
|
3201
3289
|
this.filteredrows.reverse();
|
|
@@ -3210,7 +3298,7 @@
|
|
|
3210
3298
|
|
|
3211
3299
|
// if we have opted to use simplified javascript comparison function...
|
|
3212
3300
|
if (options.useJavascriptSorting) {
|
|
3213
|
-
return this.sort(function(obj1, obj2) {
|
|
3301
|
+
return this.sort(function (obj1, obj2) {
|
|
3214
3302
|
if (obj1[propname] === obj2[propname]) return 0;
|
|
3215
3303
|
if (obj1[propname] > obj2[propname]) return 1;
|
|
3216
3304
|
if (obj1[propname] < obj2[propname]) return -1;
|
|
@@ -3333,7 +3421,7 @@
|
|
|
3333
3421
|
Resultset.prototype.$or = Resultset.prototype.findOr;
|
|
3334
3422
|
|
|
3335
3423
|
// precompile recursively
|
|
3336
|
-
function precompileQuery
|
|
3424
|
+
function precompileQuery(operator, value) {
|
|
3337
3425
|
// for regex ops, precompile
|
|
3338
3426
|
if (operator === '$regex') {
|
|
3339
3427
|
if (Array.isArray(value)) {
|
|
@@ -3492,6 +3580,12 @@
|
|
|
3492
3580
|
index = this.collection.binaryIndices[property];
|
|
3493
3581
|
}
|
|
3494
3582
|
|
|
3583
|
+
// opportunistically speed up $in searches from O(n*m) to O(n*log m)
|
|
3584
|
+
if (!searchByIndex && operator === '$in' && Array.isArray(value) && typeof Set !== 'undefined') {
|
|
3585
|
+
value = new Set(value);
|
|
3586
|
+
operator = '$inSet';
|
|
3587
|
+
}
|
|
3588
|
+
|
|
3495
3589
|
// the comparison function
|
|
3496
3590
|
var fun = LokiOps[operator];
|
|
3497
3591
|
|
|
@@ -3507,7 +3601,7 @@
|
|
|
3507
3601
|
//
|
|
3508
3602
|
// For performance reasons, each case has its own if block to minimize in-loop calculations
|
|
3509
3603
|
|
|
3510
|
-
var filter, rowIdx = 0;
|
|
3604
|
+
var filter, rowIdx = 0, record;
|
|
3511
3605
|
|
|
3512
3606
|
// If the filteredrows[] is already initialized, use it
|
|
3513
3607
|
if (this.filterInitialized) {
|
|
@@ -3517,9 +3611,10 @@
|
|
|
3517
3611
|
// currently supporting dot notation for non-indexed conditions only
|
|
3518
3612
|
if (usingDotNotation) {
|
|
3519
3613
|
property = property.split('.');
|
|
3520
|
-
for(i=0; i<len; i++) {
|
|
3614
|
+
for (i = 0; i < len; i++) {
|
|
3521
3615
|
rowIdx = filter[i];
|
|
3522
|
-
|
|
3616
|
+
record = t[rowIdx];
|
|
3617
|
+
if (dotSubScan(record, property, fun, value, record)) {
|
|
3523
3618
|
result.push(rowIdx);
|
|
3524
3619
|
if (firstOnly) {
|
|
3525
3620
|
this.filteredrows = result;
|
|
@@ -3528,9 +3623,10 @@
|
|
|
3528
3623
|
}
|
|
3529
3624
|
}
|
|
3530
3625
|
} else {
|
|
3531
|
-
for(i=0; i<len; i++) {
|
|
3626
|
+
for (i = 0; i < len; i++) {
|
|
3532
3627
|
rowIdx = filter[i];
|
|
3533
|
-
|
|
3628
|
+
record = t[rowIdx];
|
|
3629
|
+
if (fun(record[property], value, record)) {
|
|
3534
3630
|
result.push(rowIdx);
|
|
3535
3631
|
if (firstOnly) {
|
|
3536
3632
|
this.filteredrows = result;
|
|
@@ -3548,8 +3644,9 @@
|
|
|
3548
3644
|
|
|
3549
3645
|
if (usingDotNotation) {
|
|
3550
3646
|
property = property.split('.');
|
|
3551
|
-
for(i=0; i<len; i++) {
|
|
3552
|
-
|
|
3647
|
+
for (i = 0; i < len; i++) {
|
|
3648
|
+
record = t[i];
|
|
3649
|
+
if (dotSubScan(record, property, fun, value, record)) {
|
|
3553
3650
|
result.push(i);
|
|
3554
3651
|
if (firstOnly) {
|
|
3555
3652
|
this.filteredrows = result;
|
|
@@ -3559,8 +3656,9 @@
|
|
|
3559
3656
|
}
|
|
3560
3657
|
}
|
|
3561
3658
|
} else {
|
|
3562
|
-
for(i=0; i<len; i++) {
|
|
3563
|
-
|
|
3659
|
+
for (i = 0; i < len; i++) {
|
|
3660
|
+
record = t[i];
|
|
3661
|
+
if (fun(record[property], value, record)) {
|
|
3564
3662
|
result.push(i);
|
|
3565
3663
|
if (firstOnly) {
|
|
3566
3664
|
this.filteredrows = result;
|
|
@@ -3588,12 +3686,12 @@
|
|
|
3588
3686
|
}
|
|
3589
3687
|
}
|
|
3590
3688
|
else {
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3689
|
+
result.push(index.values[i]);
|
|
3690
|
+
if (firstOnly) {
|
|
3691
|
+
this.filteredrows = result;
|
|
3692
|
+
this.filterInitialized = true;
|
|
3693
|
+
return this;
|
|
3694
|
+
}
|
|
3597
3695
|
}
|
|
3598
3696
|
}
|
|
3599
3697
|
} else {
|
|
@@ -3716,7 +3814,8 @@
|
|
|
3716
3814
|
}
|
|
3717
3815
|
|
|
3718
3816
|
// if collection has delta changes active, then force clones and use 'parse-stringify' for effective change tracking of nested objects
|
|
3719
|
-
if
|
|
3817
|
+
// if collection is immutable freeze and unFreeze takes care of cloning
|
|
3818
|
+
if (!this.collection.disableDeltaChangesApi && this.collection.disableFreeze) {
|
|
3720
3819
|
options.forceClones = true;
|
|
3721
3820
|
options.forceCloneMethod = 'parse-stringify';
|
|
3722
3821
|
}
|
|
@@ -3728,7 +3827,6 @@
|
|
|
3728
3827
|
if (this.collection.cloneObjects || options.forceClones) {
|
|
3729
3828
|
len = data.length;
|
|
3730
3829
|
method = options.forceCloneMethod || this.collection.cloneMethod;
|
|
3731
|
-
|
|
3732
3830
|
for (i = 0; i < len; i++) {
|
|
3733
3831
|
obj = clone(data[i], method);
|
|
3734
3832
|
if (options.removeMeta) {
|
|
@@ -3798,7 +3896,7 @@
|
|
|
3798
3896
|
// pass in each document object currently in resultset to user supplied updateFunction
|
|
3799
3897
|
for (var idx = 0; idx < len; idx++) {
|
|
3800
3898
|
// if we have cloning option specified or are doing differential delta changes, clone object first
|
|
3801
|
-
if (this.collection.cloneObjects || !this.collection.disableDeltaChangesApi) {
|
|
3899
|
+
if (!this.disableFreeze || this.collection.cloneObjects || !this.collection.disableDeltaChangesApi) {
|
|
3802
3900
|
obj = clone(rcd[this.filteredrows[idx]], this.collection.cloneMethod);
|
|
3803
3901
|
updateFunction(obj);
|
|
3804
3902
|
this.collection.update(obj);
|
|
@@ -4059,6 +4157,9 @@
|
|
|
4059
4157
|
|
|
4060
4158
|
// keep ordered filter pipeline
|
|
4061
4159
|
this.filterPipeline = [];
|
|
4160
|
+
if (!this.collection.disableFreeze) {
|
|
4161
|
+
Object.freeze(this.filterPipeline);
|
|
4162
|
+
}
|
|
4062
4163
|
|
|
4063
4164
|
// sorting member variables
|
|
4064
4165
|
// we only support one active search, applied using applySort() or applySimpleSort()
|
|
@@ -4071,12 +4172,23 @@
|
|
|
4071
4172
|
// once we refactor transactions, i will tie in certain transactional events
|
|
4072
4173
|
|
|
4073
4174
|
this.events = {
|
|
4074
|
-
'rebuild': []
|
|
4175
|
+
'rebuild': [],
|
|
4176
|
+
'filter': [],
|
|
4177
|
+
'sort': []
|
|
4075
4178
|
};
|
|
4076
4179
|
}
|
|
4077
4180
|
|
|
4078
4181
|
DynamicView.prototype = new LokiEventEmitter();
|
|
4182
|
+
DynamicView.prototype.constructor = DynamicView;
|
|
4079
4183
|
|
|
4184
|
+
/**
|
|
4185
|
+
* getSort() - used to get the current sort
|
|
4186
|
+
*
|
|
4187
|
+
* @returns function (sortFunction) or array (sortCriteria) or object (sortCriteriaSimple)
|
|
4188
|
+
*/
|
|
4189
|
+
DynamicView.prototype.getSort = function () {
|
|
4190
|
+
return this.sortFunction || this.sortCriteria || this.sortCriteriaSimple;
|
|
4191
|
+
};
|
|
4080
4192
|
|
|
4081
4193
|
/**
|
|
4082
4194
|
* rematerialize() - internally used immediately after deserialization (loading)
|
|
@@ -4104,9 +4216,13 @@
|
|
|
4104
4216
|
this.sortDirty = true;
|
|
4105
4217
|
}
|
|
4106
4218
|
|
|
4219
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4107
4220
|
if (options.hasOwnProperty('removeWhereFilters')) {
|
|
4108
4221
|
// for each view see if it had any where filters applied... since they don't
|
|
4109
4222
|
// serialize those functions lets remove those invalid filters
|
|
4223
|
+
if (wasFrozen) {
|
|
4224
|
+
this.filterPipeline = this.filterPipeline.slice();
|
|
4225
|
+
}
|
|
4110
4226
|
fpl = this.filterPipeline.length;
|
|
4111
4227
|
fpi = fpl;
|
|
4112
4228
|
while (fpi--) {
|
|
@@ -4114,7 +4230,6 @@
|
|
|
4114
4230
|
if (fpi !== this.filterPipeline.length - 1) {
|
|
4115
4231
|
this.filterPipeline[fpi] = this.filterPipeline[this.filterPipeline.length - 1];
|
|
4116
4232
|
}
|
|
4117
|
-
|
|
4118
4233
|
this.filterPipeline.length--;
|
|
4119
4234
|
}
|
|
4120
4235
|
}
|
|
@@ -4127,7 +4242,10 @@
|
|
|
4127
4242
|
// now re-apply 'find' filterPipeline ops
|
|
4128
4243
|
fpl = ofp.length;
|
|
4129
4244
|
for (idx = 0; idx < fpl; idx++) {
|
|
4130
|
-
this.applyFind(ofp[idx].val);
|
|
4245
|
+
this.applyFind(ofp[idx].val, ofp[idx].uid);
|
|
4246
|
+
}
|
|
4247
|
+
if (wasFrozen) {
|
|
4248
|
+
Object.freeze(this.filterPipeline);
|
|
4131
4249
|
}
|
|
4132
4250
|
|
|
4133
4251
|
// during creation of unit tests, i will remove this forced refresh and leave lazy
|
|
@@ -4184,7 +4302,6 @@
|
|
|
4184
4302
|
*/
|
|
4185
4303
|
DynamicView.prototype.toJSON = function () {
|
|
4186
4304
|
var copy = new DynamicView(this.collection, this.name, this.options);
|
|
4187
|
-
|
|
4188
4305
|
copy.resultset = this.resultset;
|
|
4189
4306
|
copy.resultdata = []; // let's not save data (copy) to minimize size
|
|
4190
4307
|
copy.resultsdirty = true;
|
|
@@ -4217,8 +4334,13 @@
|
|
|
4217
4334
|
|
|
4218
4335
|
this.cachedresultset = null;
|
|
4219
4336
|
|
|
4337
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4338
|
+
var filterChanged = this.filterPipeline.length > 0;
|
|
4220
4339
|
// keep ordered filter pipeline
|
|
4221
4340
|
this.filterPipeline = [];
|
|
4341
|
+
if (wasFrozen) {
|
|
4342
|
+
Object.freeze(this.filterPipeline);
|
|
4343
|
+
}
|
|
4222
4344
|
|
|
4223
4345
|
// sorting member variables
|
|
4224
4346
|
// we only support one active search, applied using applySort() or applySimpleSort()
|
|
@@ -4230,6 +4352,10 @@
|
|
|
4230
4352
|
if (options.queueSortPhase === true) {
|
|
4231
4353
|
this.queueSortPhase();
|
|
4232
4354
|
}
|
|
4355
|
+
|
|
4356
|
+
if (filterChanged) {
|
|
4357
|
+
this.emit('filter');
|
|
4358
|
+
}
|
|
4233
4359
|
};
|
|
4234
4360
|
|
|
4235
4361
|
/**
|
|
@@ -4251,6 +4377,7 @@
|
|
|
4251
4377
|
this.sortCriteriaSimple = null;
|
|
4252
4378
|
|
|
4253
4379
|
this.queueSortPhase();
|
|
4380
|
+
this.emit('sort');
|
|
4254
4381
|
|
|
4255
4382
|
return this;
|
|
4256
4383
|
};
|
|
@@ -4271,10 +4398,14 @@
|
|
|
4271
4398
|
*/
|
|
4272
4399
|
DynamicView.prototype.applySimpleSort = function (propname, options) {
|
|
4273
4400
|
this.sortCriteriaSimple = { propname: propname, options: options || false };
|
|
4401
|
+
if (!this.collection.disableFreeze) {
|
|
4402
|
+
deepFreeze(this.sortCriteriaSimple);
|
|
4403
|
+
}
|
|
4274
4404
|
this.sortCriteria = null;
|
|
4275
4405
|
this.sortFunction = null;
|
|
4276
4406
|
|
|
4277
4407
|
this.queueSortPhase();
|
|
4408
|
+
this.emit('sort');
|
|
4278
4409
|
|
|
4279
4410
|
return this;
|
|
4280
4411
|
};
|
|
@@ -4295,11 +4426,14 @@
|
|
|
4295
4426
|
*/
|
|
4296
4427
|
DynamicView.prototype.applySortCriteria = function (criteria) {
|
|
4297
4428
|
this.sortCriteria = criteria;
|
|
4429
|
+
if (!this.collection.disableFreeze) {
|
|
4430
|
+
deepFreeze(this.sortCriteria);
|
|
4431
|
+
}
|
|
4298
4432
|
this.sortCriteriaSimple = null;
|
|
4299
4433
|
this.sortFunction = null;
|
|
4300
4434
|
|
|
4301
4435
|
this.queueSortPhase();
|
|
4302
|
-
|
|
4436
|
+
this.emit('sort');
|
|
4303
4437
|
return this;
|
|
4304
4438
|
};
|
|
4305
4439
|
|
|
@@ -4370,7 +4504,17 @@
|
|
|
4370
4504
|
* @param {object} filter - The filter object. Refer to applyFilter() for extra details.
|
|
4371
4505
|
*/
|
|
4372
4506
|
DynamicView.prototype._addFilter = function (filter) {
|
|
4507
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4508
|
+
if (wasFrozen) {
|
|
4509
|
+
this.filterPipeline = this.filterPipeline.slice();
|
|
4510
|
+
}
|
|
4511
|
+
if (!this.collection.disableFreeze) {
|
|
4512
|
+
deepFreeze(filter);
|
|
4513
|
+
}
|
|
4373
4514
|
this.filterPipeline.push(filter);
|
|
4515
|
+
if (wasFrozen) {
|
|
4516
|
+
Object.freeze(this.filterPipeline);
|
|
4517
|
+
}
|
|
4374
4518
|
this.resultset[filter.type](filter.val);
|
|
4375
4519
|
};
|
|
4376
4520
|
|
|
@@ -4389,18 +4533,22 @@
|
|
|
4389
4533
|
}
|
|
4390
4534
|
|
|
4391
4535
|
var filters = this.filterPipeline;
|
|
4536
|
+
var wasFrozen = Object.isFrozen(filters);
|
|
4392
4537
|
this.filterPipeline = [];
|
|
4393
4538
|
|
|
4394
4539
|
for (var idx = 0, len = filters.length; idx < len; idx += 1) {
|
|
4395
4540
|
this._addFilter(filters[idx]);
|
|
4396
4541
|
}
|
|
4542
|
+
if (wasFrozen) {
|
|
4543
|
+
Object.freeze(this.filterPipeline);
|
|
4544
|
+
}
|
|
4397
4545
|
|
|
4398
4546
|
if (this.sortFunction || this.sortCriteria || this.sortCriteriaSimple) {
|
|
4399
4547
|
this.queueSortPhase();
|
|
4400
4548
|
} else {
|
|
4401
4549
|
this.queueRebuildEvent();
|
|
4402
4550
|
}
|
|
4403
|
-
|
|
4551
|
+
this.emit('filter');
|
|
4404
4552
|
return this;
|
|
4405
4553
|
};
|
|
4406
4554
|
|
|
@@ -4415,7 +4563,15 @@
|
|
|
4415
4563
|
DynamicView.prototype.applyFilter = function (filter) {
|
|
4416
4564
|
var idx = this._indexOfFilterWithId(filter.uid);
|
|
4417
4565
|
if (idx >= 0) {
|
|
4566
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4567
|
+
if (wasFrozen) {
|
|
4568
|
+
this.filterPipeline = this.filterPipeline.slice();
|
|
4569
|
+
}
|
|
4418
4570
|
this.filterPipeline[idx] = filter;
|
|
4571
|
+
if (wasFrozen) {
|
|
4572
|
+
freeze(filter);
|
|
4573
|
+
Object.freeze(this.filterPipeline);
|
|
4574
|
+
}
|
|
4419
4575
|
return this.reapplyFilters();
|
|
4420
4576
|
}
|
|
4421
4577
|
|
|
@@ -4433,6 +4589,7 @@
|
|
|
4433
4589
|
this.queueRebuildEvent();
|
|
4434
4590
|
}
|
|
4435
4591
|
|
|
4592
|
+
this.emit('filter');
|
|
4436
4593
|
return this;
|
|
4437
4594
|
};
|
|
4438
4595
|
|
|
@@ -4482,8 +4639,14 @@
|
|
|
4482
4639
|
if (idx < 0) {
|
|
4483
4640
|
throw new Error("Dynamic view does not contain a filter with ID: " + uid);
|
|
4484
4641
|
}
|
|
4485
|
-
|
|
4642
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4643
|
+
if (wasFrozen) {
|
|
4644
|
+
this.filterPipeline = this.filterPipeline.slice();
|
|
4645
|
+
}
|
|
4486
4646
|
this.filterPipeline.splice(idx, 1);
|
|
4647
|
+
if (wasFrozen) {
|
|
4648
|
+
Object.freeze(this.filterPipeline);
|
|
4649
|
+
}
|
|
4487
4650
|
this.reapplyFilters();
|
|
4488
4651
|
return this;
|
|
4489
4652
|
};
|
|
@@ -4743,23 +4906,23 @@
|
|
|
4743
4906
|
|
|
4744
4907
|
rmlen = objIndex.length;
|
|
4745
4908
|
// create intersection object of data indices to remove
|
|
4746
|
-
for(rmidx=0;rmidx<rmlen; rmidx++) {
|
|
4909
|
+
for (rmidx = 0; rmidx < rmlen; rmidx++) {
|
|
4747
4910
|
rxo[objIndex[rmidx]] = true;
|
|
4748
4911
|
}
|
|
4749
4912
|
|
|
4750
4913
|
// pivot remove data indices into remove filteredrows indices and dump in hashobject
|
|
4751
|
-
for (idx=0; idx<frlen; idx++) {
|
|
4914
|
+
for (idx = 0; idx < frlen; idx++) {
|
|
4752
4915
|
if (rxo[fr[idx]]) fxo[idx] = true;
|
|
4753
4916
|
}
|
|
4754
4917
|
|
|
4755
4918
|
// if any of the removed items were in our filteredrows...
|
|
4756
4919
|
if (Object.keys(fxo).length > 0) {
|
|
4757
4920
|
// remove them from filtered rows
|
|
4758
|
-
this.resultset.filteredrows = this.resultset.filteredrows.filter(function(di, idx) { return !fxo[idx]; });
|
|
4921
|
+
this.resultset.filteredrows = this.resultset.filteredrows.filter(function (di, idx) { return !fxo[idx]; });
|
|
4759
4922
|
// if persistent...
|
|
4760
4923
|
if (this.options.persistent) {
|
|
4761
4924
|
// remove from resultdata
|
|
4762
|
-
this.resultdata = this.resultdata.filter(function(obj, idx) { return !fxo[idx]; });
|
|
4925
|
+
this.resultdata = this.resultdata.filter(function (obj, idx) { return !fxo[idx]; });
|
|
4763
4926
|
}
|
|
4764
4927
|
|
|
4765
4928
|
// and queue sorts
|
|
@@ -4771,7 +4934,7 @@
|
|
|
4771
4934
|
}
|
|
4772
4935
|
|
|
4773
4936
|
// to remove holes, we need to 'shift down' indices, this filter function finds number of positions to shift
|
|
4774
|
-
var filt = function(idx) { return function(di) { return di < drs.filteredrows[idx]; }; };
|
|
4937
|
+
var filt = function (idx) { return function (di) { return di < drs.filteredrows[idx]; }; };
|
|
4775
4938
|
|
|
4776
4939
|
frlen = drs.filteredrows.length;
|
|
4777
4940
|
for (idx = 0; idx < frlen; idx++) {
|
|
@@ -4817,6 +4980,7 @@
|
|
|
4817
4980
|
* @param {boolean} [options.autoupdate=false] - use Object.observe to update objects automatically
|
|
4818
4981
|
* @param {boolean} [options.clone=false] - specify whether inserts and queries clone to/from user
|
|
4819
4982
|
* @param {boolean} [options.serializableIndices=true[]] - converts date values on binary indexed properties to epoch time
|
|
4983
|
+
* @param {boolean} [options.disableFreeze=true] - when false all docs are frozen
|
|
4820
4984
|
* @param {string} [options.cloneMethod='parse-stringify'] - 'parse-stringify', 'jquery-extend-deep', 'shallow', 'shallow-assign'
|
|
4821
4985
|
* @param {int=} options.ttl - age of document (in ms.) before document is considered aged/stale.
|
|
4822
4986
|
* @param {int=} options.ttlInterval - time interval for clearing out 'aged' documents; not set by default.
|
|
@@ -4828,7 +4992,7 @@
|
|
|
4828
4992
|
this.name = name;
|
|
4829
4993
|
// the data held by the collection
|
|
4830
4994
|
this.data = [];
|
|
4831
|
-
this.idIndex =
|
|
4995
|
+
this.idIndex = null; // position->$loki index (built lazily)
|
|
4832
4996
|
this.binaryIndices = {}; // user defined indexes
|
|
4833
4997
|
this.constraints = {
|
|
4834
4998
|
unique: {},
|
|
@@ -4836,7 +5000,7 @@
|
|
|
4836
5000
|
};
|
|
4837
5001
|
|
|
4838
5002
|
// unique contraints contain duplicate object references, so they are not persisted.
|
|
4839
|
-
// we will keep track of properties which have unique contraint applied here, and regenerate
|
|
5003
|
+
// we will keep track of properties which have unique contraint applied here, and regenerate lazily
|
|
4840
5004
|
this.uniqueNames = [];
|
|
4841
5005
|
|
|
4842
5006
|
// transforms will be used to store frequently used query chains as a series of steps
|
|
@@ -4865,9 +5029,9 @@
|
|
|
4865
5029
|
if (!Array.isArray(options.unique)) {
|
|
4866
5030
|
options.unique = [options.unique];
|
|
4867
5031
|
}
|
|
5032
|
+
// save names; actual index is built lazily
|
|
4868
5033
|
options.unique.forEach(function (prop) {
|
|
4869
|
-
self.uniqueNames.push(prop);
|
|
4870
|
-
self.constraints.unique[prop] = new UniqueIndex(prop);
|
|
5034
|
+
self.uniqueNames.push(prop);
|
|
4871
5035
|
});
|
|
4872
5036
|
}
|
|
4873
5037
|
|
|
@@ -4913,6 +5077,9 @@
|
|
|
4913
5077
|
// same 'after' serialization as it was 'before'.
|
|
4914
5078
|
this.serializableIndices = options.hasOwnProperty('serializableIndices') ? options.serializableIndices : true;
|
|
4915
5079
|
|
|
5080
|
+
// option to deep freeze all documents
|
|
5081
|
+
this.disableFreeze = options.hasOwnProperty('disableFreeze') ? options.disableFreeze : true;
|
|
5082
|
+
|
|
4916
5083
|
//option to activate a cleaner daemon - clears "aged" documents at set intervals.
|
|
4917
5084
|
this.ttl = {
|
|
4918
5085
|
age: null,
|
|
@@ -4945,10 +5112,8 @@
|
|
|
4945
5112
|
// lightweight changes tracking (loki IDs only) for optimized db saving
|
|
4946
5113
|
this.dirtyIds = [];
|
|
4947
5114
|
|
|
4948
|
-
// initialize the id index
|
|
4949
|
-
this.ensureId();
|
|
4950
|
-
var indices = [];
|
|
4951
5115
|
// initialize optional user-supplied indices array ['age', 'lname', 'zip']
|
|
5116
|
+
var indices = [];
|
|
4952
5117
|
if (options && options.indices) {
|
|
4953
5118
|
if (Object.prototype.toString.call(options.indices) === '[object Array]') {
|
|
4954
5119
|
indices = options.indices;
|
|
@@ -4983,7 +5148,7 @@
|
|
|
4983
5148
|
return self.removeAutoUpdateObserver(object);
|
|
4984
5149
|
try {
|
|
4985
5150
|
self.update(object);
|
|
4986
|
-
} catch (err) {}
|
|
5151
|
+
} catch (err) { }
|
|
4987
5152
|
});
|
|
4988
5153
|
}
|
|
4989
5154
|
|
|
@@ -5003,7 +5168,7 @@
|
|
|
5003
5168
|
|
|
5004
5169
|
function getObjectDelta(oldObject, newObject) {
|
|
5005
5170
|
var propertyNames = newObject !== null && typeof newObject === 'object' ? Object.keys(newObject) : null;
|
|
5006
|
-
if (propertyNames && propertyNames.length && ['string', 'boolean', 'number'].indexOf(typeof(newObject)) < 0) {
|
|
5171
|
+
if (propertyNames && propertyNames.length && ['string', 'boolean', 'number'].indexOf(typeof (newObject)) < 0) {
|
|
5007
5172
|
var delta = {};
|
|
5008
5173
|
for (var i = 0; i < propertyNames.length; i++) {
|
|
5009
5174
|
var propertyName = propertyNames[i];
|
|
@@ -5062,11 +5227,12 @@
|
|
|
5062
5227
|
}
|
|
5063
5228
|
|
|
5064
5229
|
Collection.prototype = new LokiEventEmitter();
|
|
5230
|
+
Collection.prototype.contructor = Collection;
|
|
5065
5231
|
|
|
5066
5232
|
/*
|
|
5067
5233
|
* For ChangeAPI default to clone entire object, for delta changes create object with only differences (+ $loki and meta)
|
|
5068
5234
|
*/
|
|
5069
|
-
Collection.prototype.createChange = function(name, op, obj, old) {
|
|
5235
|
+
Collection.prototype.createChange = function (name, op, obj, old) {
|
|
5070
5236
|
this.changes.push({
|
|
5071
5237
|
name: name,
|
|
5072
5238
|
operation: op,
|
|
@@ -5074,7 +5240,7 @@
|
|
|
5074
5240
|
});
|
|
5075
5241
|
};
|
|
5076
5242
|
|
|
5077
|
-
Collection.prototype.insertMeta = function(obj) {
|
|
5243
|
+
Collection.prototype.insertMeta = function (obj) {
|
|
5078
5244
|
var len, idx;
|
|
5079
5245
|
|
|
5080
5246
|
if (this.disableMeta || !obj) {
|
|
@@ -5085,7 +5251,7 @@
|
|
|
5085
5251
|
if (Array.isArray(obj)) {
|
|
5086
5252
|
len = obj.length;
|
|
5087
5253
|
|
|
5088
|
-
for(idx=0; idx<len; idx++) {
|
|
5254
|
+
for (idx = 0; idx < len; idx++) {
|
|
5089
5255
|
if (!obj[idx].hasOwnProperty('meta')) {
|
|
5090
5256
|
obj[idx].meta = {};
|
|
5091
5257
|
}
|
|
@@ -5106,36 +5272,42 @@
|
|
|
5106
5272
|
obj.meta.revision = 0;
|
|
5107
5273
|
};
|
|
5108
5274
|
|
|
5109
|
-
Collection.prototype.updateMeta = function(obj) {
|
|
5275
|
+
Collection.prototype.updateMeta = function (obj) {
|
|
5110
5276
|
if (this.disableMeta || !obj) {
|
|
5111
|
-
return;
|
|
5277
|
+
return obj;
|
|
5278
|
+
}
|
|
5279
|
+
if (!this.disableFreeze) {
|
|
5280
|
+
obj = unFreeze(obj);
|
|
5281
|
+
obj.meta = unFreeze(obj.meta);
|
|
5112
5282
|
}
|
|
5113
5283
|
obj.meta.updated = (new Date()).getTime();
|
|
5114
5284
|
obj.meta.revision += 1;
|
|
5285
|
+
return obj;
|
|
5115
5286
|
};
|
|
5116
5287
|
|
|
5117
|
-
Collection.prototype.createInsertChange = function(obj) {
|
|
5288
|
+
Collection.prototype.createInsertChange = function (obj) {
|
|
5118
5289
|
this.createChange(this.name, 'I', obj);
|
|
5119
5290
|
};
|
|
5120
5291
|
|
|
5121
|
-
Collection.prototype.createUpdateChange = function(obj, old) {
|
|
5292
|
+
Collection.prototype.createUpdateChange = function (obj, old) {
|
|
5122
5293
|
this.createChange(this.name, 'U', obj, old);
|
|
5123
5294
|
};
|
|
5124
5295
|
|
|
5125
|
-
Collection.prototype.insertMetaWithChange = function(obj) {
|
|
5296
|
+
Collection.prototype.insertMetaWithChange = function (obj) {
|
|
5126
5297
|
this.insertMeta(obj);
|
|
5127
5298
|
this.createInsertChange(obj);
|
|
5128
5299
|
};
|
|
5129
5300
|
|
|
5130
|
-
Collection.prototype.updateMetaWithChange = function(obj, old) {
|
|
5131
|
-
this.updateMeta(obj);
|
|
5301
|
+
Collection.prototype.updateMetaWithChange = function (obj, old, objFrozen) {
|
|
5302
|
+
obj = this.updateMeta(obj, objFrozen);
|
|
5132
5303
|
this.createUpdateChange(obj, old);
|
|
5304
|
+
return obj;
|
|
5133
5305
|
};
|
|
5134
5306
|
|
|
5135
5307
|
Collection.prototype.lokiConsoleWrapper = {
|
|
5136
|
-
log: function () {},
|
|
5137
|
-
warn: function () {},
|
|
5138
|
-
error: function () {},
|
|
5308
|
+
log: function () { },
|
|
5309
|
+
warn: function () { },
|
|
5310
|
+
error: function () { },
|
|
5139
5311
|
};
|
|
5140
5312
|
|
|
5141
5313
|
Collection.prototype.addAutoUpdateObserver = function (object) {
|
|
@@ -5422,7 +5594,7 @@
|
|
|
5422
5594
|
options.randomSamplingFactor = 0.1;
|
|
5423
5595
|
}
|
|
5424
5596
|
|
|
5425
|
-
var valid=true, idx, iter, pos, len, biv;
|
|
5597
|
+
var valid = true, idx, iter, pos, len, biv;
|
|
5426
5598
|
|
|
5427
5599
|
// make sure we are passed a valid binary index name
|
|
5428
5600
|
if (!this.binaryIndices.hasOwnProperty(property)) {
|
|
@@ -5458,28 +5630,28 @@
|
|
|
5458
5630
|
if (options.randomSampling) {
|
|
5459
5631
|
// validate first and last
|
|
5460
5632
|
if (!LokiOps.$lte(Utils.getIn(this.data[biv[0]], property, usingDotNotation),
|
|
5461
|
-
|
|
5462
|
-
valid=false;
|
|
5633
|
+
Utils.getIn(this.data[biv[1]], property, usingDotNotation))) {
|
|
5634
|
+
valid = false;
|
|
5463
5635
|
}
|
|
5464
|
-
if (!LokiOps.$lte(Utils.getIn(this.data[biv[len-2]], property, usingDotNotation),
|
|
5465
|
-
|
|
5466
|
-
valid=false;
|
|
5636
|
+
if (!LokiOps.$lte(Utils.getIn(this.data[biv[len - 2]], property, usingDotNotation),
|
|
5637
|
+
Utils.getIn(this.data[biv[len - 1]], property, usingDotNotation))) {
|
|
5638
|
+
valid = false;
|
|
5467
5639
|
}
|
|
5468
5640
|
|
|
5469
5641
|
// if first and last positions are sorted correctly with their nearest neighbor,
|
|
5470
5642
|
// continue onto random sampling phase...
|
|
5471
5643
|
if (valid) {
|
|
5472
5644
|
// # random samplings = total count * sampling factor
|
|
5473
|
-
iter = Math.floor((len-1) * options.randomSamplingFactor);
|
|
5645
|
+
iter = Math.floor((len - 1) * options.randomSamplingFactor);
|
|
5474
5646
|
|
|
5475
5647
|
// for each random sampling, validate that the binary index is sequenced properly
|
|
5476
5648
|
// with next higher value.
|
|
5477
|
-
for(idx=0; idx<iter-1; idx++) {
|
|
5649
|
+
for (idx = 0; idx < iter - 1; idx++) {
|
|
5478
5650
|
// calculate random position
|
|
5479
|
-
pos = Math.floor(Math.random() * (len-1));
|
|
5651
|
+
pos = Math.floor(Math.random() * (len - 1));
|
|
5480
5652
|
if (!LokiOps.$lte(Utils.getIn(this.data[biv[pos]], property, usingDotNotation),
|
|
5481
|
-
|
|
5482
|
-
valid=false;
|
|
5653
|
+
Utils.getIn(this.data[biv[pos + 1]], property, usingDotNotation))) {
|
|
5654
|
+
valid = false;
|
|
5483
5655
|
break;
|
|
5484
5656
|
}
|
|
5485
5657
|
}
|
|
@@ -5487,10 +5659,10 @@
|
|
|
5487
5659
|
}
|
|
5488
5660
|
else {
|
|
5489
5661
|
// validate that the binary index is sequenced properly
|
|
5490
|
-
for(idx=0; idx<len-1; idx++) {
|
|
5662
|
+
for (idx = 0; idx < len - 1; idx++) {
|
|
5491
5663
|
if (!LokiOps.$lte(Utils.getIn(this.data[biv[idx]], property, usingDotNotation),
|
|
5492
|
-
|
|
5493
|
-
valid=false;
|
|
5664
|
+
Utils.getIn(this.data[biv[idx + 1]], property, usingDotNotation))) {
|
|
5665
|
+
valid = false;
|
|
5494
5666
|
break;
|
|
5495
5667
|
}
|
|
5496
5668
|
}
|
|
@@ -5516,6 +5688,19 @@
|
|
|
5516
5688
|
return result;
|
|
5517
5689
|
};
|
|
5518
5690
|
|
|
5691
|
+
/**
|
|
5692
|
+
* Returns a named unique index
|
|
5693
|
+
* @param {string} field - indexed field name
|
|
5694
|
+
* @param {boolean} force - if `true`, will rebuild index; otherwise, function may return null
|
|
5695
|
+
*/
|
|
5696
|
+
Collection.prototype.getUniqueIndex = function (field, force) {
|
|
5697
|
+
var index = this.constraints.unique[field];
|
|
5698
|
+
if (!index && force) {
|
|
5699
|
+
return this.ensureUniqueIndex(field);
|
|
5700
|
+
}
|
|
5701
|
+
return index;
|
|
5702
|
+
};
|
|
5703
|
+
|
|
5519
5704
|
Collection.prototype.ensureUniqueIndex = function (field) {
|
|
5520
5705
|
var index = this.constraints.unique[field];
|
|
5521
5706
|
if (!index) {
|
|
@@ -5585,13 +5770,17 @@
|
|
|
5585
5770
|
* Rebuild idIndex
|
|
5586
5771
|
*/
|
|
5587
5772
|
Collection.prototype.ensureId = function () {
|
|
5588
|
-
|
|
5773
|
+
if (this.idIndex) {
|
|
5774
|
+
return;
|
|
5775
|
+
}
|
|
5776
|
+
var data = this.data,
|
|
5589
5777
|
i = 0;
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
for (i; i < len; i
|
|
5593
|
-
|
|
5778
|
+
var len = data.length;
|
|
5779
|
+
var index = new Array(len);
|
|
5780
|
+
for (i; i < len; i++) {
|
|
5781
|
+
index[i] = data[i].$loki;
|
|
5594
5782
|
}
|
|
5783
|
+
this.idIndex = index;
|
|
5595
5784
|
};
|
|
5596
5785
|
|
|
5597
5786
|
/**
|
|
@@ -5634,7 +5823,7 @@
|
|
|
5634
5823
|
**/
|
|
5635
5824
|
Collection.prototype.removeDynamicView = function (name) {
|
|
5636
5825
|
this.DynamicViews =
|
|
5637
|
-
this.DynamicViews.filter(function(dv) { return dv.name !== name; });
|
|
5826
|
+
this.DynamicViews.filter(function (dv) { return dv.name !== name; });
|
|
5638
5827
|
};
|
|
5639
5828
|
|
|
5640
5829
|
/**
|
|
@@ -5676,7 +5865,7 @@
|
|
|
5676
5865
|
* @param {object} filterObject - 'mongo-like' query object
|
|
5677
5866
|
* @memberof Collection
|
|
5678
5867
|
*/
|
|
5679
|
-
Collection.prototype.findAndRemove = function(filterObject) {
|
|
5868
|
+
Collection.prototype.findAndRemove = function (filterObject) {
|
|
5680
5869
|
this.chain().find(filterObject).remove();
|
|
5681
5870
|
};
|
|
5682
5871
|
|
|
@@ -5764,12 +5953,19 @@
|
|
|
5764
5953
|
|
|
5765
5954
|
// if configured to clone, do so now... otherwise just use same obj reference
|
|
5766
5955
|
var obj = this.cloneObjects ? clone(doc, this.cloneMethod) : doc;
|
|
5956
|
+
if (!this.disableFreeze) {
|
|
5957
|
+
obj = unFreeze(obj);
|
|
5958
|
+
}
|
|
5767
5959
|
|
|
5768
|
-
if (!this.disableMeta
|
|
5769
|
-
obj.meta
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5960
|
+
if (!this.disableMeta) {
|
|
5961
|
+
if (typeof obj.meta === 'undefined') {
|
|
5962
|
+
obj.meta = {
|
|
5963
|
+
revision: 0,
|
|
5964
|
+
created: 0
|
|
5965
|
+
};
|
|
5966
|
+
} else if (!this.disableFreeze) {
|
|
5967
|
+
obj.meta = unFreeze(obj.meta);
|
|
5968
|
+
}
|
|
5773
5969
|
}
|
|
5774
5970
|
|
|
5775
5971
|
// both 'pre-insert' and 'insert' events are passed internal data reference even when cloning
|
|
@@ -5785,18 +5981,23 @@
|
|
|
5785
5981
|
// (moved from "insert" event listener to allow internal reference to be used)
|
|
5786
5982
|
if (this.disableChangesApi) {
|
|
5787
5983
|
this.insertMeta(obj);
|
|
5788
|
-
}
|
|
5789
|
-
else {
|
|
5984
|
+
} else {
|
|
5790
5985
|
this.insertMetaWithChange(obj);
|
|
5791
5986
|
}
|
|
5792
5987
|
|
|
5988
|
+
if (!this.disableFreeze) {
|
|
5989
|
+
deepFreeze(obj);
|
|
5990
|
+
}
|
|
5991
|
+
|
|
5793
5992
|
// if cloning is enabled, emit insert event with clone of new object
|
|
5794
5993
|
returnObj = this.cloneObjects ? clone(obj, this.cloneMethod) : obj;
|
|
5994
|
+
|
|
5795
5995
|
if (!bulkInsert) {
|
|
5796
5996
|
this.emit('insert', returnObj);
|
|
5797
5997
|
}
|
|
5798
5998
|
|
|
5799
5999
|
this.addAutoUpdateObserver(returnObj);
|
|
6000
|
+
|
|
5800
6001
|
return returnObj;
|
|
5801
6002
|
};
|
|
5802
6003
|
|
|
@@ -5812,43 +6013,31 @@
|
|
|
5812
6013
|
options = options || {};
|
|
5813
6014
|
|
|
5814
6015
|
this.data = [];
|
|
5815
|
-
this.idIndex =
|
|
6016
|
+
this.idIndex = null;
|
|
5816
6017
|
this.cachedIndex = null;
|
|
5817
6018
|
this.cachedBinaryIndex = null;
|
|
5818
6019
|
this.cachedData = null;
|
|
5819
6020
|
this.maxId = 0;
|
|
5820
6021
|
this.DynamicViews = [];
|
|
5821
6022
|
this.dirty = true;
|
|
6023
|
+
this.constraints = {
|
|
6024
|
+
unique: {},
|
|
6025
|
+
exact: {}
|
|
6026
|
+
};
|
|
5822
6027
|
|
|
5823
6028
|
// if removing indices entirely
|
|
5824
6029
|
if (options.removeIndices === true) {
|
|
5825
6030
|
this.binaryIndices = {};
|
|
5826
|
-
|
|
5827
|
-
this.constraints = {
|
|
5828
|
-
unique: {},
|
|
5829
|
-
exact: {}
|
|
5830
|
-
};
|
|
5831
6031
|
this.uniqueNames = [];
|
|
5832
6032
|
}
|
|
5833
6033
|
// clear indices but leave definitions in place
|
|
5834
6034
|
else {
|
|
5835
6035
|
// clear binary indices
|
|
5836
6036
|
var keys = Object.keys(this.binaryIndices);
|
|
5837
|
-
keys.forEach(function(biname) {
|
|
6037
|
+
keys.forEach(function (biname) {
|
|
5838
6038
|
self.binaryIndices[biname].dirty = false;
|
|
5839
6039
|
self.binaryIndices[biname].values = [];
|
|
5840
6040
|
});
|
|
5841
|
-
|
|
5842
|
-
// clear entire unique indices definition
|
|
5843
|
-
this.constraints = {
|
|
5844
|
-
unique: {},
|
|
5845
|
-
exact: {}
|
|
5846
|
-
};
|
|
5847
|
-
|
|
5848
|
-
// add definitions back
|
|
5849
|
-
this.uniqueNames.forEach(function(uiname) {
|
|
5850
|
-
self.ensureUniqueIndex(uiname);
|
|
5851
|
-
});
|
|
5852
6041
|
}
|
|
5853
6042
|
};
|
|
5854
6043
|
|
|
@@ -5873,7 +6062,7 @@
|
|
|
5873
6062
|
}
|
|
5874
6063
|
|
|
5875
6064
|
try {
|
|
5876
|
-
for (k=0; k < len; k += 1) {
|
|
6065
|
+
for (k = 0; k < len; k += 1) {
|
|
5877
6066
|
this.update(doc[k]);
|
|
5878
6067
|
}
|
|
5879
6068
|
}
|
|
@@ -5907,12 +6096,12 @@
|
|
|
5907
6096
|
position = arr[1]; // position in data array
|
|
5908
6097
|
|
|
5909
6098
|
// if configured to clone, do so now... otherwise just use same obj reference
|
|
5910
|
-
newInternal = this.cloneObjects || !this.disableDeltaChangesApi ? clone(doc, this.cloneMethod) : doc;
|
|
6099
|
+
newInternal = this.cloneObjects || (!this.disableDeltaChangesApi && this.disableFreeze) ? clone(doc, this.cloneMethod) : doc;
|
|
5911
6100
|
|
|
5912
6101
|
this.emit('pre-update', doc);
|
|
5913
6102
|
|
|
5914
|
-
|
|
5915
|
-
self.
|
|
6103
|
+
this.uniqueNames.forEach(function (key) {
|
|
6104
|
+
self.getUniqueIndex(key, true).update(oldInternal, newInternal);
|
|
5916
6105
|
});
|
|
5917
6106
|
|
|
5918
6107
|
// operate the update
|
|
@@ -5952,10 +6141,13 @@
|
|
|
5952
6141
|
|
|
5953
6142
|
// update meta and store changes if ChangesAPI is enabled
|
|
5954
6143
|
if (this.disableChangesApi) {
|
|
5955
|
-
this.updateMeta(newInternal
|
|
6144
|
+
newInternal = this.updateMeta(newInternal);
|
|
6145
|
+
} else {
|
|
6146
|
+
newInternal = this.updateMetaWithChange(newInternal, oldInternal);
|
|
5956
6147
|
}
|
|
5957
|
-
|
|
5958
|
-
|
|
6148
|
+
|
|
6149
|
+
if (!this.disableFreeze) {
|
|
6150
|
+
deepFreeze(newInternal);
|
|
5959
6151
|
}
|
|
5960
6152
|
|
|
5961
6153
|
var returnObj;
|
|
@@ -6004,23 +6196,23 @@
|
|
|
6004
6196
|
this.maxId = (this.data[this.data.length - 1].$loki + 1);
|
|
6005
6197
|
}
|
|
6006
6198
|
|
|
6007
|
-
|
|
6199
|
+
var newId = this.maxId;
|
|
6200
|
+
obj.$loki = newId;
|
|
6008
6201
|
|
|
6009
6202
|
if (!this.disableMeta) {
|
|
6010
6203
|
obj.meta.version = 0;
|
|
6011
6204
|
}
|
|
6012
6205
|
|
|
6013
|
-
var
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6206
|
+
for (var i = 0, len = this.uniqueNames.length; i < len; i ++) {
|
|
6207
|
+
this.getUniqueIndex(this.uniqueNames[i], true).set(obj);
|
|
6208
|
+
}
|
|
6209
|
+
|
|
6210
|
+
if (this.idIndex) {
|
|
6211
|
+
this.idIndex.push(newId);
|
|
6018
6212
|
}
|
|
6019
6213
|
|
|
6020
|
-
// add new obj id to idIndex
|
|
6021
|
-
this.idIndex.push(obj.$loki);
|
|
6022
6214
|
if (this.isIncremental) {
|
|
6023
|
-
this.dirtyIds.push(
|
|
6215
|
+
this.dirtyIds.push(newId);
|
|
6024
6216
|
}
|
|
6025
6217
|
|
|
6026
6218
|
// add the object
|
|
@@ -6031,14 +6223,14 @@
|
|
|
6031
6223
|
// now that we can efficiently determine the data[] position of newly added document,
|
|
6032
6224
|
// submit it for all registered DynamicViews to evaluate for inclusion/exclusion
|
|
6033
6225
|
var dvlen = this.DynamicViews.length;
|
|
6034
|
-
for (
|
|
6226
|
+
for (i = 0; i < dvlen; i++) {
|
|
6035
6227
|
this.DynamicViews[i].evaluateDocument(addedPos, true);
|
|
6036
6228
|
}
|
|
6037
6229
|
|
|
6038
6230
|
if (this.adaptiveBinaryIndices) {
|
|
6039
6231
|
// for each binary index defined in collection, immediately update rather than flag for lazy rebuild
|
|
6040
6232
|
var bIndices = this.binaryIndices;
|
|
6041
|
-
for (key in bIndices) {
|
|
6233
|
+
for (var key in bIndices) {
|
|
6042
6234
|
this.adaptiveBinaryIndexInsert(addedPos, key);
|
|
6043
6235
|
}
|
|
6044
6236
|
}
|
|
@@ -6065,7 +6257,7 @@
|
|
|
6065
6257
|
* @param {function} updateFunction - update function to run against filtered documents
|
|
6066
6258
|
* @memberof Collection
|
|
6067
6259
|
*/
|
|
6068
|
-
Collection.prototype.updateWhere = function(filterFunction, updateFunction) {
|
|
6260
|
+
Collection.prototype.updateWhere = function (filterFunction, updateFunction) {
|
|
6069
6261
|
var results = this.where(filterFunction),
|
|
6070
6262
|
i = 0,
|
|
6071
6263
|
obj;
|
|
@@ -6105,21 +6297,22 @@
|
|
|
6105
6297
|
* Internal method to remove a batch of documents from the collection.
|
|
6106
6298
|
* @param {number[]} positions - data/idIndex positions to remove
|
|
6107
6299
|
*/
|
|
6108
|
-
Collection.prototype.removeBatchByPositions = function(positions) {
|
|
6300
|
+
Collection.prototype.removeBatchByPositions = function (positions) {
|
|
6109
6301
|
var len = positions.length;
|
|
6110
6302
|
var xo = {};
|
|
6111
6303
|
var dlen, didx, idx;
|
|
6112
|
-
var bic=Object.keys(this.binaryIndices).length;
|
|
6113
|
-
var uic=Object.keys(this.constraints.unique).length;
|
|
6304
|
+
var bic = Object.keys(this.binaryIndices).length;
|
|
6305
|
+
var uic = Object.keys(this.constraints.unique).length;
|
|
6114
6306
|
var adaptiveOverride = this.adaptiveBinaryIndices && Object.keys(this.binaryIndices).length > 0;
|
|
6115
|
-
var doc, self=this;
|
|
6307
|
+
var doc, self = this;
|
|
6116
6308
|
|
|
6117
6309
|
try {
|
|
6118
6310
|
this.startTransaction();
|
|
6119
6311
|
|
|
6120
6312
|
// create hashobject for positional removal inclusion tests...
|
|
6121
6313
|
// all keys defined in this hashobject represent $loki ids of the documents to remove.
|
|
6122
|
-
|
|
6314
|
+
this.ensureId();
|
|
6315
|
+
for (idx = 0; idx < len; idx++) {
|
|
6123
6316
|
xo[this.idIndex[positions[idx]]] = true;
|
|
6124
6317
|
}
|
|
6125
6318
|
|
|
@@ -6148,11 +6341,14 @@
|
|
|
6148
6341
|
}
|
|
6149
6342
|
|
|
6150
6343
|
if (uic) {
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
self.
|
|
6344
|
+
this.uniqueNames.forEach(function (key) {
|
|
6345
|
+
var index = self.getUniqueIndex(key);
|
|
6346
|
+
if (index) {
|
|
6347
|
+
for (idx = 0; idx < len; idx++) {
|
|
6348
|
+
doc = self.data[positions[idx]];
|
|
6349
|
+
if (doc[key] !== null && doc[key] !== undefined) {
|
|
6350
|
+
index.remove(doc[key]);
|
|
6351
|
+
}
|
|
6156
6352
|
}
|
|
6157
6353
|
}
|
|
6158
6354
|
});
|
|
@@ -6163,14 +6359,14 @@
|
|
|
6163
6359
|
// since data not removed yet, in future we can emit single delete event with array...
|
|
6164
6360
|
// for now that might be breaking change to put in potential 1.6 or LokiDB (lokijs2) version
|
|
6165
6361
|
if (!this.disableChangesApi || this.events.delete.length > 1) {
|
|
6166
|
-
for(idx=0; idx < len; idx++) {
|
|
6362
|
+
for (idx = 0; idx < len; idx++) {
|
|
6167
6363
|
this.emit('delete', this.data[positions[idx]]);
|
|
6168
6364
|
}
|
|
6169
6365
|
}
|
|
6170
6366
|
|
|
6171
6367
|
// remove from data[] :
|
|
6172
6368
|
// filter collection data for items not in inclusion hashobject
|
|
6173
|
-
this.data = this.data.filter(function(obj) {
|
|
6369
|
+
this.data = this.data.filter(function (obj) {
|
|
6174
6370
|
return !xo[obj.$loki];
|
|
6175
6371
|
});
|
|
6176
6372
|
|
|
@@ -6182,8 +6378,8 @@
|
|
|
6182
6378
|
|
|
6183
6379
|
// remove from idIndex[] :
|
|
6184
6380
|
// filter idIndex for items not in inclusion hashobject
|
|
6185
|
-
this.idIndex = this.idIndex.filter(function(id) {
|
|
6186
|
-
|
|
6381
|
+
this.idIndex = this.idIndex.filter(function (id) {
|
|
6382
|
+
return !xo[id];
|
|
6187
6383
|
});
|
|
6188
6384
|
|
|
6189
6385
|
if (this.adaptiveBinaryIndices && adaptiveOverride) {
|
|
@@ -6212,21 +6408,21 @@
|
|
|
6212
6408
|
* Internal method called by remove()
|
|
6213
6409
|
* @param {object[]|number[]} batch - array of documents or $loki ids to remove
|
|
6214
6410
|
*/
|
|
6215
|
-
Collection.prototype.removeBatch = function(batch) {
|
|
6411
|
+
Collection.prototype.removeBatch = function (batch) {
|
|
6216
6412
|
var len = batch.length,
|
|
6217
|
-
dlen=this.data.length,
|
|
6413
|
+
dlen = this.data.length,
|
|
6218
6414
|
idx;
|
|
6219
6415
|
var xlt = {};
|
|
6220
6416
|
var posx = [];
|
|
6221
6417
|
|
|
6222
6418
|
// create lookup hashobject to translate $loki id to position
|
|
6223
|
-
for (idx=0; idx < dlen; idx++) {
|
|
6419
|
+
for (idx = 0; idx < dlen; idx++) {
|
|
6224
6420
|
xlt[this.data[idx].$loki] = idx;
|
|
6225
6421
|
}
|
|
6226
6422
|
|
|
6227
6423
|
// iterate the batch
|
|
6228
|
-
for (idx=0; idx < len; idx++) {
|
|
6229
|
-
if (typeof(batch[idx]) === 'object') {
|
|
6424
|
+
for (idx = 0; idx < len; idx++) {
|
|
6425
|
+
if (typeof (batch[idx]) === 'object') {
|
|
6230
6426
|
posx.push(xlt[batch[idx].$loki]);
|
|
6231
6427
|
}
|
|
6232
6428
|
else {
|
|
@@ -6243,6 +6439,8 @@
|
|
|
6243
6439
|
* @memberof Collection
|
|
6244
6440
|
*/
|
|
6245
6441
|
Collection.prototype.remove = function (doc) {
|
|
6442
|
+
var frozen;
|
|
6443
|
+
|
|
6246
6444
|
if (typeof doc === 'number') {
|
|
6247
6445
|
doc = this.get(doc);
|
|
6248
6446
|
}
|
|
@@ -6265,9 +6463,12 @@
|
|
|
6265
6463
|
// obj = arr[0],
|
|
6266
6464
|
position = arr[1];
|
|
6267
6465
|
var self = this;
|
|
6268
|
-
|
|
6466
|
+
this.uniqueNames.forEach(function (key) {
|
|
6269
6467
|
if (doc[key] !== null && typeof doc[key] !== 'undefined') {
|
|
6270
|
-
self.
|
|
6468
|
+
var index = self.getUniqueIndex(key);
|
|
6469
|
+
if (index) {
|
|
6470
|
+
index.remove(doc[key]);
|
|
6471
|
+
}
|
|
6271
6472
|
}
|
|
6272
6473
|
});
|
|
6273
6474
|
// now that we can efficiently determine the data[] position of newly added document,
|
|
@@ -6300,8 +6501,15 @@
|
|
|
6300
6501
|
this.commit();
|
|
6301
6502
|
this.dirty = true; // for autosave scenarios
|
|
6302
6503
|
this.emit('delete', arr[0]);
|
|
6504
|
+
|
|
6505
|
+
if (!this.disableFreeze) {
|
|
6506
|
+
doc = unFreeze(doc);
|
|
6507
|
+
}
|
|
6303
6508
|
delete doc.$loki;
|
|
6304
6509
|
delete doc.meta;
|
|
6510
|
+
if (!this.disableFreeze) {
|
|
6511
|
+
freeze(doc);
|
|
6512
|
+
}
|
|
6305
6513
|
return doc;
|
|
6306
6514
|
|
|
6307
6515
|
} catch (err) {
|
|
@@ -6325,6 +6533,10 @@
|
|
|
6325
6533
|
* @memberof Collection
|
|
6326
6534
|
*/
|
|
6327
6535
|
Collection.prototype.get = function (id, returnPosition) {
|
|
6536
|
+
if (!this.idIndex) {
|
|
6537
|
+
this.ensureId();
|
|
6538
|
+
}
|
|
6539
|
+
|
|
6328
6540
|
var retpos = returnPosition || false,
|
|
6329
6541
|
data = this.idIndex,
|
|
6330
6542
|
max = data.length - 1,
|
|
@@ -6364,7 +6576,7 @@
|
|
|
6364
6576
|
* @param {int} dataPosition : coll.data array index/position
|
|
6365
6577
|
* @param {string} binaryIndexName : index to search for dataPosition in
|
|
6366
6578
|
*/
|
|
6367
|
-
Collection.prototype.getBinaryIndexPosition = function(dataPosition, binaryIndexName) {
|
|
6579
|
+
Collection.prototype.getBinaryIndexPosition = function (dataPosition, binaryIndexName) {
|
|
6368
6580
|
var val = Utils.getIn(this.data[dataPosition], binaryIndexName, true);
|
|
6369
6581
|
var index = this.binaryIndices[binaryIndexName].values;
|
|
6370
6582
|
|
|
@@ -6383,7 +6595,7 @@
|
|
|
6383
6595
|
// narrow down the sub-segment of index values
|
|
6384
6596
|
// where the indexed property value exactly matches our
|
|
6385
6597
|
// value and then linear scan to find exact -index- position
|
|
6386
|
-
for(var idx = min; idx <= max; idx++) {
|
|
6598
|
+
for (var idx = min; idx <= max; idx++) {
|
|
6387
6599
|
if (index[idx] === dataPosition) return idx;
|
|
6388
6600
|
}
|
|
6389
6601
|
|
|
@@ -6396,7 +6608,7 @@
|
|
|
6396
6608
|
* @param {int} dataPosition : coll.data array index/position
|
|
6397
6609
|
* @param {string} binaryIndexName : index to search for dataPosition in
|
|
6398
6610
|
*/
|
|
6399
|
-
Collection.prototype.adaptiveBinaryIndexInsert = function(dataPosition, binaryIndexName) {
|
|
6611
|
+
Collection.prototype.adaptiveBinaryIndexInsert = function (dataPosition, binaryIndexName) {
|
|
6400
6612
|
var usingDotNotation = (binaryIndexName.indexOf('.') !== -1);
|
|
6401
6613
|
var index = this.binaryIndices[binaryIndexName].values;
|
|
6402
6614
|
var val = Utils.getIn(this.data[dataPosition], binaryIndexName, usingDotNotation);
|
|
@@ -6407,7 +6619,7 @@
|
|
|
6407
6619
|
val = Utils.getIn(this.data[dataPosition], binaryIndexName);
|
|
6408
6620
|
}
|
|
6409
6621
|
|
|
6410
|
-
var idxPos = (index.length === 0)?0:this.calculateRangeStart(binaryIndexName, val, true, usingDotNotation);
|
|
6622
|
+
var idxPos = (index.length === 0) ? 0 : this.calculateRangeStart(binaryIndexName, val, true, usingDotNotation);
|
|
6411
6623
|
|
|
6412
6624
|
// insert new data index into our binary index at the proper sorted location for relevant property calculated by idxPos.
|
|
6413
6625
|
// doing this after adjusting dataPositions so no clash with previous item at that position.
|
|
@@ -6419,14 +6631,14 @@
|
|
|
6419
6631
|
* @param {int} dataPosition : coll.data array index/position
|
|
6420
6632
|
* @param {string} binaryIndexName : index to search for dataPosition in
|
|
6421
6633
|
*/
|
|
6422
|
-
Collection.prototype.adaptiveBinaryIndexUpdate = function(dataPosition, binaryIndexName) {
|
|
6634
|
+
Collection.prototype.adaptiveBinaryIndexUpdate = function (dataPosition, binaryIndexName) {
|
|
6423
6635
|
// linear scan needed to find old position within index unless we optimize for clone scenarios later
|
|
6424
6636
|
// within (my) node 5.6.0, the following for() loop with strict compare is -much- faster than indexOf()
|
|
6425
6637
|
var idxPos,
|
|
6426
6638
|
index = this.binaryIndices[binaryIndexName].values,
|
|
6427
|
-
len=index.length;
|
|
6639
|
+
len = index.length;
|
|
6428
6640
|
|
|
6429
|
-
for(idxPos=0; idxPos < len; idxPos++) {
|
|
6641
|
+
for (idxPos = 0; idxPos < len; idxPos++) {
|
|
6430
6642
|
if (index[idxPos] === dataPosition) break;
|
|
6431
6643
|
}
|
|
6432
6644
|
|
|
@@ -6442,7 +6654,7 @@
|
|
|
6442
6654
|
* @param {number|number[]} dataPosition : coll.data array index/position
|
|
6443
6655
|
* @param {string} binaryIndexName : index to search for dataPosition in
|
|
6444
6656
|
*/
|
|
6445
|
-
Collection.prototype.adaptiveBinaryIndexRemove = function(dataPosition, binaryIndexName, removedFromIndexOnly) {
|
|
6657
|
+
Collection.prototype.adaptiveBinaryIndexRemove = function (dataPosition, binaryIndexName, removedFromIndexOnly) {
|
|
6446
6658
|
var bi = this.binaryIndices[binaryIndexName];
|
|
6447
6659
|
var len, idx, rmidx, rmlen, rxo = {};
|
|
6448
6660
|
var curr, shift, idxPos;
|
|
@@ -6456,12 +6668,12 @@
|
|
|
6456
6668
|
}
|
|
6457
6669
|
// we were passed an array (batch) of documents so use this 'batch optimized' algorithm
|
|
6458
6670
|
else {
|
|
6459
|
-
for(rmidx=0;rmidx<rmlen; rmidx++) {
|
|
6671
|
+
for (rmidx = 0; rmidx < rmlen; rmidx++) {
|
|
6460
6672
|
rxo[dataPosition[rmidx]] = true;
|
|
6461
6673
|
}
|
|
6462
6674
|
|
|
6463
6675
|
// remove document from index (with filter function)
|
|
6464
|
-
bi.values = bi.values.filter(function(di) { return !rxo[di]; });
|
|
6676
|
+
bi.values = bi.values.filter(function (di) { return !rxo[di]; });
|
|
6465
6677
|
|
|
6466
6678
|
// if we passed this optional flag parameter, we are calling from adaptiveBinaryIndexUpdate,
|
|
6467
6679
|
// in which case data positions stay the same.
|
|
@@ -6470,18 +6682,18 @@
|
|
|
6470
6682
|
}
|
|
6471
6683
|
|
|
6472
6684
|
var sortedPositions = dataPosition.slice();
|
|
6473
|
-
sortedPositions.sort(function (a, b) { return a-b; });
|
|
6685
|
+
sortedPositions.sort(function (a, b) { return a - b; });
|
|
6474
6686
|
|
|
6475
6687
|
// to remove holes, we need to 'shift down' the index's data array positions
|
|
6476
6688
|
// we need to adjust array positions -1 for each index data positions greater than removed positions
|
|
6477
6689
|
len = bi.values.length;
|
|
6478
|
-
for (idx=0; idx<len; idx++) {
|
|
6479
|
-
curr=bi.values[idx];
|
|
6480
|
-
shift=0;
|
|
6481
|
-
for(rmidx=0; rmidx<rmlen && curr > sortedPositions[rmidx]; rmidx++) {
|
|
6482
|
-
|
|
6690
|
+
for (idx = 0; idx < len; idx++) {
|
|
6691
|
+
curr = bi.values[idx];
|
|
6692
|
+
shift = 0;
|
|
6693
|
+
for (rmidx = 0; rmidx < rmlen && curr > sortedPositions[rmidx]; rmidx++) {
|
|
6694
|
+
shift++;
|
|
6483
6695
|
}
|
|
6484
|
-
bi.values[idx]-=shift;
|
|
6696
|
+
bi.values[idx] -= shift;
|
|
6485
6697
|
}
|
|
6486
6698
|
|
|
6487
6699
|
// batch processed, bail out
|
|
@@ -6565,11 +6777,11 @@
|
|
|
6565
6777
|
|
|
6566
6778
|
// if not in index and our value is less than the found one
|
|
6567
6779
|
if (Comparators.lt(val, Utils.getIn(rcd[index[lbound]], prop, usingDotNotation), false)) {
|
|
6568
|
-
return adaptive?lbound:lbound-1;
|
|
6780
|
+
return adaptive ? lbound : lbound - 1;
|
|
6569
6781
|
}
|
|
6570
6782
|
|
|
6571
6783
|
// not in index and our value is greater than the found one
|
|
6572
|
-
return adaptive?lbound+1:lbound;
|
|
6784
|
+
return adaptive ? lbound + 1 : lbound;
|
|
6573
6785
|
};
|
|
6574
6786
|
|
|
6575
6787
|
/**
|
|
@@ -6608,14 +6820,14 @@
|
|
|
6608
6820
|
return ubound;
|
|
6609
6821
|
}
|
|
6610
6822
|
|
|
6611
|
-
|
|
6823
|
+
// if not in index and our value is less than the found one
|
|
6612
6824
|
if (Comparators.gt(val, Utils.getIn(rcd[index[ubound]], prop, usingDotNotation), false)) {
|
|
6613
|
-
return ubound+1;
|
|
6825
|
+
return ubound + 1;
|
|
6614
6826
|
}
|
|
6615
6827
|
|
|
6616
6828
|
// either hole or first nonmatch
|
|
6617
|
-
if (Comparators.aeq(val, Utils.getIn(rcd[index[ubound-1]], prop, usingDotNotation))) {
|
|
6618
|
-
return ubound-1;
|
|
6829
|
+
if (Comparators.aeq(val, Utils.getIn(rcd[index[ubound - 1]], prop, usingDotNotation))) {
|
|
6830
|
+
return ubound - 1;
|
|
6619
6831
|
}
|
|
6620
6832
|
|
|
6621
6833
|
// hole, so ubound if nearest gt than the val we were looking for
|
|
@@ -6653,94 +6865,94 @@
|
|
|
6653
6865
|
|
|
6654
6866
|
// if value falls outside of our range return [0, -1] to designate no results
|
|
6655
6867
|
switch (op) {
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6667
|
-
|
|
6668
|
-
|
|
6669
|
-
|
|
6670
|
-
|
|
6671
|
-
|
|
6672
|
-
|
|
6673
|
-
|
|
6674
|
-
return [min, max];
|
|
6675
|
-
}
|
|
6676
|
-
break;
|
|
6677
|
-
case '$gte':
|
|
6678
|
-
// none are within range
|
|
6679
|
-
if (Comparators.gt(val, maxVal, false)) {
|
|
6680
|
-
return [0, -1];
|
|
6681
|
-
}
|
|
6682
|
-
// all are within range
|
|
6683
|
-
if (Comparators.gt(minVal, val, true)) {
|
|
6868
|
+
case '$eq':
|
|
6869
|
+
case '$aeq':
|
|
6870
|
+
if (Comparators.lt(val, minVal, false) || Comparators.gt(val, maxVal, false)) {
|
|
6871
|
+
return [0, -1];
|
|
6872
|
+
}
|
|
6873
|
+
break;
|
|
6874
|
+
case '$dteq':
|
|
6875
|
+
if (Comparators.lt(val, minVal, false) || Comparators.gt(val, maxVal, false)) {
|
|
6876
|
+
return [0, -1];
|
|
6877
|
+
}
|
|
6878
|
+
break;
|
|
6879
|
+
case '$gt':
|
|
6880
|
+
// none are within range
|
|
6881
|
+
if (Comparators.gt(val, maxVal, true)) {
|
|
6882
|
+
return [0, -1];
|
|
6883
|
+
}
|
|
6884
|
+
// all are within range
|
|
6885
|
+
if (Comparators.gt(minVal, val, false)) {
|
|
6684
6886
|
return [min, max];
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
|
|
6688
|
-
|
|
6689
|
-
|
|
6690
|
-
|
|
6691
|
-
|
|
6692
|
-
|
|
6693
|
-
|
|
6694
|
-
|
|
6695
|
-
|
|
6696
|
-
|
|
6697
|
-
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6711
|
-
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6715
|
-
|
|
6887
|
+
}
|
|
6888
|
+
break;
|
|
6889
|
+
case '$gte':
|
|
6890
|
+
// none are within range
|
|
6891
|
+
if (Comparators.gt(val, maxVal, false)) {
|
|
6892
|
+
return [0, -1];
|
|
6893
|
+
}
|
|
6894
|
+
// all are within range
|
|
6895
|
+
if (Comparators.gt(minVal, val, true)) {
|
|
6896
|
+
return [min, max];
|
|
6897
|
+
}
|
|
6898
|
+
break;
|
|
6899
|
+
case '$lt':
|
|
6900
|
+
// none are within range
|
|
6901
|
+
if (Comparators.lt(val, minVal, true)) {
|
|
6902
|
+
return [0, -1];
|
|
6903
|
+
}
|
|
6904
|
+
// all are within range
|
|
6905
|
+
if (Comparators.lt(maxVal, val, false)) {
|
|
6906
|
+
return [min, max];
|
|
6907
|
+
}
|
|
6908
|
+
break;
|
|
6909
|
+
case '$lte':
|
|
6910
|
+
// none are within range
|
|
6911
|
+
if (Comparators.lt(val, minVal, false)) {
|
|
6912
|
+
return [0, -1];
|
|
6913
|
+
}
|
|
6914
|
+
// all are within range
|
|
6915
|
+
if (Comparators.lt(maxVal, val, true)) {
|
|
6916
|
+
return [min, max];
|
|
6917
|
+
}
|
|
6918
|
+
break;
|
|
6919
|
+
case '$between':
|
|
6920
|
+
// none are within range (low range is greater)
|
|
6921
|
+
if (Comparators.gt(val[0], maxVal, false)) {
|
|
6922
|
+
return [0, -1];
|
|
6923
|
+
}
|
|
6924
|
+
// none are within range (high range lower)
|
|
6925
|
+
if (Comparators.lt(val[1], minVal, false)) {
|
|
6926
|
+
return [0, -1];
|
|
6927
|
+
}
|
|
6716
6928
|
|
|
6717
|
-
|
|
6718
|
-
|
|
6929
|
+
lbound = this.calculateRangeStart(prop, val[0], false, usingDotNotation);
|
|
6930
|
+
ubound = this.calculateRangeEnd(prop, val[1], usingDotNotation);
|
|
6719
6931
|
|
|
6720
|
-
|
|
6721
|
-
|
|
6932
|
+
if (lbound < 0) lbound++;
|
|
6933
|
+
if (ubound > max) ubound--;
|
|
6722
6934
|
|
|
6723
|
-
|
|
6724
|
-
|
|
6935
|
+
if (!Comparators.gt(Utils.getIn(rcd[index[lbound]], prop, usingDotNotation), val[0], true)) lbound++;
|
|
6936
|
+
if (!Comparators.lt(Utils.getIn(rcd[index[ubound]], prop, usingDotNotation), val[1], true)) ubound--;
|
|
6725
6937
|
|
|
6726
|
-
|
|
6938
|
+
if (ubound < lbound) return [0, -1];
|
|
6727
6939
|
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6940
|
+
return ([lbound, ubound]);
|
|
6941
|
+
case '$in':
|
|
6942
|
+
var idxset = [],
|
|
6943
|
+
segResult = [];
|
|
6944
|
+
// query each value '$eq' operator and merge the seqment results.
|
|
6945
|
+
for (var j = 0, len = val.length; j < len; j++) {
|
|
6734
6946
|
var seg = this.calculateRange('$eq', prop, val[j]);
|
|
6735
6947
|
|
|
6736
6948
|
for (var i = seg[0]; i <= seg[1]; i++) {
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6949
|
+
if (idxset[i] === undefined) {
|
|
6950
|
+
idxset[i] = true;
|
|
6951
|
+
segResult.push(i);
|
|
6952
|
+
}
|
|
6741
6953
|
}
|
|
6742
|
-
|
|
6743
|
-
|
|
6954
|
+
}
|
|
6955
|
+
return segResult;
|
|
6744
6956
|
}
|
|
6745
6957
|
|
|
6746
6958
|
// determine lbound where needed
|
|
@@ -6750,8 +6962,8 @@
|
|
|
6750
6962
|
case '$dteq':
|
|
6751
6963
|
case '$gte':
|
|
6752
6964
|
case '$lt':
|
|
6753
|
-
|
|
6754
|
-
|
|
6965
|
+
lbound = this.calculateRangeStart(prop, val, false, usingDotNotation);
|
|
6966
|
+
lval = Utils.getIn(rcd[index[lbound]], prop, usingDotNotation);
|
|
6755
6967
|
break;
|
|
6756
6968
|
default: break;
|
|
6757
6969
|
}
|
|
@@ -6771,50 +6983,50 @@
|
|
|
6771
6983
|
|
|
6772
6984
|
|
|
6773
6985
|
switch (op) {
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6986
|
+
case '$eq':
|
|
6987
|
+
case '$aeq':
|
|
6988
|
+
case '$dteq':
|
|
6989
|
+
// if hole (not found)
|
|
6990
|
+
if (!Comparators.aeq(lval, val)) {
|
|
6991
|
+
return [0, -1];
|
|
6992
|
+
}
|
|
6781
6993
|
|
|
6782
|
-
|
|
6994
|
+
return [lbound, ubound];
|
|
6783
6995
|
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
6996
|
+
case '$gt':
|
|
6997
|
+
// if hole (not found) ub position is already greater
|
|
6998
|
+
if (!Comparators.aeq(Utils.getIn(rcd[index[ubound]], prop, usingDotNotation), val)) {
|
|
6999
|
+
return [ubound, max];
|
|
7000
|
+
}
|
|
7001
|
+
// otherwise (found) so ubound is still equal, get next
|
|
7002
|
+
return [ubound + 1, max];
|
|
6791
7003
|
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
7004
|
+
case '$gte':
|
|
7005
|
+
// if hole (not found) lb position marks left outside of range
|
|
7006
|
+
if (!Comparators.aeq(Utils.getIn(rcd[index[lbound]], prop, usingDotNotation), val)) {
|
|
7007
|
+
return [lbound + 1, max];
|
|
7008
|
+
}
|
|
7009
|
+
// otherwise (found) so lb is first position where its equal
|
|
7010
|
+
return [lbound, max];
|
|
6799
7011
|
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
7012
|
+
case '$lt':
|
|
7013
|
+
// if hole (not found) position already is less than
|
|
7014
|
+
if (!Comparators.aeq(Utils.getIn(rcd[index[lbound]], prop, usingDotNotation), val)) {
|
|
7015
|
+
return [min, lbound];
|
|
7016
|
+
}
|
|
7017
|
+
// otherwise (found) so lb marks left inside of eq range, get previous
|
|
7018
|
+
return [min, lbound - 1];
|
|
6807
7019
|
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
7020
|
+
case '$lte':
|
|
7021
|
+
// if hole (not found) ub position marks right outside so get previous
|
|
7022
|
+
if (!Comparators.aeq(Utils.getIn(rcd[index[ubound]], prop, usingDotNotation), val)) {
|
|
7023
|
+
return [min, ubound - 1];
|
|
7024
|
+
}
|
|
7025
|
+
// otherwise (found) so ub is last position where its still equal
|
|
7026
|
+
return [min, ubound];
|
|
6815
7027
|
|
|
6816
|
-
|
|
6817
|
-
|
|
7028
|
+
default:
|
|
7029
|
+
return [0, rcd.length - 1];
|
|
6818
7030
|
}
|
|
6819
7031
|
};
|
|
6820
7032
|
|
|
@@ -6834,7 +7046,7 @@
|
|
|
6834
7046
|
};
|
|
6835
7047
|
}
|
|
6836
7048
|
|
|
6837
|
-
var result = this.
|
|
7049
|
+
var result = this.getUniqueIndex(field, true).get(value);
|
|
6838
7050
|
if (!this.cloneObjects) {
|
|
6839
7051
|
return result;
|
|
6840
7052
|
} else {
|
|
@@ -6852,7 +7064,7 @@
|
|
|
6852
7064
|
query = query || {};
|
|
6853
7065
|
|
|
6854
7066
|
// Instantiate Resultset and exec find op passing firstOnly = true param
|
|
6855
|
-
var result = this.chain().find(query,true).data();
|
|
7067
|
+
var result = this.chain().find(query, true).data();
|
|
6856
7068
|
|
|
6857
7069
|
if (Array.isArray(result) && result.length === 0) {
|
|
6858
7070
|
return null;
|
|
@@ -7335,7 +7547,7 @@
|
|
|
7335
7547
|
};
|
|
7336
7548
|
}
|
|
7337
7549
|
|
|
7338
|
-
function KeyValueStore() {}
|
|
7550
|
+
function KeyValueStore() { }
|
|
7339
7551
|
|
|
7340
7552
|
KeyValueStore.prototype = {
|
|
7341
7553
|
keys: [],
|
|
@@ -7365,8 +7577,8 @@
|
|
|
7365
7577
|
|
|
7366
7578
|
function UniqueIndex(uniqueField) {
|
|
7367
7579
|
this.field = uniqueField;
|
|
7368
|
-
this.keyMap =
|
|
7369
|
-
this.lokiMap =
|
|
7580
|
+
this.keyMap = Object.create(null);
|
|
7581
|
+
this.lokiMap = Object.create(null);
|
|
7370
7582
|
}
|
|
7371
7583
|
UniqueIndex.prototype.keyMap = {};
|
|
7372
7584
|
UniqueIndex.prototype.lokiMap = {};
|
|
@@ -7406,6 +7618,7 @@
|
|
|
7406
7618
|
UniqueIndex.prototype.remove = function (key) {
|
|
7407
7619
|
var obj = this.keyMap[key];
|
|
7408
7620
|
if (obj !== null && typeof obj !== 'undefined') {
|
|
7621
|
+
// avoid using `delete`
|
|
7409
7622
|
this.keyMap[key] = undefined;
|
|
7410
7623
|
this.lokiMap[obj.$loki] = undefined;
|
|
7411
7624
|
} else {
|
|
@@ -7413,12 +7626,12 @@
|
|
|
7413
7626
|
}
|
|
7414
7627
|
};
|
|
7415
7628
|
UniqueIndex.prototype.clear = function () {
|
|
7416
|
-
this.keyMap =
|
|
7417
|
-
this.lokiMap =
|
|
7629
|
+
this.keyMap = Object.create(null);
|
|
7630
|
+
this.lokiMap = Object.create(null);
|
|
7418
7631
|
};
|
|
7419
7632
|
|
|
7420
7633
|
function ExactIndex(exactField) {
|
|
7421
|
-
this.index =
|
|
7634
|
+
this.index = Object.create(null);
|
|
7422
7635
|
this.field = exactField;
|
|
7423
7636
|
}
|
|
7424
7637
|
|
|
@@ -7539,9 +7752,13 @@
|
|
|
7539
7752
|
}
|
|
7540
7753
|
};
|
|
7541
7754
|
|
|
7542
|
-
|
|
7755
|
+
Loki.deepFreeze = deepFreeze;
|
|
7756
|
+
Loki.freeze = freeze;
|
|
7757
|
+
Loki.unFreeze = unFreeze;
|
|
7543
7758
|
Loki.LokiOps = LokiOps;
|
|
7544
7759
|
Loki.Collection = Collection;
|
|
7760
|
+
Loki.DynamicView = DynamicView;
|
|
7761
|
+
Loki.Resultset = Resultset;
|
|
7545
7762
|
Loki.KeyValueStore = KeyValueStore;
|
|
7546
7763
|
Loki.LokiMemoryAdapter = LokiMemoryAdapter;
|
|
7547
7764
|
Loki.LokiPartitioningAdapter = LokiPartitioningAdapter;
|