@herdwatch/lokijs 1.5.8-dev.7 → 1.5.12-dev.1
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 +2 -2
- package/src/incremental-indexeddb-adapter.js +524 -179
- package/src/lokijs.js +790 -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,12 @@
|
|
|
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
|
-
|
|
2943
|
+
// for pause autosave
|
|
2944
|
+
if (!self.autosave) {
|
|
2945
|
+
return;
|
|
2946
|
+
}
|
|
2947
|
+
|
|
2948
|
+
if (self.autosaveDirty() && !self.ignoreAutosave) {
|
|
2856
2949
|
self.saveDatabase(callback);
|
|
2857
2950
|
}
|
|
2858
2951
|
}, delay);
|
|
@@ -3031,46 +3124,46 @@
|
|
|
3031
3124
|
step = transform[idx];
|
|
3032
3125
|
|
|
3033
3126
|
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
|
-
|
|
3127
|
+
case "find":
|
|
3128
|
+
rs.find(step.value);
|
|
3129
|
+
break;
|
|
3130
|
+
case "where":
|
|
3131
|
+
rs.where(step.value);
|
|
3132
|
+
break;
|
|
3133
|
+
case "simplesort":
|
|
3134
|
+
rs.simplesort(step.property, step.desc || step.options);
|
|
3135
|
+
break;
|
|
3136
|
+
case "compoundsort":
|
|
3137
|
+
rs.compoundsort(step.value);
|
|
3138
|
+
break;
|
|
3139
|
+
case "sort":
|
|
3140
|
+
rs.sort(step.value);
|
|
3141
|
+
break;
|
|
3142
|
+
case "limit":
|
|
3143
|
+
rs = rs.limit(step.value);
|
|
3144
|
+
break; // limit makes copy so update reference
|
|
3145
|
+
case "offset":
|
|
3146
|
+
rs = rs.offset(step.value);
|
|
3147
|
+
break; // offset makes copy so update reference
|
|
3148
|
+
case "map":
|
|
3149
|
+
rs = rs.map(step.value, step.dataOptions);
|
|
3150
|
+
break;
|
|
3151
|
+
case "eqJoin":
|
|
3152
|
+
rs = rs.eqJoin(step.joinData, step.leftJoinKey, step.rightJoinKey, step.mapFun, step.dataOptions);
|
|
3153
|
+
break;
|
|
3061
3154
|
// following cases break chain by returning array data so make any of these last in transform steps
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3155
|
+
case "mapReduce":
|
|
3156
|
+
rs = rs.mapReduce(step.mapFunction, step.reduceFunction);
|
|
3157
|
+
break;
|
|
3065
3158
|
// following cases update documents in current filtered resultset (use carefully)
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3073
|
-
|
|
3159
|
+
case "update":
|
|
3160
|
+
rs.update(step.value);
|
|
3161
|
+
break;
|
|
3162
|
+
case "remove":
|
|
3163
|
+
rs.remove();
|
|
3164
|
+
break;
|
|
3165
|
+
default:
|
|
3166
|
+
break;
|
|
3074
3167
|
}
|
|
3075
3168
|
}
|
|
3076
3169
|
|
|
@@ -3174,7 +3267,7 @@
|
|
|
3174
3267
|
if (!options.disableIndexIntersect && hasBinaryIndex) {
|
|
3175
3268
|
|
|
3176
3269
|
// calculate filter efficiency
|
|
3177
|
-
eff = dc/frl;
|
|
3270
|
+
eff = dc / frl;
|
|
3178
3271
|
|
|
3179
3272
|
// when javascript sort fallback is enabled, you generally need more than ~17% of total docs in resultset
|
|
3180
3273
|
// before array intersect is determined to be the faster algorithm, otherwise leave at 10% for loki sort.
|
|
@@ -3185,17 +3278,17 @@
|
|
|
3185
3278
|
// anything more than ratio of 10:1 (total documents/current results) should use old sort code path
|
|
3186
3279
|
// So we will only use array intersection if you have more than 10% of total docs in your current resultset.
|
|
3187
3280
|
if (eff <= targetEff || options.forceIndexIntersect) {
|
|
3188
|
-
var idx, fr=this.filteredrows;
|
|
3281
|
+
var idx, fr = this.filteredrows;
|
|
3189
3282
|
var io = {};
|
|
3190
3283
|
// set up hashobject for simple 'inclusion test' with existing (filtered) results
|
|
3191
|
-
for(idx=0; idx<frl; idx++) {
|
|
3284
|
+
for (idx = 0; idx < frl; idx++) {
|
|
3192
3285
|
io[fr[idx]] = true;
|
|
3193
3286
|
}
|
|
3194
3287
|
// grab full sorted binary index array
|
|
3195
3288
|
var pv = this.collection.binaryIndices[propname].values;
|
|
3196
3289
|
|
|
3197
3290
|
// filter by existing results
|
|
3198
|
-
this.filteredrows = pv.filter(function(n) { return io[n]; });
|
|
3291
|
+
this.filteredrows = pv.filter(function (n) { return io[n]; });
|
|
3199
3292
|
|
|
3200
3293
|
if (options.desc) {
|
|
3201
3294
|
this.filteredrows.reverse();
|
|
@@ -3210,7 +3303,7 @@
|
|
|
3210
3303
|
|
|
3211
3304
|
// if we have opted to use simplified javascript comparison function...
|
|
3212
3305
|
if (options.useJavascriptSorting) {
|
|
3213
|
-
return this.sort(function(obj1, obj2) {
|
|
3306
|
+
return this.sort(function (obj1, obj2) {
|
|
3214
3307
|
if (obj1[propname] === obj2[propname]) return 0;
|
|
3215
3308
|
if (obj1[propname] > obj2[propname]) return 1;
|
|
3216
3309
|
if (obj1[propname] < obj2[propname]) return -1;
|
|
@@ -3333,7 +3426,7 @@
|
|
|
3333
3426
|
Resultset.prototype.$or = Resultset.prototype.findOr;
|
|
3334
3427
|
|
|
3335
3428
|
// precompile recursively
|
|
3336
|
-
function precompileQuery
|
|
3429
|
+
function precompileQuery(operator, value) {
|
|
3337
3430
|
// for regex ops, precompile
|
|
3338
3431
|
if (operator === '$regex') {
|
|
3339
3432
|
if (Array.isArray(value)) {
|
|
@@ -3492,6 +3585,12 @@
|
|
|
3492
3585
|
index = this.collection.binaryIndices[property];
|
|
3493
3586
|
}
|
|
3494
3587
|
|
|
3588
|
+
// opportunistically speed up $in searches from O(n*m) to O(n*log m)
|
|
3589
|
+
if (!searchByIndex && operator === '$in' && Array.isArray(value) && typeof Set !== 'undefined') {
|
|
3590
|
+
value = new Set(value);
|
|
3591
|
+
operator = '$inSet';
|
|
3592
|
+
}
|
|
3593
|
+
|
|
3495
3594
|
// the comparison function
|
|
3496
3595
|
var fun = LokiOps[operator];
|
|
3497
3596
|
|
|
@@ -3507,7 +3606,7 @@
|
|
|
3507
3606
|
//
|
|
3508
3607
|
// For performance reasons, each case has its own if block to minimize in-loop calculations
|
|
3509
3608
|
|
|
3510
|
-
var filter, rowIdx = 0;
|
|
3609
|
+
var filter, rowIdx = 0, record;
|
|
3511
3610
|
|
|
3512
3611
|
// If the filteredrows[] is already initialized, use it
|
|
3513
3612
|
if (this.filterInitialized) {
|
|
@@ -3517,9 +3616,10 @@
|
|
|
3517
3616
|
// currently supporting dot notation for non-indexed conditions only
|
|
3518
3617
|
if (usingDotNotation) {
|
|
3519
3618
|
property = property.split('.');
|
|
3520
|
-
for(i=0; i<len; i++) {
|
|
3619
|
+
for (i = 0; i < len; i++) {
|
|
3521
3620
|
rowIdx = filter[i];
|
|
3522
|
-
|
|
3621
|
+
record = t[rowIdx];
|
|
3622
|
+
if (dotSubScan(record, property, fun, value, record)) {
|
|
3523
3623
|
result.push(rowIdx);
|
|
3524
3624
|
if (firstOnly) {
|
|
3525
3625
|
this.filteredrows = result;
|
|
@@ -3528,9 +3628,10 @@
|
|
|
3528
3628
|
}
|
|
3529
3629
|
}
|
|
3530
3630
|
} else {
|
|
3531
|
-
for(i=0; i<len; i++) {
|
|
3631
|
+
for (i = 0; i < len; i++) {
|
|
3532
3632
|
rowIdx = filter[i];
|
|
3533
|
-
|
|
3633
|
+
record = t[rowIdx];
|
|
3634
|
+
if (fun(record[property], value, record)) {
|
|
3534
3635
|
result.push(rowIdx);
|
|
3535
3636
|
if (firstOnly) {
|
|
3536
3637
|
this.filteredrows = result;
|
|
@@ -3548,8 +3649,9 @@
|
|
|
3548
3649
|
|
|
3549
3650
|
if (usingDotNotation) {
|
|
3550
3651
|
property = property.split('.');
|
|
3551
|
-
for(i=0; i<len; i++) {
|
|
3552
|
-
|
|
3652
|
+
for (i = 0; i < len; i++) {
|
|
3653
|
+
record = t[i];
|
|
3654
|
+
if (dotSubScan(record, property, fun, value, record)) {
|
|
3553
3655
|
result.push(i);
|
|
3554
3656
|
if (firstOnly) {
|
|
3555
3657
|
this.filteredrows = result;
|
|
@@ -3559,8 +3661,9 @@
|
|
|
3559
3661
|
}
|
|
3560
3662
|
}
|
|
3561
3663
|
} else {
|
|
3562
|
-
for(i=0; i<len; i++) {
|
|
3563
|
-
|
|
3664
|
+
for (i = 0; i < len; i++) {
|
|
3665
|
+
record = t[i];
|
|
3666
|
+
if (fun(record[property], value, record)) {
|
|
3564
3667
|
result.push(i);
|
|
3565
3668
|
if (firstOnly) {
|
|
3566
3669
|
this.filteredrows = result;
|
|
@@ -3588,12 +3691,12 @@
|
|
|
3588
3691
|
}
|
|
3589
3692
|
}
|
|
3590
3693
|
else {
|
|
3591
|
-
|
|
3592
|
-
|
|
3593
|
-
|
|
3594
|
-
|
|
3595
|
-
|
|
3596
|
-
|
|
3694
|
+
result.push(index.values[i]);
|
|
3695
|
+
if (firstOnly) {
|
|
3696
|
+
this.filteredrows = result;
|
|
3697
|
+
this.filterInitialized = true;
|
|
3698
|
+
return this;
|
|
3699
|
+
}
|
|
3597
3700
|
}
|
|
3598
3701
|
}
|
|
3599
3702
|
} else {
|
|
@@ -3716,7 +3819,8 @@
|
|
|
3716
3819
|
}
|
|
3717
3820
|
|
|
3718
3821
|
// if collection has delta changes active, then force clones and use 'parse-stringify' for effective change tracking of nested objects
|
|
3719
|
-
if
|
|
3822
|
+
// if collection is immutable freeze and unFreeze takes care of cloning
|
|
3823
|
+
if (!this.collection.disableDeltaChangesApi && this.collection.disableFreeze) {
|
|
3720
3824
|
options.forceClones = true;
|
|
3721
3825
|
options.forceCloneMethod = 'parse-stringify';
|
|
3722
3826
|
}
|
|
@@ -3728,7 +3832,6 @@
|
|
|
3728
3832
|
if (this.collection.cloneObjects || options.forceClones) {
|
|
3729
3833
|
len = data.length;
|
|
3730
3834
|
method = options.forceCloneMethod || this.collection.cloneMethod;
|
|
3731
|
-
|
|
3732
3835
|
for (i = 0; i < len; i++) {
|
|
3733
3836
|
obj = clone(data[i], method);
|
|
3734
3837
|
if (options.removeMeta) {
|
|
@@ -3798,7 +3901,7 @@
|
|
|
3798
3901
|
// pass in each document object currently in resultset to user supplied updateFunction
|
|
3799
3902
|
for (var idx = 0; idx < len; idx++) {
|
|
3800
3903
|
// if we have cloning option specified or are doing differential delta changes, clone object first
|
|
3801
|
-
if (this.collection.cloneObjects || !this.collection.disableDeltaChangesApi) {
|
|
3904
|
+
if (!this.disableFreeze || this.collection.cloneObjects || !this.collection.disableDeltaChangesApi) {
|
|
3802
3905
|
obj = clone(rcd[this.filteredrows[idx]], this.collection.cloneMethod);
|
|
3803
3906
|
updateFunction(obj);
|
|
3804
3907
|
this.collection.update(obj);
|
|
@@ -4059,6 +4162,9 @@
|
|
|
4059
4162
|
|
|
4060
4163
|
// keep ordered filter pipeline
|
|
4061
4164
|
this.filterPipeline = [];
|
|
4165
|
+
if (!this.collection.disableFreeze) {
|
|
4166
|
+
Object.freeze(this.filterPipeline);
|
|
4167
|
+
}
|
|
4062
4168
|
|
|
4063
4169
|
// sorting member variables
|
|
4064
4170
|
// we only support one active search, applied using applySort() or applySimpleSort()
|
|
@@ -4071,12 +4177,23 @@
|
|
|
4071
4177
|
// once we refactor transactions, i will tie in certain transactional events
|
|
4072
4178
|
|
|
4073
4179
|
this.events = {
|
|
4074
|
-
'rebuild': []
|
|
4180
|
+
'rebuild': [],
|
|
4181
|
+
'filter': [],
|
|
4182
|
+
'sort': []
|
|
4075
4183
|
};
|
|
4076
4184
|
}
|
|
4077
4185
|
|
|
4078
4186
|
DynamicView.prototype = new LokiEventEmitter();
|
|
4187
|
+
DynamicView.prototype.constructor = DynamicView;
|
|
4079
4188
|
|
|
4189
|
+
/**
|
|
4190
|
+
* getSort() - used to get the current sort
|
|
4191
|
+
*
|
|
4192
|
+
* @returns function (sortFunction) or array (sortCriteria) or object (sortCriteriaSimple)
|
|
4193
|
+
*/
|
|
4194
|
+
DynamicView.prototype.getSort = function () {
|
|
4195
|
+
return this.sortFunction || this.sortCriteria || this.sortCriteriaSimple;
|
|
4196
|
+
};
|
|
4080
4197
|
|
|
4081
4198
|
/**
|
|
4082
4199
|
* rematerialize() - internally used immediately after deserialization (loading)
|
|
@@ -4104,9 +4221,13 @@
|
|
|
4104
4221
|
this.sortDirty = true;
|
|
4105
4222
|
}
|
|
4106
4223
|
|
|
4224
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4107
4225
|
if (options.hasOwnProperty('removeWhereFilters')) {
|
|
4108
4226
|
// for each view see if it had any where filters applied... since they don't
|
|
4109
4227
|
// serialize those functions lets remove those invalid filters
|
|
4228
|
+
if (wasFrozen) {
|
|
4229
|
+
this.filterPipeline = this.filterPipeline.slice();
|
|
4230
|
+
}
|
|
4110
4231
|
fpl = this.filterPipeline.length;
|
|
4111
4232
|
fpi = fpl;
|
|
4112
4233
|
while (fpi--) {
|
|
@@ -4114,7 +4235,6 @@
|
|
|
4114
4235
|
if (fpi !== this.filterPipeline.length - 1) {
|
|
4115
4236
|
this.filterPipeline[fpi] = this.filterPipeline[this.filterPipeline.length - 1];
|
|
4116
4237
|
}
|
|
4117
|
-
|
|
4118
4238
|
this.filterPipeline.length--;
|
|
4119
4239
|
}
|
|
4120
4240
|
}
|
|
@@ -4127,7 +4247,10 @@
|
|
|
4127
4247
|
// now re-apply 'find' filterPipeline ops
|
|
4128
4248
|
fpl = ofp.length;
|
|
4129
4249
|
for (idx = 0; idx < fpl; idx++) {
|
|
4130
|
-
this.applyFind(ofp[idx].val);
|
|
4250
|
+
this.applyFind(ofp[idx].val, ofp[idx].uid);
|
|
4251
|
+
}
|
|
4252
|
+
if (wasFrozen) {
|
|
4253
|
+
Object.freeze(this.filterPipeline);
|
|
4131
4254
|
}
|
|
4132
4255
|
|
|
4133
4256
|
// during creation of unit tests, i will remove this forced refresh and leave lazy
|
|
@@ -4184,7 +4307,6 @@
|
|
|
4184
4307
|
*/
|
|
4185
4308
|
DynamicView.prototype.toJSON = function () {
|
|
4186
4309
|
var copy = new DynamicView(this.collection, this.name, this.options);
|
|
4187
|
-
|
|
4188
4310
|
copy.resultset = this.resultset;
|
|
4189
4311
|
copy.resultdata = []; // let's not save data (copy) to minimize size
|
|
4190
4312
|
copy.resultsdirty = true;
|
|
@@ -4217,8 +4339,13 @@
|
|
|
4217
4339
|
|
|
4218
4340
|
this.cachedresultset = null;
|
|
4219
4341
|
|
|
4342
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4343
|
+
var filterChanged = this.filterPipeline.length > 0;
|
|
4220
4344
|
// keep ordered filter pipeline
|
|
4221
4345
|
this.filterPipeline = [];
|
|
4346
|
+
if (wasFrozen) {
|
|
4347
|
+
Object.freeze(this.filterPipeline);
|
|
4348
|
+
}
|
|
4222
4349
|
|
|
4223
4350
|
// sorting member variables
|
|
4224
4351
|
// we only support one active search, applied using applySort() or applySimpleSort()
|
|
@@ -4230,6 +4357,10 @@
|
|
|
4230
4357
|
if (options.queueSortPhase === true) {
|
|
4231
4358
|
this.queueSortPhase();
|
|
4232
4359
|
}
|
|
4360
|
+
|
|
4361
|
+
if (filterChanged) {
|
|
4362
|
+
this.emit('filter');
|
|
4363
|
+
}
|
|
4233
4364
|
};
|
|
4234
4365
|
|
|
4235
4366
|
/**
|
|
@@ -4251,6 +4382,7 @@
|
|
|
4251
4382
|
this.sortCriteriaSimple = null;
|
|
4252
4383
|
|
|
4253
4384
|
this.queueSortPhase();
|
|
4385
|
+
this.emit('sort');
|
|
4254
4386
|
|
|
4255
4387
|
return this;
|
|
4256
4388
|
};
|
|
@@ -4271,10 +4403,14 @@
|
|
|
4271
4403
|
*/
|
|
4272
4404
|
DynamicView.prototype.applySimpleSort = function (propname, options) {
|
|
4273
4405
|
this.sortCriteriaSimple = { propname: propname, options: options || false };
|
|
4406
|
+
if (!this.collection.disableFreeze) {
|
|
4407
|
+
deepFreeze(this.sortCriteriaSimple);
|
|
4408
|
+
}
|
|
4274
4409
|
this.sortCriteria = null;
|
|
4275
4410
|
this.sortFunction = null;
|
|
4276
4411
|
|
|
4277
4412
|
this.queueSortPhase();
|
|
4413
|
+
this.emit('sort');
|
|
4278
4414
|
|
|
4279
4415
|
return this;
|
|
4280
4416
|
};
|
|
@@ -4295,11 +4431,14 @@
|
|
|
4295
4431
|
*/
|
|
4296
4432
|
DynamicView.prototype.applySortCriteria = function (criteria) {
|
|
4297
4433
|
this.sortCriteria = criteria;
|
|
4434
|
+
if (!this.collection.disableFreeze) {
|
|
4435
|
+
deepFreeze(this.sortCriteria);
|
|
4436
|
+
}
|
|
4298
4437
|
this.sortCriteriaSimple = null;
|
|
4299
4438
|
this.sortFunction = null;
|
|
4300
4439
|
|
|
4301
4440
|
this.queueSortPhase();
|
|
4302
|
-
|
|
4441
|
+
this.emit('sort');
|
|
4303
4442
|
return this;
|
|
4304
4443
|
};
|
|
4305
4444
|
|
|
@@ -4370,7 +4509,17 @@
|
|
|
4370
4509
|
* @param {object} filter - The filter object. Refer to applyFilter() for extra details.
|
|
4371
4510
|
*/
|
|
4372
4511
|
DynamicView.prototype._addFilter = function (filter) {
|
|
4512
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4513
|
+
if (wasFrozen) {
|
|
4514
|
+
this.filterPipeline = this.filterPipeline.slice();
|
|
4515
|
+
}
|
|
4516
|
+
if (!this.collection.disableFreeze) {
|
|
4517
|
+
deepFreeze(filter);
|
|
4518
|
+
}
|
|
4373
4519
|
this.filterPipeline.push(filter);
|
|
4520
|
+
if (wasFrozen) {
|
|
4521
|
+
Object.freeze(this.filterPipeline);
|
|
4522
|
+
}
|
|
4374
4523
|
this.resultset[filter.type](filter.val);
|
|
4375
4524
|
};
|
|
4376
4525
|
|
|
@@ -4389,18 +4538,22 @@
|
|
|
4389
4538
|
}
|
|
4390
4539
|
|
|
4391
4540
|
var filters = this.filterPipeline;
|
|
4541
|
+
var wasFrozen = Object.isFrozen(filters);
|
|
4392
4542
|
this.filterPipeline = [];
|
|
4393
4543
|
|
|
4394
4544
|
for (var idx = 0, len = filters.length; idx < len; idx += 1) {
|
|
4395
4545
|
this._addFilter(filters[idx]);
|
|
4396
4546
|
}
|
|
4547
|
+
if (wasFrozen) {
|
|
4548
|
+
Object.freeze(this.filterPipeline);
|
|
4549
|
+
}
|
|
4397
4550
|
|
|
4398
4551
|
if (this.sortFunction || this.sortCriteria || this.sortCriteriaSimple) {
|
|
4399
4552
|
this.queueSortPhase();
|
|
4400
4553
|
} else {
|
|
4401
4554
|
this.queueRebuildEvent();
|
|
4402
4555
|
}
|
|
4403
|
-
|
|
4556
|
+
this.emit('filter');
|
|
4404
4557
|
return this;
|
|
4405
4558
|
};
|
|
4406
4559
|
|
|
@@ -4415,7 +4568,15 @@
|
|
|
4415
4568
|
DynamicView.prototype.applyFilter = function (filter) {
|
|
4416
4569
|
var idx = this._indexOfFilterWithId(filter.uid);
|
|
4417
4570
|
if (idx >= 0) {
|
|
4571
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4572
|
+
if (wasFrozen) {
|
|
4573
|
+
this.filterPipeline = this.filterPipeline.slice();
|
|
4574
|
+
}
|
|
4418
4575
|
this.filterPipeline[idx] = filter;
|
|
4576
|
+
if (wasFrozen) {
|
|
4577
|
+
freeze(filter);
|
|
4578
|
+
Object.freeze(this.filterPipeline);
|
|
4579
|
+
}
|
|
4419
4580
|
return this.reapplyFilters();
|
|
4420
4581
|
}
|
|
4421
4582
|
|
|
@@ -4433,6 +4594,7 @@
|
|
|
4433
4594
|
this.queueRebuildEvent();
|
|
4434
4595
|
}
|
|
4435
4596
|
|
|
4597
|
+
this.emit('filter');
|
|
4436
4598
|
return this;
|
|
4437
4599
|
};
|
|
4438
4600
|
|
|
@@ -4482,8 +4644,14 @@
|
|
|
4482
4644
|
if (idx < 0) {
|
|
4483
4645
|
throw new Error("Dynamic view does not contain a filter with ID: " + uid);
|
|
4484
4646
|
}
|
|
4485
|
-
|
|
4647
|
+
var wasFrozen = Object.isFrozen(this.filterPipeline);
|
|
4648
|
+
if (wasFrozen) {
|
|
4649
|
+
this.filterPipeline = this.filterPipeline.slice();
|
|
4650
|
+
}
|
|
4486
4651
|
this.filterPipeline.splice(idx, 1);
|
|
4652
|
+
if (wasFrozen) {
|
|
4653
|
+
Object.freeze(this.filterPipeline);
|
|
4654
|
+
}
|
|
4487
4655
|
this.reapplyFilters();
|
|
4488
4656
|
return this;
|
|
4489
4657
|
};
|
|
@@ -4743,23 +4911,23 @@
|
|
|
4743
4911
|
|
|
4744
4912
|
rmlen = objIndex.length;
|
|
4745
4913
|
// create intersection object of data indices to remove
|
|
4746
|
-
for(rmidx=0;rmidx<rmlen; rmidx++) {
|
|
4914
|
+
for (rmidx = 0; rmidx < rmlen; rmidx++) {
|
|
4747
4915
|
rxo[objIndex[rmidx]] = true;
|
|
4748
4916
|
}
|
|
4749
4917
|
|
|
4750
4918
|
// pivot remove data indices into remove filteredrows indices and dump in hashobject
|
|
4751
|
-
for (idx=0; idx<frlen; idx++) {
|
|
4919
|
+
for (idx = 0; idx < frlen; idx++) {
|
|
4752
4920
|
if (rxo[fr[idx]]) fxo[idx] = true;
|
|
4753
4921
|
}
|
|
4754
4922
|
|
|
4755
4923
|
// if any of the removed items were in our filteredrows...
|
|
4756
4924
|
if (Object.keys(fxo).length > 0) {
|
|
4757
4925
|
// remove them from filtered rows
|
|
4758
|
-
this.resultset.filteredrows = this.resultset.filteredrows.filter(function(di, idx) { return !fxo[idx]; });
|
|
4926
|
+
this.resultset.filteredrows = this.resultset.filteredrows.filter(function (di, idx) { return !fxo[idx]; });
|
|
4759
4927
|
// if persistent...
|
|
4760
4928
|
if (this.options.persistent) {
|
|
4761
4929
|
// remove from resultdata
|
|
4762
|
-
this.resultdata = this.resultdata.filter(function(obj, idx) { return !fxo[idx]; });
|
|
4930
|
+
this.resultdata = this.resultdata.filter(function (obj, idx) { return !fxo[idx]; });
|
|
4763
4931
|
}
|
|
4764
4932
|
|
|
4765
4933
|
// and queue sorts
|
|
@@ -4771,7 +4939,7 @@
|
|
|
4771
4939
|
}
|
|
4772
4940
|
|
|
4773
4941
|
// 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]; }; };
|
|
4942
|
+
var filt = function (idx) { return function (di) { return di < drs.filteredrows[idx]; }; };
|
|
4775
4943
|
|
|
4776
4944
|
frlen = drs.filteredrows.length;
|
|
4777
4945
|
for (idx = 0; idx < frlen; idx++) {
|
|
@@ -4817,6 +4985,7 @@
|
|
|
4817
4985
|
* @param {boolean} [options.autoupdate=false] - use Object.observe to update objects automatically
|
|
4818
4986
|
* @param {boolean} [options.clone=false] - specify whether inserts and queries clone to/from user
|
|
4819
4987
|
* @param {boolean} [options.serializableIndices=true[]] - converts date values on binary indexed properties to epoch time
|
|
4988
|
+
* @param {boolean} [options.disableFreeze=true] - when false all docs are frozen
|
|
4820
4989
|
* @param {string} [options.cloneMethod='parse-stringify'] - 'parse-stringify', 'jquery-extend-deep', 'shallow', 'shallow-assign'
|
|
4821
4990
|
* @param {int=} options.ttl - age of document (in ms.) before document is considered aged/stale.
|
|
4822
4991
|
* @param {int=} options.ttlInterval - time interval for clearing out 'aged' documents; not set by default.
|
|
@@ -4828,7 +4997,7 @@
|
|
|
4828
4997
|
this.name = name;
|
|
4829
4998
|
// the data held by the collection
|
|
4830
4999
|
this.data = [];
|
|
4831
|
-
this.idIndex =
|
|
5000
|
+
this.idIndex = null; // position->$loki index (built lazily)
|
|
4832
5001
|
this.binaryIndices = {}; // user defined indexes
|
|
4833
5002
|
this.constraints = {
|
|
4834
5003
|
unique: {},
|
|
@@ -4836,7 +5005,7 @@
|
|
|
4836
5005
|
};
|
|
4837
5006
|
|
|
4838
5007
|
// 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
|
|
5008
|
+
// we will keep track of properties which have unique contraint applied here, and regenerate lazily
|
|
4840
5009
|
this.uniqueNames = [];
|
|
4841
5010
|
|
|
4842
5011
|
// transforms will be used to store frequently used query chains as a series of steps
|
|
@@ -4865,9 +5034,9 @@
|
|
|
4865
5034
|
if (!Array.isArray(options.unique)) {
|
|
4866
5035
|
options.unique = [options.unique];
|
|
4867
5036
|
}
|
|
5037
|
+
// save names; actual index is built lazily
|
|
4868
5038
|
options.unique.forEach(function (prop) {
|
|
4869
|
-
self.uniqueNames.push(prop);
|
|
4870
|
-
self.constraints.unique[prop] = new UniqueIndex(prop);
|
|
5039
|
+
self.uniqueNames.push(prop);
|
|
4871
5040
|
});
|
|
4872
5041
|
}
|
|
4873
5042
|
|
|
@@ -4913,6 +5082,9 @@
|
|
|
4913
5082
|
// same 'after' serialization as it was 'before'.
|
|
4914
5083
|
this.serializableIndices = options.hasOwnProperty('serializableIndices') ? options.serializableIndices : true;
|
|
4915
5084
|
|
|
5085
|
+
// option to deep freeze all documents
|
|
5086
|
+
this.disableFreeze = options.hasOwnProperty('disableFreeze') ? options.disableFreeze : true;
|
|
5087
|
+
|
|
4916
5088
|
//option to activate a cleaner daemon - clears "aged" documents at set intervals.
|
|
4917
5089
|
this.ttl = {
|
|
4918
5090
|
age: null,
|
|
@@ -4945,10 +5117,8 @@
|
|
|
4945
5117
|
// lightweight changes tracking (loki IDs only) for optimized db saving
|
|
4946
5118
|
this.dirtyIds = [];
|
|
4947
5119
|
|
|
4948
|
-
// initialize the id index
|
|
4949
|
-
this.ensureId();
|
|
4950
|
-
var indices = [];
|
|
4951
5120
|
// initialize optional user-supplied indices array ['age', 'lname', 'zip']
|
|
5121
|
+
var indices = [];
|
|
4952
5122
|
if (options && options.indices) {
|
|
4953
5123
|
if (Object.prototype.toString.call(options.indices) === '[object Array]') {
|
|
4954
5124
|
indices = options.indices;
|
|
@@ -4983,7 +5153,7 @@
|
|
|
4983
5153
|
return self.removeAutoUpdateObserver(object);
|
|
4984
5154
|
try {
|
|
4985
5155
|
self.update(object);
|
|
4986
|
-
} catch (err) {}
|
|
5156
|
+
} catch (err) { }
|
|
4987
5157
|
});
|
|
4988
5158
|
}
|
|
4989
5159
|
|
|
@@ -5003,7 +5173,7 @@
|
|
|
5003
5173
|
|
|
5004
5174
|
function getObjectDelta(oldObject, newObject) {
|
|
5005
5175
|
var propertyNames = newObject !== null && typeof newObject === 'object' ? Object.keys(newObject) : null;
|
|
5006
|
-
if (propertyNames && propertyNames.length && ['string', 'boolean', 'number'].indexOf(typeof(newObject)) < 0) {
|
|
5176
|
+
if (propertyNames && propertyNames.length && ['string', 'boolean', 'number'].indexOf(typeof (newObject)) < 0) {
|
|
5007
5177
|
var delta = {};
|
|
5008
5178
|
for (var i = 0; i < propertyNames.length; i++) {
|
|
5009
5179
|
var propertyName = propertyNames[i];
|
|
@@ -5062,11 +5232,12 @@
|
|
|
5062
5232
|
}
|
|
5063
5233
|
|
|
5064
5234
|
Collection.prototype = new LokiEventEmitter();
|
|
5235
|
+
Collection.prototype.contructor = Collection;
|
|
5065
5236
|
|
|
5066
5237
|
/*
|
|
5067
5238
|
* For ChangeAPI default to clone entire object, for delta changes create object with only differences (+ $loki and meta)
|
|
5068
5239
|
*/
|
|
5069
|
-
Collection.prototype.createChange = function(name, op, obj, old) {
|
|
5240
|
+
Collection.prototype.createChange = function (name, op, obj, old) {
|
|
5070
5241
|
this.changes.push({
|
|
5071
5242
|
name: name,
|
|
5072
5243
|
operation: op,
|
|
@@ -5074,7 +5245,7 @@
|
|
|
5074
5245
|
});
|
|
5075
5246
|
};
|
|
5076
5247
|
|
|
5077
|
-
Collection.prototype.insertMeta = function(obj) {
|
|
5248
|
+
Collection.prototype.insertMeta = function (obj) {
|
|
5078
5249
|
var len, idx;
|
|
5079
5250
|
|
|
5080
5251
|
if (this.disableMeta || !obj) {
|
|
@@ -5085,7 +5256,7 @@
|
|
|
5085
5256
|
if (Array.isArray(obj)) {
|
|
5086
5257
|
len = obj.length;
|
|
5087
5258
|
|
|
5088
|
-
for(idx=0; idx<len; idx++) {
|
|
5259
|
+
for (idx = 0; idx < len; idx++) {
|
|
5089
5260
|
if (!obj[idx].hasOwnProperty('meta')) {
|
|
5090
5261
|
obj[idx].meta = {};
|
|
5091
5262
|
}
|
|
@@ -5106,36 +5277,42 @@
|
|
|
5106
5277
|
obj.meta.revision = 0;
|
|
5107
5278
|
};
|
|
5108
5279
|
|
|
5109
|
-
Collection.prototype.updateMeta = function(obj) {
|
|
5280
|
+
Collection.prototype.updateMeta = function (obj) {
|
|
5110
5281
|
if (this.disableMeta || !obj) {
|
|
5111
|
-
return;
|
|
5282
|
+
return obj;
|
|
5283
|
+
}
|
|
5284
|
+
if (!this.disableFreeze) {
|
|
5285
|
+
obj = unFreeze(obj);
|
|
5286
|
+
obj.meta = unFreeze(obj.meta);
|
|
5112
5287
|
}
|
|
5113
5288
|
obj.meta.updated = (new Date()).getTime();
|
|
5114
5289
|
obj.meta.revision += 1;
|
|
5290
|
+
return obj;
|
|
5115
5291
|
};
|
|
5116
5292
|
|
|
5117
|
-
Collection.prototype.createInsertChange = function(obj) {
|
|
5293
|
+
Collection.prototype.createInsertChange = function (obj) {
|
|
5118
5294
|
this.createChange(this.name, 'I', obj);
|
|
5119
5295
|
};
|
|
5120
5296
|
|
|
5121
|
-
Collection.prototype.createUpdateChange = function(obj, old) {
|
|
5297
|
+
Collection.prototype.createUpdateChange = function (obj, old) {
|
|
5122
5298
|
this.createChange(this.name, 'U', obj, old);
|
|
5123
5299
|
};
|
|
5124
5300
|
|
|
5125
|
-
Collection.prototype.insertMetaWithChange = function(obj) {
|
|
5301
|
+
Collection.prototype.insertMetaWithChange = function (obj) {
|
|
5126
5302
|
this.insertMeta(obj);
|
|
5127
5303
|
this.createInsertChange(obj);
|
|
5128
5304
|
};
|
|
5129
5305
|
|
|
5130
|
-
Collection.prototype.updateMetaWithChange = function(obj, old) {
|
|
5131
|
-
this.updateMeta(obj);
|
|
5306
|
+
Collection.prototype.updateMetaWithChange = function (obj, old, objFrozen) {
|
|
5307
|
+
obj = this.updateMeta(obj, objFrozen);
|
|
5132
5308
|
this.createUpdateChange(obj, old);
|
|
5309
|
+
return obj;
|
|
5133
5310
|
};
|
|
5134
5311
|
|
|
5135
5312
|
Collection.prototype.lokiConsoleWrapper = {
|
|
5136
|
-
log: function () {},
|
|
5137
|
-
warn: function () {},
|
|
5138
|
-
error: function () {},
|
|
5313
|
+
log: function () { },
|
|
5314
|
+
warn: function () { },
|
|
5315
|
+
error: function () { },
|
|
5139
5316
|
};
|
|
5140
5317
|
|
|
5141
5318
|
Collection.prototype.addAutoUpdateObserver = function (object) {
|
|
@@ -5422,7 +5599,7 @@
|
|
|
5422
5599
|
options.randomSamplingFactor = 0.1;
|
|
5423
5600
|
}
|
|
5424
5601
|
|
|
5425
|
-
var valid=true, idx, iter, pos, len, biv;
|
|
5602
|
+
var valid = true, idx, iter, pos, len, biv;
|
|
5426
5603
|
|
|
5427
5604
|
// make sure we are passed a valid binary index name
|
|
5428
5605
|
if (!this.binaryIndices.hasOwnProperty(property)) {
|
|
@@ -5458,28 +5635,28 @@
|
|
|
5458
5635
|
if (options.randomSampling) {
|
|
5459
5636
|
// validate first and last
|
|
5460
5637
|
if (!LokiOps.$lte(Utils.getIn(this.data[biv[0]], property, usingDotNotation),
|
|
5461
|
-
|
|
5462
|
-
valid=false;
|
|
5638
|
+
Utils.getIn(this.data[biv[1]], property, usingDotNotation))) {
|
|
5639
|
+
valid = false;
|
|
5463
5640
|
}
|
|
5464
|
-
if (!LokiOps.$lte(Utils.getIn(this.data[biv[len-2]], property, usingDotNotation),
|
|
5465
|
-
|
|
5466
|
-
valid=false;
|
|
5641
|
+
if (!LokiOps.$lte(Utils.getIn(this.data[biv[len - 2]], property, usingDotNotation),
|
|
5642
|
+
Utils.getIn(this.data[biv[len - 1]], property, usingDotNotation))) {
|
|
5643
|
+
valid = false;
|
|
5467
5644
|
}
|
|
5468
5645
|
|
|
5469
5646
|
// if first and last positions are sorted correctly with their nearest neighbor,
|
|
5470
5647
|
// continue onto random sampling phase...
|
|
5471
5648
|
if (valid) {
|
|
5472
5649
|
// # random samplings = total count * sampling factor
|
|
5473
|
-
iter = Math.floor((len-1) * options.randomSamplingFactor);
|
|
5650
|
+
iter = Math.floor((len - 1) * options.randomSamplingFactor);
|
|
5474
5651
|
|
|
5475
5652
|
// for each random sampling, validate that the binary index is sequenced properly
|
|
5476
5653
|
// with next higher value.
|
|
5477
|
-
for(idx=0; idx<iter-1; idx++) {
|
|
5654
|
+
for (idx = 0; idx < iter - 1; idx++) {
|
|
5478
5655
|
// calculate random position
|
|
5479
|
-
pos = Math.floor(Math.random() * (len-1));
|
|
5656
|
+
pos = Math.floor(Math.random() * (len - 1));
|
|
5480
5657
|
if (!LokiOps.$lte(Utils.getIn(this.data[biv[pos]], property, usingDotNotation),
|
|
5481
|
-
|
|
5482
|
-
valid=false;
|
|
5658
|
+
Utils.getIn(this.data[biv[pos + 1]], property, usingDotNotation))) {
|
|
5659
|
+
valid = false;
|
|
5483
5660
|
break;
|
|
5484
5661
|
}
|
|
5485
5662
|
}
|
|
@@ -5487,10 +5664,10 @@
|
|
|
5487
5664
|
}
|
|
5488
5665
|
else {
|
|
5489
5666
|
// validate that the binary index is sequenced properly
|
|
5490
|
-
for(idx=0; idx<len-1; idx++) {
|
|
5667
|
+
for (idx = 0; idx < len - 1; idx++) {
|
|
5491
5668
|
if (!LokiOps.$lte(Utils.getIn(this.data[biv[idx]], property, usingDotNotation),
|
|
5492
|
-
|
|
5493
|
-
valid=false;
|
|
5669
|
+
Utils.getIn(this.data[biv[idx + 1]], property, usingDotNotation))) {
|
|
5670
|
+
valid = false;
|
|
5494
5671
|
break;
|
|
5495
5672
|
}
|
|
5496
5673
|
}
|
|
@@ -5516,6 +5693,19 @@
|
|
|
5516
5693
|
return result;
|
|
5517
5694
|
};
|
|
5518
5695
|
|
|
5696
|
+
/**
|
|
5697
|
+
* Returns a named unique index
|
|
5698
|
+
* @param {string} field - indexed field name
|
|
5699
|
+
* @param {boolean} force - if `true`, will rebuild index; otherwise, function may return null
|
|
5700
|
+
*/
|
|
5701
|
+
Collection.prototype.getUniqueIndex = function (field, force) {
|
|
5702
|
+
var index = this.constraints.unique[field];
|
|
5703
|
+
if (!index && force) {
|
|
5704
|
+
return this.ensureUniqueIndex(field);
|
|
5705
|
+
}
|
|
5706
|
+
return index;
|
|
5707
|
+
};
|
|
5708
|
+
|
|
5519
5709
|
Collection.prototype.ensureUniqueIndex = function (field) {
|
|
5520
5710
|
var index = this.constraints.unique[field];
|
|
5521
5711
|
if (!index) {
|
|
@@ -5585,13 +5775,17 @@
|
|
|
5585
5775
|
* Rebuild idIndex
|
|
5586
5776
|
*/
|
|
5587
5777
|
Collection.prototype.ensureId = function () {
|
|
5588
|
-
|
|
5778
|
+
if (this.idIndex) {
|
|
5779
|
+
return;
|
|
5780
|
+
}
|
|
5781
|
+
var data = this.data,
|
|
5589
5782
|
i = 0;
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
for (i; i < len; i
|
|
5593
|
-
|
|
5783
|
+
var len = data.length;
|
|
5784
|
+
var index = new Array(len);
|
|
5785
|
+
for (i; i < len; i++) {
|
|
5786
|
+
index[i] = data[i].$loki;
|
|
5594
5787
|
}
|
|
5788
|
+
this.idIndex = index;
|
|
5595
5789
|
};
|
|
5596
5790
|
|
|
5597
5791
|
/**
|
|
@@ -5634,7 +5828,7 @@
|
|
|
5634
5828
|
**/
|
|
5635
5829
|
Collection.prototype.removeDynamicView = function (name) {
|
|
5636
5830
|
this.DynamicViews =
|
|
5637
|
-
this.DynamicViews.filter(function(dv) { return dv.name !== name; });
|
|
5831
|
+
this.DynamicViews.filter(function (dv) { return dv.name !== name; });
|
|
5638
5832
|
};
|
|
5639
5833
|
|
|
5640
5834
|
/**
|
|
@@ -5676,7 +5870,7 @@
|
|
|
5676
5870
|
* @param {object} filterObject - 'mongo-like' query object
|
|
5677
5871
|
* @memberof Collection
|
|
5678
5872
|
*/
|
|
5679
|
-
Collection.prototype.findAndRemove = function(filterObject) {
|
|
5873
|
+
Collection.prototype.findAndRemove = function (filterObject) {
|
|
5680
5874
|
this.chain().find(filterObject).remove();
|
|
5681
5875
|
};
|
|
5682
5876
|
|
|
@@ -5764,12 +5958,19 @@
|
|
|
5764
5958
|
|
|
5765
5959
|
// if configured to clone, do so now... otherwise just use same obj reference
|
|
5766
5960
|
var obj = this.cloneObjects ? clone(doc, this.cloneMethod) : doc;
|
|
5961
|
+
if (!this.disableFreeze) {
|
|
5962
|
+
obj = unFreeze(obj);
|
|
5963
|
+
}
|
|
5767
5964
|
|
|
5768
|
-
if (!this.disableMeta
|
|
5769
|
-
obj.meta
|
|
5770
|
-
|
|
5771
|
-
|
|
5772
|
-
|
|
5965
|
+
if (!this.disableMeta) {
|
|
5966
|
+
if (typeof obj.meta === 'undefined') {
|
|
5967
|
+
obj.meta = {
|
|
5968
|
+
revision: 0,
|
|
5969
|
+
created: 0
|
|
5970
|
+
};
|
|
5971
|
+
} else if (!this.disableFreeze) {
|
|
5972
|
+
obj.meta = unFreeze(obj.meta);
|
|
5973
|
+
}
|
|
5773
5974
|
}
|
|
5774
5975
|
|
|
5775
5976
|
// both 'pre-insert' and 'insert' events are passed internal data reference even when cloning
|
|
@@ -5785,18 +5986,23 @@
|
|
|
5785
5986
|
// (moved from "insert" event listener to allow internal reference to be used)
|
|
5786
5987
|
if (this.disableChangesApi) {
|
|
5787
5988
|
this.insertMeta(obj);
|
|
5788
|
-
}
|
|
5789
|
-
else {
|
|
5989
|
+
} else {
|
|
5790
5990
|
this.insertMetaWithChange(obj);
|
|
5791
5991
|
}
|
|
5792
5992
|
|
|
5993
|
+
if (!this.disableFreeze) {
|
|
5994
|
+
deepFreeze(obj);
|
|
5995
|
+
}
|
|
5996
|
+
|
|
5793
5997
|
// if cloning is enabled, emit insert event with clone of new object
|
|
5794
5998
|
returnObj = this.cloneObjects ? clone(obj, this.cloneMethod) : obj;
|
|
5999
|
+
|
|
5795
6000
|
if (!bulkInsert) {
|
|
5796
6001
|
this.emit('insert', returnObj);
|
|
5797
6002
|
}
|
|
5798
6003
|
|
|
5799
6004
|
this.addAutoUpdateObserver(returnObj);
|
|
6005
|
+
|
|
5800
6006
|
return returnObj;
|
|
5801
6007
|
};
|
|
5802
6008
|
|
|
@@ -5812,43 +6018,31 @@
|
|
|
5812
6018
|
options = options || {};
|
|
5813
6019
|
|
|
5814
6020
|
this.data = [];
|
|
5815
|
-
this.idIndex =
|
|
6021
|
+
this.idIndex = null;
|
|
5816
6022
|
this.cachedIndex = null;
|
|
5817
6023
|
this.cachedBinaryIndex = null;
|
|
5818
6024
|
this.cachedData = null;
|
|
5819
6025
|
this.maxId = 0;
|
|
5820
6026
|
this.DynamicViews = [];
|
|
5821
6027
|
this.dirty = true;
|
|
6028
|
+
this.constraints = {
|
|
6029
|
+
unique: {},
|
|
6030
|
+
exact: {}
|
|
6031
|
+
};
|
|
5822
6032
|
|
|
5823
6033
|
// if removing indices entirely
|
|
5824
6034
|
if (options.removeIndices === true) {
|
|
5825
6035
|
this.binaryIndices = {};
|
|
5826
|
-
|
|
5827
|
-
this.constraints = {
|
|
5828
|
-
unique: {},
|
|
5829
|
-
exact: {}
|
|
5830
|
-
};
|
|
5831
6036
|
this.uniqueNames = [];
|
|
5832
6037
|
}
|
|
5833
6038
|
// clear indices but leave definitions in place
|
|
5834
6039
|
else {
|
|
5835
6040
|
// clear binary indices
|
|
5836
6041
|
var keys = Object.keys(this.binaryIndices);
|
|
5837
|
-
keys.forEach(function(biname) {
|
|
6042
|
+
keys.forEach(function (biname) {
|
|
5838
6043
|
self.binaryIndices[biname].dirty = false;
|
|
5839
6044
|
self.binaryIndices[biname].values = [];
|
|
5840
6045
|
});
|
|
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
6046
|
}
|
|
5853
6047
|
};
|
|
5854
6048
|
|
|
@@ -5873,7 +6067,7 @@
|
|
|
5873
6067
|
}
|
|
5874
6068
|
|
|
5875
6069
|
try {
|
|
5876
|
-
for (k=0; k < len; k += 1) {
|
|
6070
|
+
for (k = 0; k < len; k += 1) {
|
|
5877
6071
|
this.update(doc[k]);
|
|
5878
6072
|
}
|
|
5879
6073
|
}
|
|
@@ -5907,12 +6101,12 @@
|
|
|
5907
6101
|
position = arr[1]; // position in data array
|
|
5908
6102
|
|
|
5909
6103
|
// if configured to clone, do so now... otherwise just use same obj reference
|
|
5910
|
-
newInternal = this.cloneObjects || !this.disableDeltaChangesApi ? clone(doc, this.cloneMethod) : doc;
|
|
6104
|
+
newInternal = this.cloneObjects || (!this.disableDeltaChangesApi && this.disableFreeze) ? clone(doc, this.cloneMethod) : doc;
|
|
5911
6105
|
|
|
5912
6106
|
this.emit('pre-update', doc);
|
|
5913
6107
|
|
|
5914
|
-
|
|
5915
|
-
self.
|
|
6108
|
+
this.uniqueNames.forEach(function (key) {
|
|
6109
|
+
self.getUniqueIndex(key, true).update(oldInternal, newInternal);
|
|
5916
6110
|
});
|
|
5917
6111
|
|
|
5918
6112
|
// operate the update
|
|
@@ -5952,10 +6146,13 @@
|
|
|
5952
6146
|
|
|
5953
6147
|
// update meta and store changes if ChangesAPI is enabled
|
|
5954
6148
|
if (this.disableChangesApi) {
|
|
5955
|
-
this.updateMeta(newInternal
|
|
6149
|
+
newInternal = this.updateMeta(newInternal);
|
|
6150
|
+
} else {
|
|
6151
|
+
newInternal = this.updateMetaWithChange(newInternal, oldInternal);
|
|
5956
6152
|
}
|
|
5957
|
-
|
|
5958
|
-
|
|
6153
|
+
|
|
6154
|
+
if (!this.disableFreeze) {
|
|
6155
|
+
deepFreeze(newInternal);
|
|
5959
6156
|
}
|
|
5960
6157
|
|
|
5961
6158
|
var returnObj;
|
|
@@ -6004,23 +6201,23 @@
|
|
|
6004
6201
|
this.maxId = (this.data[this.data.length - 1].$loki + 1);
|
|
6005
6202
|
}
|
|
6006
6203
|
|
|
6007
|
-
|
|
6204
|
+
var newId = this.maxId;
|
|
6205
|
+
obj.$loki = newId;
|
|
6008
6206
|
|
|
6009
6207
|
if (!this.disableMeta) {
|
|
6010
6208
|
obj.meta.version = 0;
|
|
6011
6209
|
}
|
|
6012
6210
|
|
|
6013
|
-
var
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6211
|
+
for (var i = 0, len = this.uniqueNames.length; i < len; i ++) {
|
|
6212
|
+
this.getUniqueIndex(this.uniqueNames[i], true).set(obj);
|
|
6213
|
+
}
|
|
6214
|
+
|
|
6215
|
+
if (this.idIndex) {
|
|
6216
|
+
this.idIndex.push(newId);
|
|
6018
6217
|
}
|
|
6019
6218
|
|
|
6020
|
-
// add new obj id to idIndex
|
|
6021
|
-
this.idIndex.push(obj.$loki);
|
|
6022
6219
|
if (this.isIncremental) {
|
|
6023
|
-
this.dirtyIds.push(
|
|
6220
|
+
this.dirtyIds.push(newId);
|
|
6024
6221
|
}
|
|
6025
6222
|
|
|
6026
6223
|
// add the object
|
|
@@ -6031,14 +6228,14 @@
|
|
|
6031
6228
|
// now that we can efficiently determine the data[] position of newly added document,
|
|
6032
6229
|
// submit it for all registered DynamicViews to evaluate for inclusion/exclusion
|
|
6033
6230
|
var dvlen = this.DynamicViews.length;
|
|
6034
|
-
for (
|
|
6231
|
+
for (i = 0; i < dvlen; i++) {
|
|
6035
6232
|
this.DynamicViews[i].evaluateDocument(addedPos, true);
|
|
6036
6233
|
}
|
|
6037
6234
|
|
|
6038
6235
|
if (this.adaptiveBinaryIndices) {
|
|
6039
6236
|
// for each binary index defined in collection, immediately update rather than flag for lazy rebuild
|
|
6040
6237
|
var bIndices = this.binaryIndices;
|
|
6041
|
-
for (key in bIndices) {
|
|
6238
|
+
for (var key in bIndices) {
|
|
6042
6239
|
this.adaptiveBinaryIndexInsert(addedPos, key);
|
|
6043
6240
|
}
|
|
6044
6241
|
}
|
|
@@ -6065,7 +6262,7 @@
|
|
|
6065
6262
|
* @param {function} updateFunction - update function to run against filtered documents
|
|
6066
6263
|
* @memberof Collection
|
|
6067
6264
|
*/
|
|
6068
|
-
Collection.prototype.updateWhere = function(filterFunction, updateFunction) {
|
|
6265
|
+
Collection.prototype.updateWhere = function (filterFunction, updateFunction) {
|
|
6069
6266
|
var results = this.where(filterFunction),
|
|
6070
6267
|
i = 0,
|
|
6071
6268
|
obj;
|
|
@@ -6105,21 +6302,22 @@
|
|
|
6105
6302
|
* Internal method to remove a batch of documents from the collection.
|
|
6106
6303
|
* @param {number[]} positions - data/idIndex positions to remove
|
|
6107
6304
|
*/
|
|
6108
|
-
Collection.prototype.removeBatchByPositions = function(positions) {
|
|
6305
|
+
Collection.prototype.removeBatchByPositions = function (positions) {
|
|
6109
6306
|
var len = positions.length;
|
|
6110
6307
|
var xo = {};
|
|
6111
6308
|
var dlen, didx, idx;
|
|
6112
|
-
var bic=Object.keys(this.binaryIndices).length;
|
|
6113
|
-
var uic=Object.keys(this.constraints.unique).length;
|
|
6309
|
+
var bic = Object.keys(this.binaryIndices).length;
|
|
6310
|
+
var uic = Object.keys(this.constraints.unique).length;
|
|
6114
6311
|
var adaptiveOverride = this.adaptiveBinaryIndices && Object.keys(this.binaryIndices).length > 0;
|
|
6115
|
-
var doc, self=this;
|
|
6312
|
+
var doc, self = this;
|
|
6116
6313
|
|
|
6117
6314
|
try {
|
|
6118
6315
|
this.startTransaction();
|
|
6119
6316
|
|
|
6120
6317
|
// create hashobject for positional removal inclusion tests...
|
|
6121
6318
|
// all keys defined in this hashobject represent $loki ids of the documents to remove.
|
|
6122
|
-
|
|
6319
|
+
this.ensureId();
|
|
6320
|
+
for (idx = 0; idx < len; idx++) {
|
|
6123
6321
|
xo[this.idIndex[positions[idx]]] = true;
|
|
6124
6322
|
}
|
|
6125
6323
|
|
|
@@ -6148,11 +6346,14 @@
|
|
|
6148
6346
|
}
|
|
6149
6347
|
|
|
6150
6348
|
if (uic) {
|
|
6151
|
-
|
|
6152
|
-
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
self.
|
|
6349
|
+
this.uniqueNames.forEach(function (key) {
|
|
6350
|
+
var index = self.getUniqueIndex(key);
|
|
6351
|
+
if (index) {
|
|
6352
|
+
for (idx = 0; idx < len; idx++) {
|
|
6353
|
+
doc = self.data[positions[idx]];
|
|
6354
|
+
if (doc[key] !== null && doc[key] !== undefined) {
|
|
6355
|
+
index.remove(doc[key]);
|
|
6356
|
+
}
|
|
6156
6357
|
}
|
|
6157
6358
|
}
|
|
6158
6359
|
});
|
|
@@ -6163,14 +6364,14 @@
|
|
|
6163
6364
|
// since data not removed yet, in future we can emit single delete event with array...
|
|
6164
6365
|
// for now that might be breaking change to put in potential 1.6 or LokiDB (lokijs2) version
|
|
6165
6366
|
if (!this.disableChangesApi || this.events.delete.length > 1) {
|
|
6166
|
-
for(idx=0; idx < len; idx++) {
|
|
6367
|
+
for (idx = 0; idx < len; idx++) {
|
|
6167
6368
|
this.emit('delete', this.data[positions[idx]]);
|
|
6168
6369
|
}
|
|
6169
6370
|
}
|
|
6170
6371
|
|
|
6171
6372
|
// remove from data[] :
|
|
6172
6373
|
// filter collection data for items not in inclusion hashobject
|
|
6173
|
-
this.data = this.data.filter(function(obj) {
|
|
6374
|
+
this.data = this.data.filter(function (obj) {
|
|
6174
6375
|
return !xo[obj.$loki];
|
|
6175
6376
|
});
|
|
6176
6377
|
|
|
@@ -6182,8 +6383,8 @@
|
|
|
6182
6383
|
|
|
6183
6384
|
// remove from idIndex[] :
|
|
6184
6385
|
// filter idIndex for items not in inclusion hashobject
|
|
6185
|
-
this.idIndex = this.idIndex.filter(function(id) {
|
|
6186
|
-
|
|
6386
|
+
this.idIndex = this.idIndex.filter(function (id) {
|
|
6387
|
+
return !xo[id];
|
|
6187
6388
|
});
|
|
6188
6389
|
|
|
6189
6390
|
if (this.adaptiveBinaryIndices && adaptiveOverride) {
|
|
@@ -6212,21 +6413,21 @@
|
|
|
6212
6413
|
* Internal method called by remove()
|
|
6213
6414
|
* @param {object[]|number[]} batch - array of documents or $loki ids to remove
|
|
6214
6415
|
*/
|
|
6215
|
-
Collection.prototype.removeBatch = function(batch) {
|
|
6416
|
+
Collection.prototype.removeBatch = function (batch) {
|
|
6216
6417
|
var len = batch.length,
|
|
6217
|
-
dlen=this.data.length,
|
|
6418
|
+
dlen = this.data.length,
|
|
6218
6419
|
idx;
|
|
6219
6420
|
var xlt = {};
|
|
6220
6421
|
var posx = [];
|
|
6221
6422
|
|
|
6222
6423
|
// create lookup hashobject to translate $loki id to position
|
|
6223
|
-
for (idx=0; idx < dlen; idx++) {
|
|
6424
|
+
for (idx = 0; idx < dlen; idx++) {
|
|
6224
6425
|
xlt[this.data[idx].$loki] = idx;
|
|
6225
6426
|
}
|
|
6226
6427
|
|
|
6227
6428
|
// iterate the batch
|
|
6228
|
-
for (idx=0; idx < len; idx++) {
|
|
6229
|
-
if (typeof(batch[idx]) === 'object') {
|
|
6429
|
+
for (idx = 0; idx < len; idx++) {
|
|
6430
|
+
if (typeof (batch[idx]) === 'object') {
|
|
6230
6431
|
posx.push(xlt[batch[idx].$loki]);
|
|
6231
6432
|
}
|
|
6232
6433
|
else {
|
|
@@ -6243,6 +6444,8 @@
|
|
|
6243
6444
|
* @memberof Collection
|
|
6244
6445
|
*/
|
|
6245
6446
|
Collection.prototype.remove = function (doc) {
|
|
6447
|
+
var frozen;
|
|
6448
|
+
|
|
6246
6449
|
if (typeof doc === 'number') {
|
|
6247
6450
|
doc = this.get(doc);
|
|
6248
6451
|
}
|
|
@@ -6265,9 +6468,12 @@
|
|
|
6265
6468
|
// obj = arr[0],
|
|
6266
6469
|
position = arr[1];
|
|
6267
6470
|
var self = this;
|
|
6268
|
-
|
|
6471
|
+
this.uniqueNames.forEach(function (key) {
|
|
6269
6472
|
if (doc[key] !== null && typeof doc[key] !== 'undefined') {
|
|
6270
|
-
self.
|
|
6473
|
+
var index = self.getUniqueIndex(key);
|
|
6474
|
+
if (index) {
|
|
6475
|
+
index.remove(doc[key]);
|
|
6476
|
+
}
|
|
6271
6477
|
}
|
|
6272
6478
|
});
|
|
6273
6479
|
// now that we can efficiently determine the data[] position of newly added document,
|
|
@@ -6300,8 +6506,15 @@
|
|
|
6300
6506
|
this.commit();
|
|
6301
6507
|
this.dirty = true; // for autosave scenarios
|
|
6302
6508
|
this.emit('delete', arr[0]);
|
|
6509
|
+
|
|
6510
|
+
if (!this.disableFreeze) {
|
|
6511
|
+
doc = unFreeze(doc);
|
|
6512
|
+
}
|
|
6303
6513
|
delete doc.$loki;
|
|
6304
6514
|
delete doc.meta;
|
|
6515
|
+
if (!this.disableFreeze) {
|
|
6516
|
+
freeze(doc);
|
|
6517
|
+
}
|
|
6305
6518
|
return doc;
|
|
6306
6519
|
|
|
6307
6520
|
} catch (err) {
|
|
@@ -6325,6 +6538,10 @@
|
|
|
6325
6538
|
* @memberof Collection
|
|
6326
6539
|
*/
|
|
6327
6540
|
Collection.prototype.get = function (id, returnPosition) {
|
|
6541
|
+
if (!this.idIndex) {
|
|
6542
|
+
this.ensureId();
|
|
6543
|
+
}
|
|
6544
|
+
|
|
6328
6545
|
var retpos = returnPosition || false,
|
|
6329
6546
|
data = this.idIndex,
|
|
6330
6547
|
max = data.length - 1,
|
|
@@ -6364,7 +6581,7 @@
|
|
|
6364
6581
|
* @param {int} dataPosition : coll.data array index/position
|
|
6365
6582
|
* @param {string} binaryIndexName : index to search for dataPosition in
|
|
6366
6583
|
*/
|
|
6367
|
-
Collection.prototype.getBinaryIndexPosition = function(dataPosition, binaryIndexName) {
|
|
6584
|
+
Collection.prototype.getBinaryIndexPosition = function (dataPosition, binaryIndexName) {
|
|
6368
6585
|
var val = Utils.getIn(this.data[dataPosition], binaryIndexName, true);
|
|
6369
6586
|
var index = this.binaryIndices[binaryIndexName].values;
|
|
6370
6587
|
|
|
@@ -6383,7 +6600,7 @@
|
|
|
6383
6600
|
// narrow down the sub-segment of index values
|
|
6384
6601
|
// where the indexed property value exactly matches our
|
|
6385
6602
|
// value and then linear scan to find exact -index- position
|
|
6386
|
-
for(var idx = min; idx <= max; idx++) {
|
|
6603
|
+
for (var idx = min; idx <= max; idx++) {
|
|
6387
6604
|
if (index[idx] === dataPosition) return idx;
|
|
6388
6605
|
}
|
|
6389
6606
|
|
|
@@ -6396,7 +6613,7 @@
|
|
|
6396
6613
|
* @param {int} dataPosition : coll.data array index/position
|
|
6397
6614
|
* @param {string} binaryIndexName : index to search for dataPosition in
|
|
6398
6615
|
*/
|
|
6399
|
-
Collection.prototype.adaptiveBinaryIndexInsert = function(dataPosition, binaryIndexName) {
|
|
6616
|
+
Collection.prototype.adaptiveBinaryIndexInsert = function (dataPosition, binaryIndexName) {
|
|
6400
6617
|
var usingDotNotation = (binaryIndexName.indexOf('.') !== -1);
|
|
6401
6618
|
var index = this.binaryIndices[binaryIndexName].values;
|
|
6402
6619
|
var val = Utils.getIn(this.data[dataPosition], binaryIndexName, usingDotNotation);
|
|
@@ -6407,7 +6624,7 @@
|
|
|
6407
6624
|
val = Utils.getIn(this.data[dataPosition], binaryIndexName);
|
|
6408
6625
|
}
|
|
6409
6626
|
|
|
6410
|
-
var idxPos = (index.length === 0)?0:this.calculateRangeStart(binaryIndexName, val, true, usingDotNotation);
|
|
6627
|
+
var idxPos = (index.length === 0) ? 0 : this.calculateRangeStart(binaryIndexName, val, true, usingDotNotation);
|
|
6411
6628
|
|
|
6412
6629
|
// insert new data index into our binary index at the proper sorted location for relevant property calculated by idxPos.
|
|
6413
6630
|
// doing this after adjusting dataPositions so no clash with previous item at that position.
|
|
@@ -6419,14 +6636,14 @@
|
|
|
6419
6636
|
* @param {int} dataPosition : coll.data array index/position
|
|
6420
6637
|
* @param {string} binaryIndexName : index to search for dataPosition in
|
|
6421
6638
|
*/
|
|
6422
|
-
Collection.prototype.adaptiveBinaryIndexUpdate = function(dataPosition, binaryIndexName) {
|
|
6639
|
+
Collection.prototype.adaptiveBinaryIndexUpdate = function (dataPosition, binaryIndexName) {
|
|
6423
6640
|
// linear scan needed to find old position within index unless we optimize for clone scenarios later
|
|
6424
6641
|
// within (my) node 5.6.0, the following for() loop with strict compare is -much- faster than indexOf()
|
|
6425
6642
|
var idxPos,
|
|
6426
6643
|
index = this.binaryIndices[binaryIndexName].values,
|
|
6427
|
-
len=index.length;
|
|
6644
|
+
len = index.length;
|
|
6428
6645
|
|
|
6429
|
-
for(idxPos=0; idxPos < len; idxPos++) {
|
|
6646
|
+
for (idxPos = 0; idxPos < len; idxPos++) {
|
|
6430
6647
|
if (index[idxPos] === dataPosition) break;
|
|
6431
6648
|
}
|
|
6432
6649
|
|
|
@@ -6442,7 +6659,7 @@
|
|
|
6442
6659
|
* @param {number|number[]} dataPosition : coll.data array index/position
|
|
6443
6660
|
* @param {string} binaryIndexName : index to search for dataPosition in
|
|
6444
6661
|
*/
|
|
6445
|
-
Collection.prototype.adaptiveBinaryIndexRemove = function(dataPosition, binaryIndexName, removedFromIndexOnly) {
|
|
6662
|
+
Collection.prototype.adaptiveBinaryIndexRemove = function (dataPosition, binaryIndexName, removedFromIndexOnly) {
|
|
6446
6663
|
var bi = this.binaryIndices[binaryIndexName];
|
|
6447
6664
|
var len, idx, rmidx, rmlen, rxo = {};
|
|
6448
6665
|
var curr, shift, idxPos;
|
|
@@ -6456,12 +6673,12 @@
|
|
|
6456
6673
|
}
|
|
6457
6674
|
// we were passed an array (batch) of documents so use this 'batch optimized' algorithm
|
|
6458
6675
|
else {
|
|
6459
|
-
for(rmidx=0;rmidx<rmlen; rmidx++) {
|
|
6676
|
+
for (rmidx = 0; rmidx < rmlen; rmidx++) {
|
|
6460
6677
|
rxo[dataPosition[rmidx]] = true;
|
|
6461
6678
|
}
|
|
6462
6679
|
|
|
6463
6680
|
// remove document from index (with filter function)
|
|
6464
|
-
bi.values = bi.values.filter(function(di) { return !rxo[di]; });
|
|
6681
|
+
bi.values = bi.values.filter(function (di) { return !rxo[di]; });
|
|
6465
6682
|
|
|
6466
6683
|
// if we passed this optional flag parameter, we are calling from adaptiveBinaryIndexUpdate,
|
|
6467
6684
|
// in which case data positions stay the same.
|
|
@@ -6470,18 +6687,18 @@
|
|
|
6470
6687
|
}
|
|
6471
6688
|
|
|
6472
6689
|
var sortedPositions = dataPosition.slice();
|
|
6473
|
-
sortedPositions.sort(function (a, b) { return a-b; });
|
|
6690
|
+
sortedPositions.sort(function (a, b) { return a - b; });
|
|
6474
6691
|
|
|
6475
6692
|
// to remove holes, we need to 'shift down' the index's data array positions
|
|
6476
6693
|
// we need to adjust array positions -1 for each index data positions greater than removed positions
|
|
6477
6694
|
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
|
-
|
|
6695
|
+
for (idx = 0; idx < len; idx++) {
|
|
6696
|
+
curr = bi.values[idx];
|
|
6697
|
+
shift = 0;
|
|
6698
|
+
for (rmidx = 0; rmidx < rmlen && curr > sortedPositions[rmidx]; rmidx++) {
|
|
6699
|
+
shift++;
|
|
6483
6700
|
}
|
|
6484
|
-
bi.values[idx]-=shift;
|
|
6701
|
+
bi.values[idx] -= shift;
|
|
6485
6702
|
}
|
|
6486
6703
|
|
|
6487
6704
|
// batch processed, bail out
|
|
@@ -6565,11 +6782,11 @@
|
|
|
6565
6782
|
|
|
6566
6783
|
// if not in index and our value is less than the found one
|
|
6567
6784
|
if (Comparators.lt(val, Utils.getIn(rcd[index[lbound]], prop, usingDotNotation), false)) {
|
|
6568
|
-
return adaptive?lbound:lbound-1;
|
|
6785
|
+
return adaptive ? lbound : lbound - 1;
|
|
6569
6786
|
}
|
|
6570
6787
|
|
|
6571
6788
|
// not in index and our value is greater than the found one
|
|
6572
|
-
return adaptive?lbound+1:lbound;
|
|
6789
|
+
return adaptive ? lbound + 1 : lbound;
|
|
6573
6790
|
};
|
|
6574
6791
|
|
|
6575
6792
|
/**
|
|
@@ -6608,14 +6825,14 @@
|
|
|
6608
6825
|
return ubound;
|
|
6609
6826
|
}
|
|
6610
6827
|
|
|
6611
|
-
|
|
6828
|
+
// if not in index and our value is less than the found one
|
|
6612
6829
|
if (Comparators.gt(val, Utils.getIn(rcd[index[ubound]], prop, usingDotNotation), false)) {
|
|
6613
|
-
return ubound+1;
|
|
6830
|
+
return ubound + 1;
|
|
6614
6831
|
}
|
|
6615
6832
|
|
|
6616
6833
|
// either hole or first nonmatch
|
|
6617
|
-
if (Comparators.aeq(val, Utils.getIn(rcd[index[ubound-1]], prop, usingDotNotation))) {
|
|
6618
|
-
return ubound-1;
|
|
6834
|
+
if (Comparators.aeq(val, Utils.getIn(rcd[index[ubound - 1]], prop, usingDotNotation))) {
|
|
6835
|
+
return ubound - 1;
|
|
6619
6836
|
}
|
|
6620
6837
|
|
|
6621
6838
|
// hole, so ubound if nearest gt than the val we were looking for
|
|
@@ -6653,94 +6870,94 @@
|
|
|
6653
6870
|
|
|
6654
6871
|
// if value falls outside of our range return [0, -1] to designate no results
|
|
6655
6872
|
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)) {
|
|
6873
|
+
case '$eq':
|
|
6874
|
+
case '$aeq':
|
|
6875
|
+
if (Comparators.lt(val, minVal, false) || Comparators.gt(val, maxVal, false)) {
|
|
6876
|
+
return [0, -1];
|
|
6877
|
+
}
|
|
6878
|
+
break;
|
|
6879
|
+
case '$dteq':
|
|
6880
|
+
if (Comparators.lt(val, minVal, false) || Comparators.gt(val, maxVal, false)) {
|
|
6881
|
+
return [0, -1];
|
|
6882
|
+
}
|
|
6883
|
+
break;
|
|
6884
|
+
case '$gt':
|
|
6885
|
+
// none are within range
|
|
6886
|
+
if (Comparators.gt(val, maxVal, true)) {
|
|
6887
|
+
return [0, -1];
|
|
6888
|
+
}
|
|
6889
|
+
// all are within range
|
|
6890
|
+
if (Comparators.gt(minVal, val, false)) {
|
|
6684
6891
|
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
|
-
|
|
6892
|
+
}
|
|
6893
|
+
break;
|
|
6894
|
+
case '$gte':
|
|
6895
|
+
// none are within range
|
|
6896
|
+
if (Comparators.gt(val, maxVal, false)) {
|
|
6897
|
+
return [0, -1];
|
|
6898
|
+
}
|
|
6899
|
+
// all are within range
|
|
6900
|
+
if (Comparators.gt(minVal, val, true)) {
|
|
6901
|
+
return [min, max];
|
|
6902
|
+
}
|
|
6903
|
+
break;
|
|
6904
|
+
case '$lt':
|
|
6905
|
+
// none are within range
|
|
6906
|
+
if (Comparators.lt(val, minVal, true)) {
|
|
6907
|
+
return [0, -1];
|
|
6908
|
+
}
|
|
6909
|
+
// all are within range
|
|
6910
|
+
if (Comparators.lt(maxVal, val, false)) {
|
|
6911
|
+
return [min, max];
|
|
6912
|
+
}
|
|
6913
|
+
break;
|
|
6914
|
+
case '$lte':
|
|
6915
|
+
// none are within range
|
|
6916
|
+
if (Comparators.lt(val, minVal, false)) {
|
|
6917
|
+
return [0, -1];
|
|
6918
|
+
}
|
|
6919
|
+
// all are within range
|
|
6920
|
+
if (Comparators.lt(maxVal, val, true)) {
|
|
6921
|
+
return [min, max];
|
|
6922
|
+
}
|
|
6923
|
+
break;
|
|
6924
|
+
case '$between':
|
|
6925
|
+
// none are within range (low range is greater)
|
|
6926
|
+
if (Comparators.gt(val[0], maxVal, false)) {
|
|
6927
|
+
return [0, -1];
|
|
6928
|
+
}
|
|
6929
|
+
// none are within range (high range lower)
|
|
6930
|
+
if (Comparators.lt(val[1], minVal, false)) {
|
|
6931
|
+
return [0, -1];
|
|
6932
|
+
}
|
|
6716
6933
|
|
|
6717
|
-
|
|
6718
|
-
|
|
6934
|
+
lbound = this.calculateRangeStart(prop, val[0], false, usingDotNotation);
|
|
6935
|
+
ubound = this.calculateRangeEnd(prop, val[1], usingDotNotation);
|
|
6719
6936
|
|
|
6720
|
-
|
|
6721
|
-
|
|
6937
|
+
if (lbound < 0) lbound++;
|
|
6938
|
+
if (ubound > max) ubound--;
|
|
6722
6939
|
|
|
6723
|
-
|
|
6724
|
-
|
|
6940
|
+
if (!Comparators.gt(Utils.getIn(rcd[index[lbound]], prop, usingDotNotation), val[0], true)) lbound++;
|
|
6941
|
+
if (!Comparators.lt(Utils.getIn(rcd[index[ubound]], prop, usingDotNotation), val[1], true)) ubound--;
|
|
6725
6942
|
|
|
6726
|
-
|
|
6943
|
+
if (ubound < lbound) return [0, -1];
|
|
6727
6944
|
|
|
6728
|
-
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6945
|
+
return ([lbound, ubound]);
|
|
6946
|
+
case '$in':
|
|
6947
|
+
var idxset = [],
|
|
6948
|
+
segResult = [];
|
|
6949
|
+
// query each value '$eq' operator and merge the seqment results.
|
|
6950
|
+
for (var j = 0, len = val.length; j < len; j++) {
|
|
6734
6951
|
var seg = this.calculateRange('$eq', prop, val[j]);
|
|
6735
6952
|
|
|
6736
6953
|
for (var i = seg[0]; i <= seg[1]; i++) {
|
|
6737
|
-
|
|
6738
|
-
|
|
6739
|
-
|
|
6740
|
-
|
|
6954
|
+
if (idxset[i] === undefined) {
|
|
6955
|
+
idxset[i] = true;
|
|
6956
|
+
segResult.push(i);
|
|
6957
|
+
}
|
|
6741
6958
|
}
|
|
6742
|
-
|
|
6743
|
-
|
|
6959
|
+
}
|
|
6960
|
+
return segResult;
|
|
6744
6961
|
}
|
|
6745
6962
|
|
|
6746
6963
|
// determine lbound where needed
|
|
@@ -6750,8 +6967,8 @@
|
|
|
6750
6967
|
case '$dteq':
|
|
6751
6968
|
case '$gte':
|
|
6752
6969
|
case '$lt':
|
|
6753
|
-
|
|
6754
|
-
|
|
6970
|
+
lbound = this.calculateRangeStart(prop, val, false, usingDotNotation);
|
|
6971
|
+
lval = Utils.getIn(rcd[index[lbound]], prop, usingDotNotation);
|
|
6755
6972
|
break;
|
|
6756
6973
|
default: break;
|
|
6757
6974
|
}
|
|
@@ -6771,50 +6988,50 @@
|
|
|
6771
6988
|
|
|
6772
6989
|
|
|
6773
6990
|
switch (op) {
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
|
|
6991
|
+
case '$eq':
|
|
6992
|
+
case '$aeq':
|
|
6993
|
+
case '$dteq':
|
|
6994
|
+
// if hole (not found)
|
|
6995
|
+
if (!Comparators.aeq(lval, val)) {
|
|
6996
|
+
return [0, -1];
|
|
6997
|
+
}
|
|
6781
6998
|
|
|
6782
|
-
|
|
6999
|
+
return [lbound, ubound];
|
|
6783
7000
|
|
|
6784
|
-
|
|
6785
|
-
|
|
6786
|
-
|
|
6787
|
-
|
|
6788
|
-
|
|
6789
|
-
|
|
6790
|
-
|
|
7001
|
+
case '$gt':
|
|
7002
|
+
// if hole (not found) ub position is already greater
|
|
7003
|
+
if (!Comparators.aeq(Utils.getIn(rcd[index[ubound]], prop, usingDotNotation), val)) {
|
|
7004
|
+
return [ubound, max];
|
|
7005
|
+
}
|
|
7006
|
+
// otherwise (found) so ubound is still equal, get next
|
|
7007
|
+
return [ubound + 1, max];
|
|
6791
7008
|
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6795
|
-
|
|
6796
|
-
|
|
6797
|
-
|
|
6798
|
-
|
|
7009
|
+
case '$gte':
|
|
7010
|
+
// if hole (not found) lb position marks left outside of range
|
|
7011
|
+
if (!Comparators.aeq(Utils.getIn(rcd[index[lbound]], prop, usingDotNotation), val)) {
|
|
7012
|
+
return [lbound + 1, max];
|
|
7013
|
+
}
|
|
7014
|
+
// otherwise (found) so lb is first position where its equal
|
|
7015
|
+
return [lbound, max];
|
|
6799
7016
|
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
7017
|
+
case '$lt':
|
|
7018
|
+
// if hole (not found) position already is less than
|
|
7019
|
+
if (!Comparators.aeq(Utils.getIn(rcd[index[lbound]], prop, usingDotNotation), val)) {
|
|
7020
|
+
return [min, lbound];
|
|
7021
|
+
}
|
|
7022
|
+
// otherwise (found) so lb marks left inside of eq range, get previous
|
|
7023
|
+
return [min, lbound - 1];
|
|
6807
7024
|
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
6811
|
-
|
|
6812
|
-
|
|
6813
|
-
|
|
6814
|
-
|
|
7025
|
+
case '$lte':
|
|
7026
|
+
// if hole (not found) ub position marks right outside so get previous
|
|
7027
|
+
if (!Comparators.aeq(Utils.getIn(rcd[index[ubound]], prop, usingDotNotation), val)) {
|
|
7028
|
+
return [min, ubound - 1];
|
|
7029
|
+
}
|
|
7030
|
+
// otherwise (found) so ub is last position where its still equal
|
|
7031
|
+
return [min, ubound];
|
|
6815
7032
|
|
|
6816
|
-
|
|
6817
|
-
|
|
7033
|
+
default:
|
|
7034
|
+
return [0, rcd.length - 1];
|
|
6818
7035
|
}
|
|
6819
7036
|
};
|
|
6820
7037
|
|
|
@@ -6834,7 +7051,7 @@
|
|
|
6834
7051
|
};
|
|
6835
7052
|
}
|
|
6836
7053
|
|
|
6837
|
-
var result = this.
|
|
7054
|
+
var result = this.getUniqueIndex(field, true).get(value);
|
|
6838
7055
|
if (!this.cloneObjects) {
|
|
6839
7056
|
return result;
|
|
6840
7057
|
} else {
|
|
@@ -6852,7 +7069,7 @@
|
|
|
6852
7069
|
query = query || {};
|
|
6853
7070
|
|
|
6854
7071
|
// Instantiate Resultset and exec find op passing firstOnly = true param
|
|
6855
|
-
var result = this.chain().find(query,true).data();
|
|
7072
|
+
var result = this.chain().find(query, true).data();
|
|
6856
7073
|
|
|
6857
7074
|
if (Array.isArray(result) && result.length === 0) {
|
|
6858
7075
|
return null;
|
|
@@ -7335,7 +7552,7 @@
|
|
|
7335
7552
|
};
|
|
7336
7553
|
}
|
|
7337
7554
|
|
|
7338
|
-
function KeyValueStore() {}
|
|
7555
|
+
function KeyValueStore() { }
|
|
7339
7556
|
|
|
7340
7557
|
KeyValueStore.prototype = {
|
|
7341
7558
|
keys: [],
|
|
@@ -7365,8 +7582,8 @@
|
|
|
7365
7582
|
|
|
7366
7583
|
function UniqueIndex(uniqueField) {
|
|
7367
7584
|
this.field = uniqueField;
|
|
7368
|
-
this.keyMap =
|
|
7369
|
-
this.lokiMap =
|
|
7585
|
+
this.keyMap = Object.create(null);
|
|
7586
|
+
this.lokiMap = Object.create(null);
|
|
7370
7587
|
}
|
|
7371
7588
|
UniqueIndex.prototype.keyMap = {};
|
|
7372
7589
|
UniqueIndex.prototype.lokiMap = {};
|
|
@@ -7406,6 +7623,7 @@
|
|
|
7406
7623
|
UniqueIndex.prototype.remove = function (key) {
|
|
7407
7624
|
var obj = this.keyMap[key];
|
|
7408
7625
|
if (obj !== null && typeof obj !== 'undefined') {
|
|
7626
|
+
// avoid using `delete`
|
|
7409
7627
|
this.keyMap[key] = undefined;
|
|
7410
7628
|
this.lokiMap[obj.$loki] = undefined;
|
|
7411
7629
|
} else {
|
|
@@ -7413,12 +7631,12 @@
|
|
|
7413
7631
|
}
|
|
7414
7632
|
};
|
|
7415
7633
|
UniqueIndex.prototype.clear = function () {
|
|
7416
|
-
this.keyMap =
|
|
7417
|
-
this.lokiMap =
|
|
7634
|
+
this.keyMap = Object.create(null);
|
|
7635
|
+
this.lokiMap = Object.create(null);
|
|
7418
7636
|
};
|
|
7419
7637
|
|
|
7420
7638
|
function ExactIndex(exactField) {
|
|
7421
|
-
this.index =
|
|
7639
|
+
this.index = Object.create(null);
|
|
7422
7640
|
this.field = exactField;
|
|
7423
7641
|
}
|
|
7424
7642
|
|
|
@@ -7539,9 +7757,13 @@
|
|
|
7539
7757
|
}
|
|
7540
7758
|
};
|
|
7541
7759
|
|
|
7542
|
-
|
|
7760
|
+
Loki.deepFreeze = deepFreeze;
|
|
7761
|
+
Loki.freeze = freeze;
|
|
7762
|
+
Loki.unFreeze = unFreeze;
|
|
7543
7763
|
Loki.LokiOps = LokiOps;
|
|
7544
7764
|
Loki.Collection = Collection;
|
|
7765
|
+
Loki.DynamicView = DynamicView;
|
|
7766
|
+
Loki.Resultset = Resultset;
|
|
7545
7767
|
Loki.KeyValueStore = KeyValueStore;
|
|
7546
7768
|
Loki.LokiMemoryAdapter = LokiMemoryAdapter;
|
|
7547
7769
|
Loki.LokiPartitioningAdapter = LokiPartitioningAdapter;
|