1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 | (function (root, factory) {
|
8 | if (typeof define === 'function' && define.amd) {
|
9 |
|
10 | define([], factory);
|
11 | } else if (typeof exports === 'object') {
|
12 |
|
13 | module.exports = factory();
|
14 | } else {
|
15 |
|
16 | root.loki = factory();
|
17 | }
|
18 | }(this, function () {
|
19 |
|
20 | return (function () {
|
21 | 'use strict';
|
22 |
|
23 | var hasOwnProperty = Object.prototype.hasOwnProperty;
|
24 |
|
25 | var Utils = {
|
26 | copyProperties: function (src, dest) {
|
27 | var prop;
|
28 | for (prop in src) {
|
29 | dest[prop] = src[prop];
|
30 | }
|
31 | },
|
32 |
|
33 | resolveTransformObject: function (subObj, params, depth) {
|
34 | var prop,
|
35 | pname;
|
36 |
|
37 | if (typeof depth !== 'number') {
|
38 | depth = 0;
|
39 | }
|
40 |
|
41 | if (++depth >= 10) return subObj;
|
42 |
|
43 | for (prop in subObj) {
|
44 | if (typeof subObj[prop] === 'string' && subObj[prop].indexOf("[%lktxp]") === 0) {
|
45 | pname = subObj[prop].substring(8);
|
46 | if (params.hasOwnProperty(pname)) {
|
47 | subObj[prop] = params[pname];
|
48 | }
|
49 | } else if (typeof subObj[prop] === "object") {
|
50 | subObj[prop] = Utils.resolveTransformObject(subObj[prop], params, depth);
|
51 | }
|
52 | }
|
53 |
|
54 | return subObj;
|
55 | },
|
56 |
|
57 | resolveTransformParams: function (transform, params) {
|
58 | var idx,
|
59 | clonedStep,
|
60 | resolvedTransform = [];
|
61 |
|
62 | if (typeof params === 'undefined') return transform;
|
63 |
|
64 |
|
65 | for (idx = 0; idx < transform.length; idx++) {
|
66 |
|
67 | clonedStep = JSON.parse(JSON.stringify(transform[idx]));
|
68 | resolvedTransform.push(Utils.resolveTransformObject(clonedStep, params));
|
69 | }
|
70 |
|
71 | return resolvedTransform;
|
72 | }
|
73 | };
|
74 |
|
75 | |
76 |
|
77 |
|
78 |
|
79 |
|
80 | function ltHelper(prop1, prop2, equal) {
|
81 | var cv1, cv2;
|
82 |
|
83 |
|
84 | if (!prop1 || !prop2 || prop1 === true || prop2 === true) {
|
85 | if ((prop1 === true || prop1 === false) && (prop2 === true || prop2 === false)) {
|
86 | if (equal) {
|
87 | return prop1 === prop2;
|
88 | } else {
|
89 | if (prop1) {
|
90 | return false;
|
91 | } else {
|
92 | return prop2;
|
93 | }
|
94 | }
|
95 | }
|
96 |
|
97 | if (prop2 === undefined || prop2 === null || prop1 === true || prop2 === false) {
|
98 | return equal;
|
99 | }
|
100 | if (prop1 === undefined || prop1 === null || prop1 === false || prop2 === true) {
|
101 | return true;
|
102 | }
|
103 | }
|
104 |
|
105 | if (prop1 === prop2) {
|
106 | return equal;
|
107 | }
|
108 |
|
109 | if (prop1 < prop2) {
|
110 | return true;
|
111 | }
|
112 |
|
113 | if (prop1 > prop2) {
|
114 | return false;
|
115 | }
|
116 |
|
117 |
|
118 | cv1 = prop1.toString();
|
119 | cv2 = prop2.toString();
|
120 |
|
121 | if (cv1 == cv2) {
|
122 | return equal;
|
123 | }
|
124 |
|
125 | if (cv1 < cv2) {
|
126 | return true;
|
127 | }
|
128 |
|
129 | return false;
|
130 | }
|
131 |
|
132 | function gtHelper(prop1, prop2, equal) {
|
133 | var cv1, cv2;
|
134 |
|
135 |
|
136 | if (!prop1 || !prop2 || prop1 === true || prop2 === true) {
|
137 | if ((prop1 === true || prop1 === false) && (prop2 === true || prop2 === false)) {
|
138 | if (equal) {
|
139 | return prop1 === prop2;
|
140 | } else {
|
141 | if (prop1) {
|
142 | return !prop2;
|
143 | } else {
|
144 | return false;
|
145 | }
|
146 | }
|
147 | }
|
148 |
|
149 | if (prop1 === undefined || prop1 === null || prop1 === false || prop2 === true) {
|
150 | return equal;
|
151 | }
|
152 | if (prop2 === undefined || prop2 === null || prop1 === true || prop2 === false) {
|
153 | return true;
|
154 | }
|
155 | }
|
156 |
|
157 | if (prop1 === prop2) {
|
158 | return equal;
|
159 | }
|
160 |
|
161 | if (prop1 > prop2) {
|
162 | return true;
|
163 | }
|
164 |
|
165 | if (prop1 < prop2) {
|
166 | return false;
|
167 | }
|
168 |
|
169 |
|
170 | cv1 = prop1.toString();
|
171 | cv2 = prop2.toString();
|
172 |
|
173 | if (cv1 == cv2) {
|
174 | return equal;
|
175 | }
|
176 |
|
177 | if (cv1 > cv2) {
|
178 | return true;
|
179 | }
|
180 |
|
181 | return false;
|
182 | }
|
183 |
|
184 | function sortHelper(prop1, prop2, desc) {
|
185 | if (prop1 === prop2) {
|
186 | return 0;
|
187 | }
|
188 |
|
189 | if (ltHelper(prop1, prop2, false)) {
|
190 | return (desc) ? (1) : (-1);
|
191 | }
|
192 |
|
193 | if (gtHelper(prop1, prop2, false)) {
|
194 | return (desc) ? (-1) : (1);
|
195 | }
|
196 |
|
197 |
|
198 | return 0;
|
199 | }
|
200 |
|
201 | |
202 |
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 | function compoundeval(properties, obj1, obj2) {
|
210 | var res = 0;
|
211 | var prop, field;
|
212 | for (var i = 0, len = properties.length; i < len; i++) {
|
213 | prop = properties[i];
|
214 | field = prop[0];
|
215 | res = sortHelper(obj1[field], obj2[field], prop[1]);
|
216 | if (res !== 0) {
|
217 | return res;
|
218 | }
|
219 | }
|
220 | return 0;
|
221 | }
|
222 |
|
223 | |
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 | function dotSubScan(root, paths, fun, value, poffset) {
|
233 | var pathOffset = poffset || 0;
|
234 | var path = paths[pathOffset];
|
235 | if (root === undefined || root === null || !hasOwnProperty.call(root, path)) {
|
236 | return false;
|
237 | }
|
238 |
|
239 | var valueFound = false;
|
240 | var element = root[path];
|
241 | if (pathOffset + 1 >= paths.length) {
|
242 |
|
243 |
|
244 | valueFound = fun(element, value);
|
245 | } else if (Array.isArray(element)) {
|
246 | for (var index = 0, len = element.length; index < len; index += 1) {
|
247 | valueFound = dotSubScan(element[index], paths, fun, value, pathOffset + 1);
|
248 | if (valueFound === true) {
|
249 | break;
|
250 | }
|
251 | }
|
252 | } else {
|
253 | valueFound = dotSubScan(element, paths, fun, value, pathOffset + 1);
|
254 | }
|
255 |
|
256 | return valueFound;
|
257 | }
|
258 |
|
259 | function containsCheckFn(a) {
|
260 | if (typeof a === 'string' || Array.isArray(a)) {
|
261 | return function (b) {
|
262 | return a.indexOf(b) !== -1;
|
263 | };
|
264 | } else if (typeof a === 'object' && a !== null) {
|
265 | return function (b) {
|
266 | return hasOwnProperty.call(a, b);
|
267 | };
|
268 | }
|
269 | return null;
|
270 | }
|
271 |
|
272 | function doQueryOp(val, op) {
|
273 | for (var p in op) {
|
274 | if (hasOwnProperty.call(op, p)) {
|
275 | return LokiOps[p](val, op[p]);
|
276 | }
|
277 | }
|
278 | return false;
|
279 | }
|
280 |
|
281 | var LokiOps = {
|
282 |
|
283 |
|
284 |
|
285 | $eq: function (a, b) {
|
286 | return a === b;
|
287 | },
|
288 |
|
289 |
|
290 | $aeq: function (a, b) {
|
291 | return a == b;
|
292 | },
|
293 |
|
294 | $ne: function (a, b) {
|
295 |
|
296 | if (b !== b) {
|
297 |
|
298 | return (a === a);
|
299 | }
|
300 |
|
301 | return a !== b;
|
302 | },
|
303 |
|
304 | $dteq: function (a, b) {
|
305 | if (ltHelper(a, b, false)) {
|
306 | return false;
|
307 | }
|
308 | return !gtHelper(a, b, false);
|
309 | },
|
310 |
|
311 | $gt: function (a, b) {
|
312 | return gtHelper(a, b, false);
|
313 | },
|
314 |
|
315 | $gte: function (a, b) {
|
316 | return gtHelper(a, b, true);
|
317 | },
|
318 |
|
319 | $lt: function (a, b) {
|
320 | return ltHelper(a, b, false);
|
321 | },
|
322 |
|
323 | $lte: function (a, b) {
|
324 | return ltHelper(a, b, true);
|
325 | },
|
326 |
|
327 |
|
328 | $between: function (a, vals) {
|
329 | if (a === undefined || a === null) return false;
|
330 | return (gtHelper(a, vals[0], true) && ltHelper(a, vals[1], true));
|
331 | },
|
332 |
|
333 | $in: function (a, b) {
|
334 | return b.indexOf(a) !== -1;
|
335 | },
|
336 |
|
337 | $nin: function (a, b) {
|
338 | return b.indexOf(a) === -1;
|
339 | },
|
340 |
|
341 | $keyin: function (a, b) {
|
342 | return a in b;
|
343 | },
|
344 |
|
345 | $nkeyin: function (a, b) {
|
346 | return !(a in b);
|
347 | },
|
348 |
|
349 | $definedin: function (a, b) {
|
350 | return b[a] !== undefined;
|
351 | },
|
352 |
|
353 | $undefinedin: function (a, b) {
|
354 | return b[a] === undefined;
|
355 | },
|
356 |
|
357 | $regex: function (a, b) {
|
358 | return b.test(a);
|
359 | },
|
360 |
|
361 | $containsString: function (a, b) {
|
362 | return (typeof a === 'string') && (a.indexOf(b) !== -1);
|
363 | },
|
364 |
|
365 | $containsNone: function (a, b) {
|
366 | return !LokiOps.$containsAny(a, b);
|
367 | },
|
368 |
|
369 | $containsAny: function (a, b) {
|
370 | var checkFn = containsCheckFn(a);
|
371 | if (checkFn !== null) {
|
372 | return (Array.isArray(b)) ? (b.some(checkFn)) : (checkFn(b));
|
373 | }
|
374 | return false;
|
375 | },
|
376 |
|
377 | $contains: function (a, b) {
|
378 | var checkFn = containsCheckFn(a);
|
379 | if (checkFn !== null) {
|
380 | return (Array.isArray(b)) ? (b.every(checkFn)) : (checkFn(b));
|
381 | }
|
382 | return false;
|
383 | },
|
384 |
|
385 | $type: function (a, b) {
|
386 | var type = typeof a;
|
387 | if (type === 'object') {
|
388 | if (Array.isArray(a)) {
|
389 | type = 'array';
|
390 | } else if (a instanceof Date) {
|
391 | type = 'date';
|
392 | }
|
393 | }
|
394 | return (typeof b !== 'object') ? (type === b) : doQueryOp(type, b);
|
395 | },
|
396 |
|
397 | $size: function (a, b) {
|
398 | if (Array.isArray(a)) {
|
399 | return (typeof b !== 'object') ? (a.length === b) : doQueryOp(a.length, b);
|
400 | }
|
401 | return false;
|
402 | },
|
403 |
|
404 | $len: function (a, b) {
|
405 | if (typeof a === 'string') {
|
406 | return (typeof b !== 'object') ? (a.length === b) : doQueryOp(a.length, b);
|
407 | }
|
408 | return false;
|
409 | },
|
410 |
|
411 | $where: function (a, b) {
|
412 | return b(a) === true;
|
413 | },
|
414 |
|
415 |
|
416 |
|
417 |
|
418 |
|
419 | $not: function (a, b) {
|
420 | return !doQueryOp(a, b);
|
421 | },
|
422 |
|
423 | $and: function (a, b) {
|
424 | for (var idx = 0, len = b.length; idx < len; idx += 1) {
|
425 | if (!doQueryOp(a, b[idx])) {
|
426 | return false;
|
427 | }
|
428 | }
|
429 | return true;
|
430 | },
|
431 |
|
432 | $or: function (a, b) {
|
433 | for (var idx = 0, len = b.length; idx < len; idx += 1) {
|
434 | if (doQueryOp(a, b[idx])) {
|
435 | return true;
|
436 | }
|
437 | }
|
438 | return false;
|
439 | }
|
440 | };
|
441 |
|
442 |
|
443 | var indexedOpsList = ['$eq', '$aeq', '$dteq', '$gt', '$gte', '$lt', '$lte', '$in', '$between'];
|
444 |
|
445 | function clone(data, method) {
|
446 | var cloneMethod = method || 'parse-stringify',
|
447 | cloned;
|
448 |
|
449 | switch (cloneMethod) {
|
450 | case "parse-stringify":
|
451 | cloned = JSON.parse(JSON.stringify(data));
|
452 | break;
|
453 | case "jquery-extend-deep":
|
454 | cloned = jQuery.extend(true, {}, data);
|
455 | break;
|
456 | case "shallow":
|
457 | cloned = Object.create(data.prototype || null);
|
458 | Object.keys(data).map(function (i) {
|
459 | cloned[i] = data[i];
|
460 | });
|
461 | break;
|
462 | default:
|
463 | break;
|
464 | }
|
465 |
|
466 | return cloned;
|
467 | }
|
468 |
|
469 | function cloneObjectArray(objarray, method) {
|
470 | var i,
|
471 | result = [];
|
472 |
|
473 | if (method == "parse-stringify") {
|
474 | return clone(objarray, method);
|
475 | }
|
476 |
|
477 | i = objarray.length - 1;
|
478 |
|
479 | for (; i <= 0; i--) {
|
480 | result.push(clone(objarray[i], method));
|
481 | }
|
482 |
|
483 | return result;
|
484 | }
|
485 |
|
486 | function localStorageAvailable() {
|
487 | try {
|
488 | return (window && window.localStorage !== undefined && window.localStorage !== null);
|
489 | } catch (e) {
|
490 | return false;
|
491 | }
|
492 | }
|
493 |
|
494 |
|
495 | |
496 |
|
497 |
|
498 |
|
499 |
|
500 |
|
501 |
|
502 | function LokiEventEmitter() {}
|
503 |
|
504 | |
505 |
|
506 |
|
507 |
|
508 | LokiEventEmitter.prototype.events = {};
|
509 |
|
510 | |
511 |
|
512 |
|
513 |
|
514 |
|
515 |
|
516 | LokiEventEmitter.prototype.asyncListeners = false;
|
517 |
|
518 | |
519 |
|
520 |
|
521 |
|
522 |
|
523 |
|
524 |
|
525 | LokiEventEmitter.prototype.on = function (eventName, listener) {
|
526 | var event;
|
527 | var self = this;
|
528 |
|
529 | if (Array.isArray(eventName)) {
|
530 | eventName.forEach(function(currentEventName) {
|
531 | self.on(currentEventName, listener);
|
532 | });
|
533 | return listener;
|
534 | }
|
535 |
|
536 | event = this.events[eventName];
|
537 | if (!event) {
|
538 | event = this.events[eventName] = [];
|
539 | }
|
540 | event.push(listener);
|
541 | return listener;
|
542 | };
|
543 |
|
544 | |
545 |
|
546 |
|
547 |
|
548 |
|
549 |
|
550 |
|
551 |
|
552 | LokiEventEmitter.prototype.emit = function (eventName, data) {
|
553 | var self = this;
|
554 | if (eventName && this.events[eventName]) {
|
555 | this.events[eventName].forEach(function (listener) {
|
556 | if (self.asyncListeners) {
|
557 | setTimeout(function () {
|
558 | listener(data);
|
559 | }, 1);
|
560 | } else {
|
561 | listener(data);
|
562 | }
|
563 |
|
564 | });
|
565 | } else {
|
566 | throw new Error('No event ' + eventName + ' defined');
|
567 | }
|
568 | };
|
569 |
|
570 | |
571 |
|
572 |
|
573 |
|
574 |
|
575 |
|
576 |
|
577 |
|
578 | LokiEventEmitter.prototype.addListener = LokiEventEmitter.prototype.on;
|
579 |
|
580 | |
581 |
|
582 |
|
583 |
|
584 |
|
585 |
|
586 | LokiEventEmitter.prototype.removeListener = function (eventName, listener) {
|
587 | var self = this;
|
588 | if (Array.isArray(eventName)) {
|
589 | eventName.forEach(function(currentEventName) {
|
590 | self.removeListener(currentEventName, listen);
|
591 | });
|
592 | }
|
593 |
|
594 | if (this.events[eventName]) {
|
595 | var listeners = this.events[eventName];
|
596 | listeners.splice(listeners.indexOf(listener), 1);
|
597 | }
|
598 | };
|
599 |
|
600 | |
601 |
|
602 |
|
603 |
|
604 |
|
605 |
|
606 |
|
607 |
|
608 |
|
609 |
|
610 |
|
611 |
|
612 |
|
613 |
|
614 |
|
615 |
|
616 | function Loki(filename, options) {
|
617 | this.filename = filename || 'loki.db';
|
618 | this.collections = [];
|
619 |
|
620 |
|
621 |
|
622 | this.databaseVersion = 1.1;
|
623 | this.engineVersion = 1.1;
|
624 |
|
625 |
|
626 |
|
627 | this.autosave = false;
|
628 | this.autosaveInterval = 5000;
|
629 | this.autosaveHandle = null;
|
630 |
|
631 | this.options = {};
|
632 |
|
633 |
|
634 |
|
635 |
|
636 |
|
637 |
|
638 |
|
639 |
|
640 | this.persistenceMethod = null;
|
641 |
|
642 |
|
643 | this.persistenceAdapter = null;
|
644 |
|
645 |
|
646 | this.verbose = options && options.hasOwnProperty('verbose') ? options.verbose : false;
|
647 |
|
648 | this.events = {
|
649 | 'init': [],
|
650 | 'loaded': [],
|
651 | 'flushChanges': [],
|
652 | 'close': [],
|
653 | 'changes': [],
|
654 | 'warning': []
|
655 | };
|
656 |
|
657 | var getENV = function () {
|
658 |
|
659 |
|
660 |
|
661 |
|
662 |
|
663 |
|
664 |
|
665 |
|
666 |
|
667 | if (typeof window === 'undefined') {
|
668 | return 'NODEJS';
|
669 | }
|
670 |
|
671 | if (typeof global !== 'undefined' && global.window) {
|
672 | return 'NODEJS';
|
673 | }
|
674 |
|
675 | if (typeof document !== 'undefined') {
|
676 | if (document.URL.indexOf('http://') === -1 && document.URL.indexOf('https://') === -1) {
|
677 | return 'CORDOVA';
|
678 | }
|
679 | return 'BROWSER';
|
680 | }
|
681 | return 'CORDOVA';
|
682 | };
|
683 |
|
684 |
|
685 |
|
686 |
|
687 |
|
688 | if (options && options.hasOwnProperty('env')) {
|
689 | this.ENV = options.env;
|
690 | } else {
|
691 | this.ENV = getENV();
|
692 | }
|
693 |
|
694 |
|
695 | if (this.ENV === 'undefined') {
|
696 | this.ENV = 'NODEJS';
|
697 | }
|
698 |
|
699 |
|
700 | this.configureOptions(options, true);
|
701 |
|
702 |
|
703 | this.on('init', this.clearChanges);
|
704 |
|
705 | }
|
706 |
|
707 |
|
708 | Loki.prototype = new LokiEventEmitter();
|
709 | Loki.prototype.constructor = Loki;
|
710 |
|
711 |
|
712 |
|
713 | Loki.prototype.getIndexedAdapter = function () {
|
714 | var adapter;
|
715 |
|
716 | if (typeof require === 'function') {
|
717 | adapter = require("./loki-indexed-adapter.js");
|
718 | }
|
719 |
|
720 | return adapter;
|
721 | };
|
722 |
|
723 |
|
724 | |
725 |
|
726 |
|
727 |
|
728 |
|
729 |
|
730 |
|
731 |
|
732 |
|
733 |
|
734 |
|
735 |
|
736 |
|
737 |
|
738 |
|
739 |
|
740 | Loki.prototype.configureOptions = function (options, initialConfig) {
|
741 | var defaultPersistence = {
|
742 | 'NODEJS': 'fs',
|
743 | 'BROWSER': 'localStorage',
|
744 | 'CORDOVA': 'localStorage'
|
745 | },
|
746 | persistenceMethods = {
|
747 | 'fs': LokiFsAdapter,
|
748 | 'localStorage': LokiLocalStorageAdapter
|
749 | };
|
750 |
|
751 | this.options = {};
|
752 |
|
753 | this.persistenceMethod = null;
|
754 |
|
755 |
|
756 | this.persistenceAdapter = null;
|
757 |
|
758 |
|
759 | if (typeof (options) !== 'undefined') {
|
760 | this.options = options;
|
761 |
|
762 | if (this.options.hasOwnProperty('persistenceMethod')) {
|
763 |
|
764 | if (typeof (persistenceMethods[options.persistenceMethod]) == 'function') {
|
765 | this.persistenceMethod = options.persistenceMethod;
|
766 | this.persistenceAdapter = new persistenceMethods[options.persistenceMethod]();
|
767 | }
|
768 |
|
769 | }
|
770 |
|
771 |
|
772 | if (this.options.hasOwnProperty('adapter')) {
|
773 | this.persistenceMethod = 'adapter';
|
774 | this.persistenceAdapter = options.adapter;
|
775 | this.options.adapter = null;
|
776 | }
|
777 |
|
778 |
|
779 |
|
780 | if (options.autoload && initialConfig) {
|
781 |
|
782 | var self = this;
|
783 | setTimeout(function () {
|
784 | self.loadDatabase(options, options.autoloadCallback);
|
785 | }, 1);
|
786 | }
|
787 |
|
788 | if (this.options.hasOwnProperty('autosaveInterval')) {
|
789 | this.autosaveDisable();
|
790 | this.autosaveInterval = parseInt(this.options.autosaveInterval, 10);
|
791 | }
|
792 |
|
793 | if (this.options.hasOwnProperty('autosave') && this.options.autosave) {
|
794 | this.autosaveDisable();
|
795 | this.autosave = true;
|
796 |
|
797 | if (this.options.hasOwnProperty('autosaveCallback')) {
|
798 | this.autosaveEnable(options, options.autosaveCallback);
|
799 | } else {
|
800 | this.autosaveEnable();
|
801 | }
|
802 | }
|
803 | }
|
804 |
|
805 |
|
806 | if (!this.options.hasOwnProperty('serializationMethod')) {
|
807 | this.options.serializationMethod = 'normal';
|
808 | }
|
809 |
|
810 |
|
811 | if (!this.options.hasOwnProperty('destructureDelimiter')) {
|
812 | this.options.destructureDelimiter = '$<\n';
|
813 | }
|
814 |
|
815 |
|
816 | if (this.persistenceAdapter === null) {
|
817 | this.persistenceMethod = defaultPersistence[this.ENV];
|
818 | if (this.persistenceMethod) {
|
819 | this.persistenceAdapter = new persistenceMethods[this.persistenceMethod]();
|
820 | }
|
821 | }
|
822 |
|
823 | };
|
824 |
|
825 | |
826 |
|
827 |
|
828 |
|
829 |
|
830 |
|
831 |
|
832 | Loki.prototype.copy = function(options) {
|
833 | var databaseCopy = new Loki(this.filename);
|
834 | var clen, idx;
|
835 |
|
836 | options = options || {};
|
837 |
|
838 |
|
839 | databaseCopy.loadJSONObject(this, { retainDirtyFlags: true });
|
840 |
|
841 |
|
842 | if(options.hasOwnProperty("removeNonSerializable") && options.removeNonSerializable === true) {
|
843 | databaseCopy.autosaveHandle = null;
|
844 | databaseCopy.persistenceAdapter = null;
|
845 |
|
846 | clen = databaseCopy.collections.length;
|
847 | for (idx=0; idx<clen; idx++) {
|
848 | databaseCopy.collections[idx].constraints = null;
|
849 | databaseCopy.collections[idx].ttl = null;
|
850 | }
|
851 | }
|
852 |
|
853 | return databaseCopy;
|
854 | };
|
855 |
|
856 | |
857 |
|
858 |
|
859 |
|
860 |
|
861 |
|
862 |
|
863 |
|
864 |
|
865 |
|
866 |
|
867 |
|
868 | Loki.prototype.anonym = function (docs, options) {
|
869 | var collection = new Collection('anonym', options);
|
870 | collection.insert(docs);
|
871 |
|
872 | if (this.verbose)
|
873 | collection.console = console;
|
874 |
|
875 | return collection;
|
876 | };
|
877 |
|
878 | |
879 |
|
880 |
|
881 |
|
882 |
|
883 |
|
884 |
|
885 |
|
886 |
|
887 |
|
888 |
|
889 |
|
890 |
|
891 |
|
892 |
|
893 |
|
894 | Loki.prototype.addCollection = function (name, options) {
|
895 | var collection = new Collection(name, options);
|
896 | this.collections.push(collection);
|
897 |
|
898 | if (this.verbose)
|
899 | collection.console = console;
|
900 |
|
901 | return collection;
|
902 | };
|
903 |
|
904 | Loki.prototype.loadCollection = function (collection) {
|
905 | if (!collection.name) {
|
906 | throw new Error('Collection must have a name property to be loaded');
|
907 | }
|
908 | this.collections.push(collection);
|
909 | };
|
910 |
|
911 | |
912 |
|
913 |
|
914 |
|
915 |
|
916 |
|
917 | Loki.prototype.getCollection = function (collectionName) {
|
918 | var i,
|
919 | len = this.collections.length;
|
920 |
|
921 | for (i = 0; i < len; i += 1) {
|
922 | if (this.collections[i].name === collectionName) {
|
923 | return this.collections[i];
|
924 | }
|
925 | }
|
926 |
|
927 |
|
928 | this.emit('warning', 'collection ' + collectionName + ' not found');
|
929 | return null;
|
930 | };
|
931 |
|
932 | Loki.prototype.listCollections = function () {
|
933 |
|
934 | var i = this.collections.length,
|
935 | colls = [];
|
936 |
|
937 | while (i--) {
|
938 | colls.push({
|
939 | name: this.collections[i].name,
|
940 | type: this.collections[i].objType,
|
941 | count: this.collections[i].data.length
|
942 | });
|
943 | }
|
944 | return colls;
|
945 | };
|
946 |
|
947 | |
948 |
|
949 |
|
950 |
|
951 |
|
952 | Loki.prototype.removeCollection = function (collectionName) {
|
953 | var i,
|
954 | len = this.collections.length;
|
955 |
|
956 | for (i = 0; i < len; i += 1) {
|
957 | if (this.collections[i].name === collectionName) {
|
958 | var tmpcol = new Collection(collectionName, {});
|
959 | var curcol = this.collections[i];
|
960 | for (var prop in curcol) {
|
961 | if (curcol.hasOwnProperty(prop) && tmpcol.hasOwnProperty(prop)) {
|
962 | curcol[prop] = tmpcol[prop];
|
963 | }
|
964 | }
|
965 | this.collections.splice(i, 1);
|
966 | return;
|
967 | }
|
968 | }
|
969 | };
|
970 |
|
971 | Loki.prototype.getName = function () {
|
972 | return this.name;
|
973 | };
|
974 |
|
975 | |
976 |
|
977 |
|
978 |
|
979 | Loki.prototype.serializeReplacer = function (key, value) {
|
980 | switch (key) {
|
981 | case 'autosaveHandle':
|
982 | case 'persistenceAdapter':
|
983 | case 'constraints':
|
984 | case 'ttl':
|
985 | return null;
|
986 | default:
|
987 | return value;
|
988 | }
|
989 | };
|
990 |
|
991 | |
992 |
|
993 |
|
994 |
|
995 |
|
996 |
|
997 | Loki.prototype.serialize = function (options) {
|
998 | options = options || {};
|
999 |
|
1000 | if (!options.hasOwnProperty("serializationMethod")) {
|
1001 | options.serializationMethod = this.options.serializationMethod;
|
1002 | }
|
1003 |
|
1004 | switch(options.serializationMethod) {
|
1005 | case "normal": return JSON.stringify(this, this.serializeReplacer);
|
1006 | case "pretty": return JSON.stringify(this, this.serializeReplacer, 2);
|
1007 | case "destructured": return this.serializeDestructured();
|
1008 | default: return JSON.stringify(this, this.serializeReplacer);
|
1009 | }
|
1010 | };
|
1011 |
|
1012 |
|
1013 | Loki.prototype.toJson = Loki.prototype.serialize;
|
1014 |
|
1015 | |
1016 |
|
1017 |
|
1018 |
|
1019 |
|
1020 |
|
1021 |
|
1022 |
|
1023 |
|
1024 |
|
1025 |
|
1026 |
|
1027 |
|
1028 |
|
1029 |
|
1030 | Loki.prototype.serializeDestructured = function(options) {
|
1031 | var idx, sidx, result, resultlen;
|
1032 | var reconstruct = [];
|
1033 | var dbcopy;
|
1034 |
|
1035 | options = options || {};
|
1036 |
|
1037 | if (!options.hasOwnProperty("partitioned")) {
|
1038 | options.partitioned = false;
|
1039 | }
|
1040 |
|
1041 | if (!options.hasOwnProperty("delimited")) {
|
1042 | options.delimited = true;
|
1043 | }
|
1044 |
|
1045 | if (!options.hasOwnProperty("delimiter")) {
|
1046 | options.delimiter = this.options.destructureDelimiter;
|
1047 | }
|
1048 |
|
1049 |
|
1050 | if (options.partitioned === true && options.hasOwnProperty("partition") && options.partition >= 0) {
|
1051 | return this.serializeCollection({
|
1052 | delimited: options.delimited,
|
1053 | delimiter: options.delimiter,
|
1054 | collectionIndex: options.partition
|
1055 | });
|
1056 | }
|
1057 |
|
1058 |
|
1059 | dbcopy = new Loki(this.filename);
|
1060 | dbcopy.loadJSONObject(this);
|
1061 |
|
1062 | for(idx=0; idx < dbcopy.collections.length; idx++) {
|
1063 | dbcopy.collections[idx].data = [];
|
1064 | }
|
1065 |
|
1066 |
|
1067 | if (options.partitioned === true && options.partition === -1) {
|
1068 |
|
1069 | return dbcopy.serialize({
|
1070 | serializationMethod: "normal"
|
1071 | });
|
1072 | }
|
1073 |
|
1074 |
|
1075 |
|
1076 | reconstruct.push(dbcopy.serialize({
|
1077 | serializationMethod: "normal"
|
1078 | }));
|
1079 |
|
1080 | dbcopy = null;
|
1081 |
|
1082 |
|
1083 | for(idx=0; idx < this.collections.length; idx++) {
|
1084 | result = this.serializeCollection({
|
1085 | delimited: options.delimited,
|
1086 | delimiter: options.delimiter,
|
1087 | collectionIndex: idx
|
1088 | });
|
1089 |
|
1090 |
|
1091 | if (options.partitioned === false && options.delimited === false) {
|
1092 | if (!Array.isArray(result)) {
|
1093 | throw new Error("a nondelimited, non partitioned collection serialization did not return an expected array");
|
1094 | }
|
1095 |
|
1096 |
|
1097 |
|
1098 |
|
1099 | resultlen = result.length;
|
1100 |
|
1101 | for (sidx=0; sidx < resultlen; sidx++) {
|
1102 | reconstruct.push(result[sidx]);
|
1103 | result[sidx] = null;
|
1104 | }
|
1105 |
|
1106 | reconstruct.push("");
|
1107 | }
|
1108 | else {
|
1109 | reconstruct.push(result);
|
1110 | }
|
1111 | }
|
1112 |
|
1113 |
|
1114 | if (options.partitioned) {
|
1115 |
|
1116 |
|
1117 | if (options.delimited) {
|
1118 | return reconstruct;
|
1119 | }
|
1120 |
|
1121 |
|
1122 |
|
1123 | else {
|
1124 | return reconstruct;
|
1125 | }
|
1126 | }
|
1127 | else {
|
1128 |
|
1129 |
|
1130 |
|
1131 | if (options.delimited) {
|
1132 |
|
1133 | reconstruct.push("");
|
1134 |
|
1135 | return reconstruct.join(options.delimiter);
|
1136 | }
|
1137 |
|
1138 |
|
1139 | else {
|
1140 |
|
1141 | reconstruct.push("");
|
1142 |
|
1143 | return reconstruct;
|
1144 | }
|
1145 | }
|
1146 |
|
1147 | reconstruct.push("");
|
1148 |
|
1149 | return reconstruct.join(delim);
|
1150 | };
|
1151 |
|
1152 | |
1153 |
|
1154 |
|
1155 |
|
1156 |
|
1157 |
|
1158 |
|
1159 |
|
1160 |
|
1161 |
|
1162 |
|
1163 | Loki.prototype.serializeCollection = function(options) {
|
1164 | var doccount,
|
1165 | docidx,
|
1166 | resultlines = [];
|
1167 |
|
1168 | options = options || {};
|
1169 |
|
1170 | if (!options.hasOwnProperty("delimited")) {
|
1171 | options.delimited = true;
|
1172 | }
|
1173 |
|
1174 | if (!options.hasOwnProperty("collectionIndex")) {
|
1175 | throw new Error("serializeCollection called without 'collectionIndex' option");
|
1176 | }
|
1177 |
|
1178 | doccount = this.collections[options.collectionIndex].data.length;
|
1179 |
|
1180 | resultlines = [];
|
1181 |
|
1182 | for(docidx=0; docidx<doccount; docidx++) {
|
1183 | resultlines.push(JSON.stringify(this.collections[options.collectionIndex].data[docidx]));
|
1184 | }
|
1185 |
|
1186 |
|
1187 | if (options.delimited) {
|
1188 |
|
1189 | resultlines.push("");
|
1190 |
|
1191 | return resultlines.join(options.delimiter);
|
1192 | }
|
1193 | else {
|
1194 |
|
1195 | return resultlines;
|
1196 | }
|
1197 | };
|
1198 |
|
1199 | |
1200 |
|
1201 |
|
1202 |
|
1203 |
|
1204 |
|
1205 |
|
1206 |
|
1207 |
|
1208 |
|
1209 |
|
1210 |
|
1211 |
|
1212 |
|
1213 |
|
1214 |
|
1215 | Loki.prototype.deserializeDestructured = function(destructuredSource, options) {
|
1216 | var workarray=[];
|
1217 | var len, cdb;
|
1218 | var idx, collIndex=0, collCount, lineIndex=1, done=false;
|
1219 | var currLine, currObject;
|
1220 |
|
1221 | options = options || {};
|
1222 |
|
1223 | if (!options.hasOwnProperty("partitioned")) {
|
1224 | options.partitioned = false;
|
1225 | }
|
1226 |
|
1227 | if (!options.hasOwnProperty("delimited")) {
|
1228 | options.delimited = true;
|
1229 | }
|
1230 |
|
1231 | if (!options.hasOwnProperty("delimiter")) {
|
1232 | options.delimiter = this.options.destructureDelimiter;
|
1233 | }
|
1234 |
|
1235 |
|
1236 |
|
1237 |
|
1238 |
|
1239 | if (options.partitioned) {
|
1240 |
|
1241 | if (options.hasOwnProperty('partition')) {
|
1242 |
|
1243 | if (options.partition === -1) {
|
1244 | cdb = JSON.parse(destructuredSource[0]);
|
1245 |
|
1246 | return cdb;
|
1247 | }
|
1248 |
|
1249 |
|
1250 | return this.deserializeCollection(destructuredSource[options.partition+1], options);
|
1251 | }
|
1252 |
|
1253 |
|
1254 | cdb = JSON.parse(destructuredSource[0]);
|
1255 | collCount = cdb.collections.length;
|
1256 | for(collIndex=0; collIndex<collCount; collIndex++) {
|
1257 |
|
1258 | cdb.collections[collIndex].data = this.deserializeCollection(destructuredSource[collIndex+1], options);
|
1259 | }
|
1260 |
|
1261 | return cdb;
|
1262 | }
|
1263 |
|
1264 |
|
1265 |
|
1266 |
|
1267 |
|
1268 |
|
1269 | if (options.delimited) {
|
1270 | workarray = destructuredSource.split(options.delimiter);
|
1271 | destructuredSource = null;
|
1272 | len = workarray.length;
|
1273 |
|
1274 | if (len === 0) {
|
1275 | return null;
|
1276 | }
|
1277 | }
|
1278 |
|
1279 | else {
|
1280 | workarray = destructuredSource;
|
1281 | }
|
1282 |
|
1283 |
|
1284 | cdb = JSON.parse(workarray[0]);
|
1285 | collCount = cdb.collections.length;
|
1286 | workarray[0] = null;
|
1287 |
|
1288 | while (!done) {
|
1289 | currLine = workarray[lineIndex];
|
1290 |
|
1291 |
|
1292 | if (workarray[lineIndex] === "") {
|
1293 |
|
1294 | if (++collIndex > collCount) {
|
1295 | done = true;
|
1296 | }
|
1297 | }
|
1298 | else {
|
1299 | currObject = JSON.parse(workarray[lineIndex]);
|
1300 | cdb.collections[collIndex].data.push(currObject);
|
1301 | }
|
1302 |
|
1303 |
|
1304 | workarray[lineIndex++] = null;
|
1305 | }
|
1306 |
|
1307 | return cdb;
|
1308 | };
|
1309 |
|
1310 | |
1311 |
|
1312 |
|
1313 |
|
1314 |
|
1315 |
|
1316 |
|
1317 |
|
1318 |
|
1319 |
|
1320 |
|
1321 | Loki.prototype.deserializeCollection = function(destructuredSource, options) {
|
1322 | var workarray=[];
|
1323 | var idx, len;
|
1324 |
|
1325 | options = options || {};
|
1326 |
|
1327 | if (!options.hasOwnProperty("partitioned")) {
|
1328 | options.partitioned = false;
|
1329 | }
|
1330 |
|
1331 | if (!options.hasOwnProperty("delimited")) {
|
1332 | options.delimited = true;
|
1333 | }
|
1334 |
|
1335 | if (!options.hasOwnProperty("delimiter")) {
|
1336 | options.delimiter = this.options.destructureDelimiter;
|
1337 | }
|
1338 |
|
1339 | if (options.delimited) {
|
1340 | workarray = destructuredSource.split(options.delimiter);
|
1341 | workarray.pop();
|
1342 | }
|
1343 | else {
|
1344 | workarray = destructuredSource;
|
1345 | }
|
1346 |
|
1347 | len = workarray.length;
|
1348 | for (idx=0; idx < len; idx++) {
|
1349 | workarray[idx] = JSON.parse(workarray[idx]);
|
1350 | }
|
1351 |
|
1352 | return workarray;
|
1353 | };
|
1354 |
|
1355 | |
1356 |
|
1357 |
|
1358 |
|
1359 |
|
1360 |
|
1361 |
|
1362 | Loki.prototype.loadJSON = function (serializedDb, options) {
|
1363 | var dbObject;
|
1364 | if (serializedDb.length === 0) {
|
1365 | dbObject = {};
|
1366 | } else {
|
1367 |
|
1368 | switch (this.options.serializationMethod) {
|
1369 | case "normal":
|
1370 | case "pretty": dbObject = JSON.parse(serializedDb); break;
|
1371 | case "destructured": dbObject = this.deserializeDestructured(serializedDb); break;
|
1372 | default: dbObject = JSON.parse(serializedDb); break;
|
1373 | }
|
1374 | }
|
1375 |
|
1376 | this.loadJSONObject(dbObject, options);
|
1377 | };
|
1378 |
|
1379 | |
1380 |
|
1381 |
|
1382 |
|
1383 |
|
1384 |
|
1385 |
|
1386 |
|
1387 | Loki.prototype.loadJSONObject = function (dbObject, options) {
|
1388 | var i = 0,
|
1389 | len = dbObject.collections ? dbObject.collections.length : 0,
|
1390 | coll,
|
1391 | copyColl,
|
1392 | clen,
|
1393 | j,
|
1394 | loader,
|
1395 | collObj;
|
1396 |
|
1397 | this.name = dbObject.name;
|
1398 |
|
1399 |
|
1400 | this.databaseVersion = 1.0;
|
1401 | if (dbObject.hasOwnProperty('databaseVersion')) {
|
1402 | this.databaseVersion = dbObject.databaseVersion;
|
1403 | }
|
1404 |
|
1405 | this.collections = [];
|
1406 |
|
1407 | function makeLoader(coll) {
|
1408 | var collOptions = options[coll.name];
|
1409 | var inflater;
|
1410 |
|
1411 | if(collOptions.proto) {
|
1412 | inflater = collOptions.inflate || Utils.copyProperties;
|
1413 |
|
1414 | return function(data) {
|
1415 | var collObj = new(collOptions.proto)();
|
1416 | inflater(data, collObj);
|
1417 | return collObj;
|
1418 | };
|
1419 | }
|
1420 |
|
1421 | return collOptions.inflate;
|
1422 | }
|
1423 |
|
1424 | for (i; i < len; i += 1) {
|
1425 | coll = dbObject.collections[i];
|
1426 | copyColl = this.addCollection(coll.name);
|
1427 |
|
1428 | copyColl.adaptiveBinaryIndices = coll.hasOwnProperty('adaptiveBinaryIndices')?(coll.adaptiveBinaryIndices === true): false;
|
1429 | copyColl.transactional = coll.transactional;
|
1430 | copyColl.asyncListeners = coll.asyncListeners;
|
1431 | copyColl.disableChangesApi = coll.disableChangesApi;
|
1432 | copyColl.cloneObjects = coll.cloneObjects;
|
1433 | copyColl.cloneMethod = coll.cloneMethod || "parse-stringify";
|
1434 | copyColl.autoupdate = coll.autoupdate;
|
1435 | copyColl.changes = coll.changes;
|
1436 |
|
1437 | if (options && options.retainDirtyFlags === true) {
|
1438 | copyColl.dirty = coll.dirty;
|
1439 | }
|
1440 | else {
|
1441 | copyColl.dirty = false;
|
1442 | }
|
1443 |
|
1444 |
|
1445 | clen = coll.data.length;
|
1446 | j = 0;
|
1447 | if (options && options.hasOwnProperty(coll.name)) {
|
1448 | loader = makeLoader(coll);
|
1449 |
|
1450 | for (j; j < clen; j++) {
|
1451 | collObj = loader(coll.data[j]);
|
1452 | copyColl.data[j] = collObj;
|
1453 | copyColl.addAutoUpdateObserver(collObj);
|
1454 | }
|
1455 | } else {
|
1456 |
|
1457 | for (j; j < clen; j++) {
|
1458 | copyColl.data[j] = coll.data[j];
|
1459 | copyColl.addAutoUpdateObserver(copyColl.data[j]);
|
1460 | }
|
1461 | }
|
1462 |
|
1463 | copyColl.maxId = (coll.data.length === 0) ? 0 : coll.maxId;
|
1464 | copyColl.idIndex = coll.idIndex;
|
1465 | if (typeof (coll.binaryIndices) !== 'undefined') {
|
1466 | copyColl.binaryIndices = coll.binaryIndices;
|
1467 | }
|
1468 | if (typeof coll.transforms !== 'undefined') {
|
1469 | copyColl.transforms = coll.transforms;
|
1470 | }
|
1471 |
|
1472 | copyColl.ensureId();
|
1473 |
|
1474 |
|
1475 | copyColl.uniqueNames = [];
|
1476 | if (coll.hasOwnProperty("uniqueNames")) {
|
1477 | copyColl.uniqueNames = coll.uniqueNames;
|
1478 | for (j = 0; j < copyColl.uniqueNames.length; j++) {
|
1479 | copyColl.ensureUniqueIndex(copyColl.uniqueNames[j]);
|
1480 | }
|
1481 | }
|
1482 |
|
1483 |
|
1484 | if (typeof (coll.DynamicViews) === 'undefined') continue;
|
1485 |
|
1486 |
|
1487 | for (var idx = 0; idx < coll.DynamicViews.length; idx++) {
|
1488 | var colldv = coll.DynamicViews[idx];
|
1489 |
|
1490 | var dv = copyColl.addDynamicView(colldv.name, colldv.options);
|
1491 | dv.resultdata = colldv.resultdata;
|
1492 | dv.resultsdirty = colldv.resultsdirty;
|
1493 | dv.filterPipeline = colldv.filterPipeline;
|
1494 |
|
1495 | dv.sortCriteria = colldv.sortCriteria;
|
1496 | dv.sortFunction = null;
|
1497 |
|
1498 | dv.sortDirty = colldv.sortDirty;
|
1499 | dv.resultset.filteredrows = colldv.resultset.filteredrows;
|
1500 | dv.resultset.searchIsChained = colldv.resultset.searchIsChained;
|
1501 | dv.resultset.filterInitialized = colldv.resultset.filterInitialized;
|
1502 |
|
1503 | dv.rematerialize({
|
1504 | removeWhereFilters: true
|
1505 | });
|
1506 | }
|
1507 | }
|
1508 | };
|
1509 |
|
1510 | |
1511 |
|
1512 |
|
1513 |
|
1514 |
|
1515 |
|
1516 |
|
1517 | Loki.prototype.close = function (callback) {
|
1518 |
|
1519 |
|
1520 | if (this.autosave) {
|
1521 | this.autosaveDisable();
|
1522 | if (this.autosaveDirty()) {
|
1523 | this.saveDatabase(callback);
|
1524 | callback = undefined;
|
1525 | }
|
1526 | }
|
1527 |
|
1528 | if (callback) {
|
1529 | this.on('close', callback);
|
1530 | }
|
1531 | this.emit('close');
|
1532 | };
|
1533 |
|
1534 | |
1535 |
|
1536 |
|
1537 |
|
1538 | |
1539 |
|
1540 |
|
1541 |
|
1542 |
|
1543 | |
1544 |
|
1545 |
|
1546 |
|
1547 |
|
1548 |
|
1549 |
|
1550 |
|
1551 |
|
1552 |
|
1553 | Loki.prototype.generateChangesNotification = function (arrayOfCollectionNames) {
|
1554 | function getCollName(coll) {
|
1555 | return coll.name;
|
1556 | }
|
1557 | var changes = [],
|
1558 | selectedCollections = arrayOfCollectionNames || this.collections.map(getCollName);
|
1559 |
|
1560 | this.collections.forEach(function (coll) {
|
1561 | if (selectedCollections.indexOf(getCollName(coll)) !== -1) {
|
1562 | changes = changes.concat(coll.getChanges());
|
1563 | }
|
1564 | });
|
1565 | return changes;
|
1566 | };
|
1567 |
|
1568 | |
1569 |
|
1570 |
|
1571 |
|
1572 |
|
1573 | Loki.prototype.serializeChanges = function (collectionNamesArray) {
|
1574 | return JSON.stringify(this.generateChangesNotification(collectionNamesArray));
|
1575 | };
|
1576 |
|
1577 | |
1578 |
|
1579 |
|
1580 |
|
1581 | Loki.prototype.clearChanges = function () {
|
1582 | this.collections.forEach(function (coll) {
|
1583 | if (coll.flushChanges) {
|
1584 | coll.flushChanges();
|
1585 | }
|
1586 | });
|
1587 | };
|
1588 |
|
1589 | |
1590 |
|
1591 |
|
1592 |
|
1593 | |
1594 |
|
1595 |
|
1596 |
|
1597 |
|
1598 |
|
1599 | |
1600 |
|
1601 |
|
1602 |
|
1603 |
|
1604 |
|
1605 | function LokiMemoryAdapter() {
|
1606 | this.hashStore = {};
|
1607 | }
|
1608 |
|
1609 | |
1610 |
|
1611 |
|
1612 |
|
1613 |
|
1614 |
|
1615 |
|
1616 |
|
1617 | LokiMemoryAdapter.prototype.loadDatabase = function (dbname, callback) {
|
1618 | if (this.hashStore.hasOwnProperty(dbname)) {
|
1619 | callback(this.hashStore[dbname].value);
|
1620 | }
|
1621 | else {
|
1622 | callback (new Error("unable to load database, " + dbname + " was not found in memory adapter"));
|
1623 | }
|
1624 | };
|
1625 |
|
1626 | |
1627 |
|
1628 |
|
1629 |
|
1630 |
|
1631 |
|
1632 |
|
1633 |
|
1634 | LokiMemoryAdapter.prototype.saveDatabase = function (dbname, dbstring, callback) {
|
1635 | var saveCount = (this.hashStore.hasOwnProperty(dbname)?this.hashStore[dbname].savecount:0);
|
1636 |
|
1637 | this.hashStore[dbname] = {
|
1638 | savecount: saveCount+1,
|
1639 | lastsave: new Date(),
|
1640 | value: dbstring
|
1641 | };
|
1642 |
|
1643 | callback();
|
1644 | };
|
1645 |
|
1646 | |
1647 |
|
1648 |
|
1649 |
|
1650 |
|
1651 |
|
1652 |
|
1653 |
|
1654 |
|
1655 |
|
1656 |
|
1657 |
|
1658 |
|
1659 |
|
1660 |
|
1661 |
|
1662 |
|
1663 | function LokiPartitioningAdapter(adapter, options) {
|
1664 | this.mode = "reference";
|
1665 | this.adapter = null;
|
1666 | this.options = options || {};
|
1667 | this.dbref = null;
|
1668 | this.dbname = "";
|
1669 | this.pageIterator = {};
|
1670 |
|
1671 |
|
1672 | if (adapter) {
|
1673 | if (adapter.mode === "reference") {
|
1674 | throw new Error("LokiPartitioningAdapter cannot be instantiated with a reference mode adapter");
|
1675 | }
|
1676 | else {
|
1677 | this.adapter = adapter;
|
1678 | }
|
1679 | }
|
1680 | else {
|
1681 | throw new Error("LokiPartitioningAdapter requires a (non-reference mode) adapter on construction");
|
1682 | }
|
1683 |
|
1684 |
|
1685 | if (!this.options.hasOwnProperty("paging")) {
|
1686 | this.options.paging = false;
|
1687 | }
|
1688 |
|
1689 |
|
1690 | if (!this.options.hasOwnProperty("pageSize")) {
|
1691 | this.options.pageSize = 25*1024*1024;
|
1692 | }
|
1693 |
|
1694 | if (!this.options.hasOwnProperty("delimiter")) {
|
1695 | this.options.delimiter = '$<\n';
|
1696 | }
|
1697 | }
|
1698 |
|
1699 | |
1700 |
|
1701 |
|
1702 |
|
1703 |
|
1704 |
|
1705 |
|
1706 |
|
1707 | LokiPartitioningAdapter.prototype.loadDatabase = function (dbname, callback) {
|
1708 | var self=this;
|
1709 | this.dbname = dbname;
|
1710 | this.dbref = new Loki(dbname);
|
1711 |
|
1712 |
|
1713 | this.adapter.loadDatabase(dbname, function(result) {
|
1714 | if (typeof result !== "string") {
|
1715 | callback(new Error("LokiPartitioningAdapter received an unexpected response from inner adapter loadDatabase()"));
|
1716 | }
|
1717 |
|
1718 |
|
1719 | var db = JSON.parse(result);
|
1720 | self.dbref.loadJSONObject(db);
|
1721 | db = null;
|
1722 |
|
1723 | var clen = self.dbref.collections.length;
|
1724 |
|
1725 | if (self.dbref.collections.length === 0) {
|
1726 | callback(self.dbref);
|
1727 | return;
|
1728 | }
|
1729 |
|
1730 | self.pageIterator = {
|
1731 | collection: 0,
|
1732 | pageIndex: 0
|
1733 | };
|
1734 |
|
1735 | self.loadNextPartition(0, function() {
|
1736 | callback(self.dbref);
|
1737 | });
|
1738 | });
|
1739 | };
|
1740 |
|
1741 | |
1742 |
|
1743 |
|
1744 |
|
1745 |
|
1746 |
|
1747 | LokiPartitioningAdapter.prototype.loadNextPartition = function(partition, callback) {
|
1748 | var keyname = this.dbname + "." + partition;
|
1749 | var self=this;
|
1750 |
|
1751 | if (this.options.paging === true) {
|
1752 | this.pageIterator.pageIndex = 0;
|
1753 | this.loadNextPage(callback);
|
1754 | return;
|
1755 | }
|
1756 |
|
1757 | this.adapter.loadDatabase(keyname, function(result) {
|
1758 | var data = self.dbref.deserializeCollection(result, { delimited: true, collectionIndex: partition });
|
1759 | self.dbref.collections[partition].data = data;
|
1760 |
|
1761 | if (++partition < self.dbref.collections.length) {
|
1762 | self.loadNextPartition(partition, callback);
|
1763 | }
|
1764 | else {
|
1765 | callback();
|
1766 | }
|
1767 | });
|
1768 | };
|
1769 |
|
1770 | |
1771 |
|
1772 |
|
1773 |
|
1774 |
|
1775 | LokiPartitioningAdapter.prototype.loadNextPage = function(callback) {
|
1776 |
|
1777 | var keyname = this.dbname + "." + this.pageIterator.collection + "." + this.pageIterator.pageIndex;
|
1778 | var self=this;
|
1779 |
|
1780 |
|
1781 | this.adapter.loadDatabase(keyname, function(result) {
|
1782 | var data = result.split(self.options.delimiter);
|
1783 | result = "";
|
1784 | var dlen = data.length;
|
1785 | var idx;
|
1786 |
|
1787 |
|
1788 | var isLastPage = (data[dlen-1] === "");
|
1789 | if (isLastPage) {
|
1790 | data.pop();
|
1791 | dlen = data.length;
|
1792 |
|
1793 | if (data[dlen-1] === "" && dlen === 1) {
|
1794 | data.pop();
|
1795 | dlen = data.length;
|
1796 | }
|
1797 | }
|
1798 |
|
1799 |
|
1800 | for(idx=0; idx < dlen; idx++) {
|
1801 | self.dbref.collections[self.pageIterator.collection].data.push(JSON.parse(data[idx]));
|
1802 | data[idx] = null;
|
1803 | }
|
1804 | data = [];
|
1805 |
|
1806 |
|
1807 | if (isLastPage) {
|
1808 |
|
1809 |
|
1810 | if (++self.pageIterator.collection < self.dbref.collections.length) {
|
1811 | self.loadNextPartition(self.pageIterator.collection, callback);
|
1812 | }
|
1813 | else {
|
1814 | callback();
|
1815 | }
|
1816 | }
|
1817 | else {
|
1818 | self.pageIterator.pageIndex++;
|
1819 | self.loadNextPage(callback);
|
1820 | }
|
1821 | });
|
1822 | };
|
1823 |
|
1824 | |
1825 |
|
1826 |
|
1827 |
|
1828 |
|
1829 |
|
1830 |
|
1831 |
|
1832 |
|
1833 |
|
1834 | LokiPartitioningAdapter.prototype.exportDatabase = function(dbname, dbref, callback) {
|
1835 | var self=this;
|
1836 | var idx, clen = dbref.collections.length;
|
1837 |
|
1838 | this.dbref = dbref;
|
1839 | this.dbname = dbname;
|
1840 |
|
1841 |
|
1842 | this.dirtyPartitions = [-1];
|
1843 | for(idx=0; idx<clen; idx++) {
|
1844 | if (dbref.collections[idx].dirty) {
|
1845 | this.dirtyPartitions.push(idx);
|
1846 | }
|
1847 | }
|
1848 |
|
1849 | this.saveNextPartition(function(err) {
|
1850 | callback(err);
|
1851 | });
|
1852 | };
|
1853 |
|
1854 | |
1855 |
|
1856 |
|
1857 |
|
1858 |
|
1859 | LokiPartitioningAdapter.prototype.saveNextPartition = function(callback) {
|
1860 | var self=this;
|
1861 | var partition = this.dirtyPartitions.shift();
|
1862 | var keyname = this.dbname + ((partition===-1)?"":("." + partition));
|
1863 |
|
1864 |
|
1865 | if (this.options.paging && partition !== -1) {
|
1866 | this.pageIterator = {
|
1867 | collection: partition,
|
1868 | docIndex: 0,
|
1869 | pageIndex: 0
|
1870 | };
|
1871 |
|
1872 |
|
1873 | this.saveNextPage(function(err) {
|
1874 | if (self.dirtyPartitions.length === 0) {
|
1875 | callback(err);
|
1876 | }
|
1877 | else {
|
1878 | self.saveNextPartition(callback);
|
1879 | }
|
1880 | });
|
1881 | return;
|
1882 | }
|
1883 |
|
1884 |
|
1885 | var result = this.dbref.serializeDestructured({
|
1886 | partitioned : true,
|
1887 | delimited: true,
|
1888 | partition: partition
|
1889 | });
|
1890 |
|
1891 | this.adapter.saveDatabase(keyname, result, function(err) {
|
1892 | if (err) {
|
1893 | callback(err);
|
1894 | return;
|
1895 | }
|
1896 |
|
1897 | if (self.dirtyPartitions.length === 0) {
|
1898 | callback(null);
|
1899 | }
|
1900 | else {
|
1901 | self.saveNextPartition(callback);
|
1902 | }
|
1903 | });
|
1904 | };
|
1905 |
|
1906 | |
1907 |
|
1908 |
|
1909 |
|
1910 |
|
1911 | LokiPartitioningAdapter.prototype.saveNextPage = function(callback) {
|
1912 | var self=this;
|
1913 | var coll = this.dbref.collections[this.pageIterator.collection];
|
1914 | var keyname = this.dbname + "." + this.pageIterator.collection + "." + this.pageIterator.pageIndex;
|
1915 | var pageLen=0,
|
1916 | cdlen = coll.data.length,
|
1917 | delimlen = this.options.delimiter.length;
|
1918 | var serializedObject = "",
|
1919 | pageBuilder = "";
|
1920 | var doneWithPartition=false,
|
1921 | doneWithPage=false;
|
1922 |
|
1923 | var pageSaveCallback = function(err) {
|
1924 | pageBuilder = "";
|
1925 |
|
1926 | if (err) {
|
1927 | callback(err);
|
1928 | }
|
1929 |
|
1930 |
|
1931 | if (doneWithPartition) {
|
1932 | callback(null);
|
1933 | }
|
1934 | else {
|
1935 | self.pageIterator.pageIndex++;
|
1936 | self.saveNextPage(callback);
|
1937 | }
|
1938 | };
|
1939 |
|
1940 | if (coll.data.length === 0) {
|
1941 | doneWithPartition = true;
|
1942 | }
|
1943 |
|
1944 | while (true) {
|
1945 | if (!doneWithPartition) {
|
1946 |
|
1947 | serializedObject = JSON.stringify(coll.data[this.pageIterator.docIndex]);
|
1948 | pageBuilder += serializedObject;
|
1949 | pageLen += serializedObject.length;
|
1950 |
|
1951 |
|
1952 | if (++this.pageIterator.docIndex >= cdlen) doneWithPartition = true;
|
1953 | }
|
1954 |
|
1955 | if (pageLen >= this.options.pageSize) doneWithPage = true;
|
1956 |
|
1957 |
|
1958 |
|
1959 | if (!doneWithPage || doneWithPartition) {
|
1960 | pageBuilder += this.options.delimiter;
|
1961 | pageLen += delimlen;
|
1962 | }
|
1963 |
|
1964 |
|
1965 | if (doneWithPartition || doneWithPage) {
|
1966 | this.adapter.saveDatabase(keyname, pageBuilder, pageSaveCallback);
|
1967 | return;
|
1968 | }
|
1969 | }
|
1970 | };
|
1971 |
|
1972 | |
1973 |
|
1974 |
|
1975 |
|
1976 | function LokiFsAdapter() {
|
1977 | this.fs = require('fs');
|
1978 | }
|
1979 |
|
1980 | |
1981 |
|
1982 |
|
1983 |
|
1984 |
|
1985 |
|
1986 | LokiFsAdapter.prototype.loadDatabase = function loadDatabase(dbname, callback) {
|
1987 | var self = this;
|
1988 |
|
1989 | this.fs.stat(dbname, function (err, stats) {
|
1990 | if (!err && stats.isFile()) {
|
1991 | self.fs.readFile(dbname, {
|
1992 | encoding: 'utf8'
|
1993 | }, function readFileCallback(err, data) {
|
1994 | if (err) {
|
1995 | callback(new Error(err));
|
1996 | } else {
|
1997 | callback(data);
|
1998 | }
|
1999 | });
|
2000 | }
|
2001 | else {
|
2002 | callback(null);
|
2003 | }
|
2004 | });
|
2005 | };
|
2006 |
|
2007 | |
2008 |
|
2009 |
|
2010 |
|
2011 |
|
2012 |
|
2013 |
|
2014 | LokiFsAdapter.prototype.saveDatabase = function saveDatabase(dbname, dbstring, callback) {
|
2015 | var self = this;
|
2016 | var tmpdbname = dbname + '~';
|
2017 | this.fs.writeFile(tmpdbname, dbstring, function writeFileCallback(err) {
|
2018 | if (err) {
|
2019 | callback(new Error(err));
|
2020 | } else {
|
2021 | self.fs.rename(tmpdbname,dbname,callback);
|
2022 | }
|
2023 | });
|
2024 | };
|
2025 |
|
2026 | |
2027 |
|
2028 |
|
2029 |
|
2030 |
|
2031 |
|
2032 |
|
2033 | LokiFsAdapter.prototype.deleteDatabase = function deleteDatabase(dbname, callback) {
|
2034 | this.fs.unlink(dbname, function deleteDatabaseCallback(err) {
|
2035 | if (err) {
|
2036 | callback(new Error(err));
|
2037 | } else {
|
2038 | callback();
|
2039 | }
|
2040 | });
|
2041 | };
|
2042 |
|
2043 |
|
2044 | |
2045 |
|
2046 |
|
2047 |
|
2048 | function LokiLocalStorageAdapter() {}
|
2049 |
|
2050 | |
2051 |
|
2052 |
|
2053 |
|
2054 |
|
2055 |
|
2056 | LokiLocalStorageAdapter.prototype.loadDatabase = function loadDatabase(dbname, callback) {
|
2057 | if (localStorageAvailable()) {
|
2058 | callback(localStorage.getItem(dbname));
|
2059 | } else {
|
2060 | callback(new Error('localStorage is not available'));
|
2061 | }
|
2062 | };
|
2063 |
|
2064 | |
2065 |
|
2066 |
|
2067 |
|
2068 |
|
2069 |
|
2070 |
|
2071 | LokiLocalStorageAdapter.prototype.saveDatabase = function saveDatabase(dbname, dbstring, callback) {
|
2072 | if (localStorageAvailable()) {
|
2073 | localStorage.setItem(dbname, dbstring);
|
2074 | callback(null);
|
2075 | } else {
|
2076 | callback(new Error('localStorage is not available'));
|
2077 | }
|
2078 | };
|
2079 |
|
2080 | |
2081 |
|
2082 |
|
2083 |
|
2084 |
|
2085 |
|
2086 |
|
2087 | LokiLocalStorageAdapter.prototype.deleteDatabase = function deleteDatabase(dbname, callback) {
|
2088 | if (localStorageAvailable()) {
|
2089 | localStorage.removeItem(dbname);
|
2090 | callback(null);
|
2091 | } else {
|
2092 | callback(new Error('localStorage is not available'));
|
2093 | }
|
2094 | };
|
2095 |
|
2096 | |
2097 |
|
2098 |
|
2099 |
|
2100 |
|
2101 |
|
2102 |
|
2103 |
|
2104 |
|
2105 | Loki.prototype.loadDatabase = function (options, callback) {
|
2106 | var cFun = callback || function (err, data) {
|
2107 | if (err) {
|
2108 | throw err;
|
2109 | }
|
2110 | },
|
2111 | self = this;
|
2112 |
|
2113 |
|
2114 | if (this.persistenceAdapter !== null) {
|
2115 |
|
2116 | this.persistenceAdapter.loadDatabase(this.filename, function loadDatabaseCallback(dbString) {
|
2117 | if (typeof (dbString) === 'string') {
|
2118 | var parseSuccess = false;
|
2119 | try {
|
2120 | self.loadJSON(dbString, options || {});
|
2121 | parseSuccess = true;
|
2122 | } catch (err) {
|
2123 | cFun(err);
|
2124 | }
|
2125 | if (parseSuccess) {
|
2126 | cFun(null);
|
2127 | self.emit('loaded', 'database ' + self.filename + ' loaded');
|
2128 | }
|
2129 | } else {
|
2130 |
|
2131 | if (typeof (dbString) === "object" && dbString !== null && !(dbString instanceof Error)) {
|
2132 | self.loadJSONObject(dbString, options || {});
|
2133 | cFun(null);
|
2134 | self.emit('loaded', 'database ' + self.filename + ' loaded');
|
2135 | } else {
|
2136 |
|
2137 | cFun(dbString);
|
2138 | }
|
2139 | }
|
2140 | });
|
2141 |
|
2142 | } else {
|
2143 | cFun(new Error('persistenceAdapter not configured'));
|
2144 | }
|
2145 | };
|
2146 |
|
2147 | |
2148 |
|
2149 |
|
2150 |
|
2151 |
|
2152 |
|
2153 |
|
2154 |
|
2155 | Loki.prototype.saveDatabase = function (callback) {
|
2156 | var cFun = callback || function (err) {
|
2157 | if (err) {
|
2158 | throw err;
|
2159 | }
|
2160 | return;
|
2161 | },
|
2162 | self = this;
|
2163 |
|
2164 |
|
2165 | if (this.persistenceAdapter !== null) {
|
2166 |
|
2167 | if (this.persistenceAdapter.mode === "reference" && typeof this.persistenceAdapter.exportDatabase === "function") {
|
2168 |
|
2169 | this.persistenceAdapter.exportDatabase(this.filename, this.copy({removeNonSerializable:true}), function exportDatabaseCallback(err) {
|
2170 | self.autosaveClearFlags();
|
2171 | cFun(err);
|
2172 | });
|
2173 | }
|
2174 |
|
2175 | else {
|
2176 | this.persistenceAdapter.saveDatabase(this.filename, self.serialize(), function saveDatabasecallback(err) {
|
2177 | self.autosaveClearFlags();
|
2178 | cFun(err);
|
2179 | });
|
2180 | }
|
2181 | } else {
|
2182 | cFun(new Error('persistenceAdapter not configured'));
|
2183 | }
|
2184 | };
|
2185 |
|
2186 |
|
2187 | Loki.prototype.save = Loki.prototype.saveDatabase;
|
2188 |
|
2189 | |
2190 |
|
2191 |
|
2192 |
|
2193 |
|
2194 |
|
2195 |
|
2196 |
|
2197 |
|
2198 |
|
2199 | Loki.prototype.deleteDatabase = function (options, callback) {
|
2200 | var cFun = callback || function (err, data) {
|
2201 | if (err) {
|
2202 | throw err;
|
2203 | }
|
2204 | };
|
2205 |
|
2206 |
|
2207 | if (this.persistenceAdapter !== null) {
|
2208 | this.persistenceAdapter.deleteDatabase(this.filename, function deleteDatabaseCallback(err) {
|
2209 | cFun(err);
|
2210 | });
|
2211 | } else {
|
2212 | cFun(new Error('persistenceAdapter not configured'));
|
2213 | }
|
2214 | };
|
2215 |
|
2216 | |
2217 |
|
2218 |
|
2219 |
|
2220 |
|
2221 | Loki.prototype.autosaveDirty = function () {
|
2222 | for (var idx = 0; idx < this.collections.length; idx++) {
|
2223 | if (this.collections[idx].dirty) {
|
2224 | return true;
|
2225 | }
|
2226 | }
|
2227 |
|
2228 | return false;
|
2229 | };
|
2230 |
|
2231 | |
2232 |
|
2233 |
|
2234 |
|
2235 |
|
2236 | Loki.prototype.autosaveClearFlags = function () {
|
2237 | for (var idx = 0; idx < this.collections.length; idx++) {
|
2238 | this.collections[idx].dirty = false;
|
2239 | }
|
2240 | };
|
2241 |
|
2242 | |
2243 |
|
2244 |
|
2245 |
|
2246 |
|
2247 |
|
2248 | Loki.prototype.autosaveEnable = function (options, callback) {
|
2249 | this.autosave = true;
|
2250 |
|
2251 | var delay = 5000,
|
2252 | self = this;
|
2253 |
|
2254 | if (typeof (this.autosaveInterval) !== 'undefined' && this.autosaveInterval !== null) {
|
2255 | delay = this.autosaveInterval;
|
2256 | }
|
2257 |
|
2258 | this.autosaveHandle = setInterval(function autosaveHandleInterval() {
|
2259 |
|
2260 |
|
2261 |
|
2262 |
|
2263 | if (self.autosaveDirty()) {
|
2264 | self.saveDatabase(callback);
|
2265 | }
|
2266 | }, delay);
|
2267 | };
|
2268 |
|
2269 | |
2270 |
|
2271 |
|
2272 |
|
2273 | Loki.prototype.autosaveDisable = function () {
|
2274 | if (typeof (this.autosaveHandle) !== 'undefined' && this.autosaveHandle !== null) {
|
2275 | clearInterval(this.autosaveHandle);
|
2276 | this.autosaveHandle = null;
|
2277 | }
|
2278 | };
|
2279 |
|
2280 |
|
2281 | |
2282 |
|
2283 |
|
2284 |
|
2285 |
|
2286 |
|
2287 |
|
2288 |
|
2289 |
|
2290 |
|
2291 |
|
2292 |
|
2293 |
|
2294 |
|
2295 |
|
2296 |
|
2297 |
|
2298 | function Resultset(collection, options) {
|
2299 | options = options || {};
|
2300 |
|
2301 | options.queryObj = options.queryObj || null;
|
2302 | options.queryFunc = options.queryFunc || null;
|
2303 | options.firstOnly = options.firstOnly || false;
|
2304 |
|
2305 |
|
2306 | this.collection = collection;
|
2307 |
|
2308 |
|
2309 | this.searchIsChained = (!options.queryObj && !options.queryFunc);
|
2310 | this.filteredrows = [];
|
2311 | this.filterInitialized = false;
|
2312 |
|
2313 |
|
2314 | if (typeof (options.queryObj) !== "undefined" && options.queryObj !== null) {
|
2315 | return this.find(options.queryObj, options.firstOnly);
|
2316 | }
|
2317 | if (typeof (options.queryFunc) !== "undefined" && options.queryFunc !== null) {
|
2318 | return this.where(options.queryFunc);
|
2319 | }
|
2320 |
|
2321 |
|
2322 | return this;
|
2323 | }
|
2324 |
|
2325 | |
2326 |
|
2327 |
|
2328 |
|
2329 |
|
2330 | Resultset.prototype.reset = function () {
|
2331 | if (this.filteredrows.length > 0) {
|
2332 | this.filteredrows = [];
|
2333 | }
|
2334 | this.filterInitialized = false;
|
2335 | return this;
|
2336 | };
|
2337 |
|
2338 | |
2339 |
|
2340 |
|
2341 |
|
2342 | Resultset.prototype.toJSON = function () {
|
2343 | var copy = this.copy();
|
2344 | copy.collection = null;
|
2345 | return copy;
|
2346 | };
|
2347 |
|
2348 | |
2349 |
|
2350 |
|
2351 |
|
2352 |
|
2353 |
|
2354 |
|
2355 |
|
2356 | Resultset.prototype.limit = function (qty) {
|
2357 |
|
2358 | if (this.searchIsChained && !this.filterInitialized && this.filteredrows.length === 0) {
|
2359 | this.filteredrows = this.collection.prepareFullDocIndex();
|
2360 | }
|
2361 |
|
2362 | var rscopy = new Resultset(this.collection);
|
2363 | rscopy.filteredrows = this.filteredrows.slice(0, qty);
|
2364 | rscopy.filterInitialized = true;
|
2365 | return rscopy;
|
2366 | };
|
2367 |
|
2368 | |
2369 |
|
2370 |
|
2371 |
|
2372 |
|
2373 |
|
2374 |
|
2375 | Resultset.prototype.offset = function (pos) {
|
2376 |
|
2377 | if (this.searchIsChained && !this.filterInitialized && this.filteredrows.length === 0) {
|
2378 | this.filteredrows = this.collection.prepareFullDocIndex();
|
2379 | }
|
2380 |
|
2381 | var rscopy = new Resultset(this.collection);
|
2382 | rscopy.filteredrows = this.filteredrows.slice(pos);
|
2383 | rscopy.filterInitialized = true;
|
2384 | return rscopy;
|
2385 | };
|
2386 |
|
2387 | |
2388 |
|
2389 |
|
2390 |
|
2391 |
|
2392 |
|
2393 | Resultset.prototype.copy = function () {
|
2394 | var result = new Resultset(this.collection);
|
2395 |
|
2396 | if (this.filteredrows.length > 0) {
|
2397 | result.filteredrows = this.filteredrows.slice();
|
2398 | }
|
2399 | result.filterInitialized = this.filterInitialized;
|
2400 |
|
2401 | return result;
|
2402 | };
|
2403 |
|
2404 | |
2405 |
|
2406 |
|
2407 |
|
2408 | Resultset.prototype.branch = Resultset.prototype.copy;
|
2409 |
|
2410 | |
2411 |
|
2412 |
|
2413 |
|
2414 |
|
2415 |
|
2416 |
|
2417 |
|
2418 | Resultset.prototype.transform = function (transform, parameters) {
|
2419 | var idx,
|
2420 | step,
|
2421 | rs = this;
|
2422 |
|
2423 |
|
2424 | if (typeof transform === 'string') {
|
2425 | if (this.collection.transforms.hasOwnProperty(transform)) {
|
2426 | transform = this.collection.transforms[transform];
|
2427 | }
|
2428 | }
|
2429 |
|
2430 |
|
2431 | if (typeof transform !== 'object' || !Array.isArray(transform)) {
|
2432 | throw new Error("Invalid transform");
|
2433 | }
|
2434 |
|
2435 | if (typeof parameters !== 'undefined') {
|
2436 | transform = Utils.resolveTransformParams(transform, parameters);
|
2437 | }
|
2438 |
|
2439 | for (idx = 0; idx < transform.length; idx++) {
|
2440 | step = transform[idx];
|
2441 |
|
2442 | switch (step.type) {
|
2443 | case "find":
|
2444 | rs.find(step.value);
|
2445 | break;
|
2446 | case "where":
|
2447 | rs.where(step.value);
|
2448 | break;
|
2449 | case "simplesort":
|
2450 | rs.simplesort(step.property, step.desc);
|
2451 | break;
|
2452 | case "compoundsort":
|
2453 | rs.compoundsort(step.value);
|
2454 | break;
|
2455 | case "sort":
|
2456 | rs.sort(step.value);
|
2457 | break;
|
2458 | case "limit":
|
2459 | rs = rs.limit(step.value);
|
2460 | break;
|
2461 | case "offset":
|
2462 | rs = rs.offset(step.value);
|
2463 | break;
|
2464 | case "map":
|
2465 | rs = rs.map(step.value);
|
2466 | break;
|
2467 | case "eqJoin":
|
2468 | rs = rs.eqJoin(step.joinData, step.leftJoinKey, step.rightJoinKey, step.mapFun);
|
2469 | break;
|
2470 |
|
2471 | case "mapReduce":
|
2472 | rs = rs.mapReduce(step.mapFunction, step.reduceFunction);
|
2473 | break;
|
2474 |
|
2475 | case "update":
|
2476 | rs.update(step.value);
|
2477 | break;
|
2478 | case "remove":
|
2479 | rs.remove();
|
2480 | break;
|
2481 | default:
|
2482 | break;
|
2483 | }
|
2484 | }
|
2485 |
|
2486 | return rs;
|
2487 | };
|
2488 |
|
2489 | |
2490 |
|
2491 |
|
2492 |
|
2493 |
|
2494 |
|
2495 |
|
2496 |
|
2497 |
|
2498 |
|
2499 |
|
2500 |
|
2501 |
|
2502 | Resultset.prototype.sort = function (comparefun) {
|
2503 |
|
2504 | if (this.searchIsChained && !this.filterInitialized && this.filteredrows.length === 0) {
|
2505 | this.filteredrows = this.collection.prepareFullDocIndex();
|
2506 | }
|
2507 |
|
2508 | var wrappedComparer =
|
2509 | (function (userComparer, data) {
|
2510 | return function (a, b) {
|
2511 | return userComparer(data[a], data[b]);
|
2512 | };
|
2513 | })(comparefun, this.collection.data);
|
2514 |
|
2515 | this.filteredrows.sort(wrappedComparer);
|
2516 |
|
2517 | return this;
|
2518 | };
|
2519 |
|
2520 | |
2521 |
|
2522 |
|
2523 |
|
2524 |
|
2525 |
|
2526 |
|
2527 |
|
2528 |
|
2529 | Resultset.prototype.simplesort = function (propname, isdesc) {
|
2530 |
|
2531 | if (this.searchIsChained && !this.filterInitialized && this.filteredrows.length === 0) {
|
2532 | this.filteredrows = this.collection.prepareFullDocIndex();
|
2533 | }
|
2534 |
|
2535 | if (typeof (isdesc) === 'undefined') {
|
2536 | isdesc = false;
|
2537 | }
|
2538 |
|
2539 | var wrappedComparer =
|
2540 | (function (prop, desc, data) {
|
2541 | return function (a, b) {
|
2542 | return sortHelper(data[a][prop], data[b][prop], desc);
|
2543 | };
|
2544 | })(propname, isdesc, this.collection.data);
|
2545 |
|
2546 | this.filteredrows.sort(wrappedComparer);
|
2547 |
|
2548 | return this;
|
2549 | };
|
2550 |
|
2551 | |
2552 |
|
2553 |
|
2554 |
|
2555 |
|
2556 |
|
2557 |
|
2558 |
|
2559 |
|
2560 |
|
2561 |
|
2562 |
|
2563 | Resultset.prototype.compoundsort = function (properties) {
|
2564 | if (properties.length === 0) {
|
2565 | throw new Error("Invalid call to compoundsort, need at least one property");
|
2566 | }
|
2567 |
|
2568 | var prop;
|
2569 | if (properties.length === 1) {
|
2570 | prop = properties[0];
|
2571 | if (Array.isArray(prop)) {
|
2572 | return this.simplesort(prop[0], prop[1]);
|
2573 | }
|
2574 | return this.simplesort(prop, false);
|
2575 | }
|
2576 |
|
2577 |
|
2578 | for (var i = 0, len = properties.length; i < len; i += 1) {
|
2579 | prop = properties[i];
|
2580 | if (!Array.isArray(prop)) {
|
2581 | properties[i] = [prop, false];
|
2582 | }
|
2583 | }
|
2584 |
|
2585 |
|
2586 | if (this.searchIsChained && !this.filterInitialized && this.filteredrows.length === 0) {
|
2587 | this.filteredrows = this.collection.prepareFullDocIndex();
|
2588 | }
|
2589 |
|
2590 | var wrappedComparer =
|
2591 | (function (props, data) {
|
2592 | return function (a, b) {
|
2593 | return compoundeval(props, data[a], data[b]);
|
2594 | };
|
2595 | })(properties, this.collection.data);
|
2596 |
|
2597 | this.filteredrows.sort(wrappedComparer);
|
2598 |
|
2599 | return this;
|
2600 | };
|
2601 |
|
2602 | |
2603 |
|
2604 |
|
2605 |
|
2606 |
|
2607 |
|
2608 |
|
2609 |
|
2610 |
|
2611 | Resultset.prototype.findOr = function (expressionArray) {
|
2612 | var fr = null,
|
2613 | fri = 0,
|
2614 | frlen = 0,
|
2615 | docset = [],
|
2616 | idxset = [],
|
2617 | idx = 0,
|
2618 | origCount = this.count();
|
2619 |
|
2620 |
|
2621 |
|
2622 | for (var ei = 0, elen = expressionArray.length; ei < elen; ei++) {
|
2623 |
|
2624 | fr = this.branch().find(expressionArray[ei]).filteredrows;
|
2625 | frlen = fr.length;
|
2626 |
|
2627 | if (frlen === origCount) {
|
2628 | return this;
|
2629 | }
|
2630 |
|
2631 |
|
2632 | for (fri = 0; fri < frlen; fri++) {
|
2633 | idx = fr[fri];
|
2634 | if (idxset[idx] === undefined) {
|
2635 | idxset[idx] = true;
|
2636 | docset.push(idx);
|
2637 | }
|
2638 | }
|
2639 | }
|
2640 |
|
2641 | this.filteredrows = docset;
|
2642 | this.filterInitialized = true;
|
2643 |
|
2644 | return this;
|
2645 | };
|
2646 | Resultset.prototype.$or = Resultset.prototype.findOr;
|
2647 |
|
2648 | |
2649 |
|
2650 |
|
2651 |
|
2652 |
|
2653 |
|
2654 |
|
2655 |
|
2656 |
|
2657 | Resultset.prototype.findAnd = function (expressionArray) {
|
2658 |
|
2659 |
|
2660 | for (var i = 0, len = expressionArray.length; i < len; i++) {
|
2661 | if (this.count() === 0) {
|
2662 | return this;
|
2663 | }
|
2664 | this.find(expressionArray[i]);
|
2665 | }
|
2666 | return this;
|
2667 | };
|
2668 | Resultset.prototype.$and = Resultset.prototype.findAnd;
|
2669 |
|
2670 | |
2671 |
|
2672 |
|
2673 |
|
2674 |
|
2675 |
|
2676 |
|
2677 |
|
2678 | Resultset.prototype.find = function (query, firstOnly) {
|
2679 | if (this.collection.data.length === 0) {
|
2680 | if (this.searchIsChained) {
|
2681 | this.filteredrows = [];
|
2682 | this.filterInitialized = true;
|
2683 | return this;
|
2684 | }
|
2685 | return [];
|
2686 | }
|
2687 |
|
2688 | var queryObject = query || 'getAll',
|
2689 | p,
|
2690 | property,
|
2691 | queryObjectOp,
|
2692 | operator,
|
2693 | value,
|
2694 | key,
|
2695 | searchByIndex = false,
|
2696 | result = [],
|
2697 | index = null;
|
2698 |
|
2699 |
|
2700 | firstOnly = firstOnly || false;
|
2701 |
|
2702 | if (typeof queryObject === 'object') {
|
2703 | for (p in queryObject) {
|
2704 | if (hasOwnProperty.call(queryObject, p)) {
|
2705 | property = p;
|
2706 | queryObjectOp = queryObject[p];
|
2707 | break;
|
2708 | }
|
2709 | }
|
2710 | }
|
2711 |
|
2712 |
|
2713 | if (!property || queryObject === 'getAll') {
|
2714 |
|
2715 |
|
2716 | if (firstOnly) {
|
2717 | return (this.collection.data.length > 0)?this.collection.data[0]: null;
|
2718 | }
|
2719 |
|
2720 | return (this.searchIsChained) ? (this) : (this.collection.data.slice());
|
2721 | }
|
2722 |
|
2723 |
|
2724 | if (property === '$and' || property === '$or') {
|
2725 | if (this.searchIsChained) {
|
2726 | this[property](queryObjectOp);
|
2727 |
|
2728 |
|
2729 | if (firstOnly && this.filteredrows.length > 1) {
|
2730 | this.filteredrows = this.filteredrows.slice(0, 1);
|
2731 | }
|
2732 |
|
2733 | return this;
|
2734 | } else {
|
2735 |
|
2736 | result = this.collection.chain()[property](queryObjectOp).data();
|
2737 |
|
2738 |
|
2739 |
|
2740 |
|
2741 | if (firstOnly) {
|
2742 | return (result.length === 0) ? ([]) : (result[0]);
|
2743 | }
|
2744 |
|
2745 |
|
2746 | return result;
|
2747 | }
|
2748 | }
|
2749 |
|
2750 |
|
2751 | if (queryObjectOp === null || (typeof queryObjectOp !== 'object' || queryObjectOp instanceof Date)) {
|
2752 | operator = '$eq';
|
2753 | value = queryObjectOp;
|
2754 | } else if (typeof queryObjectOp === 'object') {
|
2755 | for (key in queryObjectOp) {
|
2756 | if (hasOwnProperty.call(queryObjectOp, key)) {
|
2757 | operator = key;
|
2758 | value = queryObjectOp[key];
|
2759 | break;
|
2760 | }
|
2761 | }
|
2762 | } else {
|
2763 | throw new Error('Do not know what you want to do.');
|
2764 | }
|
2765 |
|
2766 |
|
2767 | if (operator === '$regex') {
|
2768 | if (Array.isArray(value)) {
|
2769 | value = new RegExp(value[0], value[1]);
|
2770 | } else if (!(value instanceof RegExp)) {
|
2771 | value = new RegExp(value);
|
2772 | }
|
2773 | }
|
2774 |
|
2775 |
|
2776 | var usingDotNotation = (property.indexOf('.') !== -1);
|
2777 |
|
2778 |
|
2779 |
|
2780 |
|
2781 | var doIndexCheck = !usingDotNotation &&
|
2782 | (!this.searchIsChained || !this.filterInitialized);
|
2783 |
|
2784 | if (doIndexCheck && this.collection.binaryIndices[property] &&
|
2785 | indexedOpsList.indexOf(operator) !== -1) {
|
2786 |
|
2787 |
|
2788 |
|
2789 |
|
2790 | if (this.collection.adaptiveBinaryIndices !== true) {
|
2791 | this.collection.ensureIndex(property);
|
2792 | }
|
2793 |
|
2794 | searchByIndex = true;
|
2795 | index = this.collection.binaryIndices[property];
|
2796 | }
|
2797 |
|
2798 |
|
2799 | var fun = LokiOps[operator];
|
2800 |
|
2801 |
|
2802 | var t = this.collection.data;
|
2803 |
|
2804 | var i = 0,
|
2805 | len = 0;
|
2806 |
|
2807 |
|
2808 |
|
2809 |
|
2810 |
|
2811 |
|
2812 |
|
2813 |
|
2814 |
|
2815 | if (!this.searchIsChained) {
|
2816 | if (!searchByIndex) {
|
2817 | i = t.length;
|
2818 |
|
2819 | if (firstOnly) {
|
2820 | if (usingDotNotation) {
|
2821 | property = property.split('.');
|
2822 | while (i--) {
|
2823 | if (dotSubScan(t[i], property, fun, value)) {
|
2824 | return (t[i]);
|
2825 | }
|
2826 | }
|
2827 | } else {
|
2828 | while (i--) {
|
2829 | if (fun(t[i][property], value)) {
|
2830 | return (t[i]);
|
2831 | }
|
2832 | }
|
2833 | }
|
2834 |
|
2835 | return [];
|
2836 | }
|
2837 |
|
2838 |
|
2839 |
|
2840 | if (usingDotNotation) {
|
2841 | property = property.split('.');
|
2842 | while (i--) {
|
2843 | if (dotSubScan(t[i], property, fun, value)) {
|
2844 | result.push(t[i]);
|
2845 | }
|
2846 | }
|
2847 | } else {
|
2848 | while (i--) {
|
2849 | if (fun(t[i][property], value)) {
|
2850 | result.push(t[i]);
|
2851 | }
|
2852 | }
|
2853 | }
|
2854 | } else {
|
2855 |
|
2856 | var seg = this.collection.calculateRange(operator, property, value);
|
2857 |
|
2858 |
|
2859 |
|
2860 | if (firstOnly) {
|
2861 | if (seg[1] !== -1) {
|
2862 | return t[index.values[seg[0]]];
|
2863 | }
|
2864 | return [];
|
2865 | }
|
2866 |
|
2867 | if (operator !== '$in') {
|
2868 | for (i = seg[0]; i <= seg[1]; i++) {
|
2869 | result.push(t[index.values[i]]);
|
2870 | }
|
2871 | } else {
|
2872 | for (i = 0, len = seg.length; i < len; i++) {
|
2873 | result.push(t[index.values[seg[i]]]);
|
2874 | }
|
2875 | }
|
2876 | }
|
2877 |
|
2878 |
|
2879 | return result;
|
2880 | }
|
2881 |
|
2882 |
|
2883 |
|
2884 |
|
2885 | var filter, rowIdx = 0;
|
2886 |
|
2887 |
|
2888 | if (this.filterInitialized) {
|
2889 | filter = this.filteredrows;
|
2890 | i = filter.length;
|
2891 |
|
2892 |
|
2893 | if (usingDotNotation) {
|
2894 | property = property.split('.');
|
2895 | while (i--) {
|
2896 | rowIdx = filter[i];
|
2897 | if (dotSubScan(t[rowIdx], property, fun, value)) {
|
2898 | result.push(rowIdx);
|
2899 | }
|
2900 | }
|
2901 | } else {
|
2902 | while (i--) {
|
2903 | rowIdx = filter[i];
|
2904 | if (fun(t[rowIdx][property], value)) {
|
2905 | result.push(rowIdx);
|
2906 | }
|
2907 | }
|
2908 | }
|
2909 | }
|
2910 |
|
2911 | else {
|
2912 |
|
2913 | if (!searchByIndex) {
|
2914 | i = t.length;
|
2915 |
|
2916 | if (usingDotNotation) {
|
2917 | property = property.split('.');
|
2918 | while (i--) {
|
2919 | if (dotSubScan(t[i], property, fun, value)) {
|
2920 | result.push(i);
|
2921 | }
|
2922 | }
|
2923 | } else {
|
2924 | while (i--) {
|
2925 | if (fun(t[i][property], value)) {
|
2926 | result.push(i);
|
2927 | }
|
2928 | }
|
2929 | }
|
2930 | } else {
|
2931 |
|
2932 | var segm = this.collection.calculateRange(operator, property, value);
|
2933 |
|
2934 | if (operator !== '$in') {
|
2935 | for (i = segm[0]; i <= segm[1]; i++) {
|
2936 | result.push(index.values[i]);
|
2937 | }
|
2938 | } else {
|
2939 | for (i = 0, len = segm.length; i < len; i++) {
|
2940 | result.push(index.values[segm[i]]);
|
2941 | }
|
2942 | }
|
2943 | }
|
2944 |
|
2945 | this.filterInitialized = true;
|
2946 | }
|
2947 |
|
2948 | this.filteredrows = result;
|
2949 | return this;
|
2950 | };
|
2951 |
|
2952 |
|
2953 | |
2954 |
|
2955 |
|
2956 |
|
2957 |
|
2958 |
|
2959 |
|
2960 | Resultset.prototype.where = function (fun) {
|
2961 | var viewFunction,
|
2962 | result = [];
|
2963 |
|
2964 | if ('function' === typeof fun) {
|
2965 | viewFunction = fun;
|
2966 | } else {
|
2967 | throw new TypeError('Argument is not a stored view or a function');
|
2968 | }
|
2969 | try {
|
2970 |
|
2971 | if (!this.searchIsChained) {
|
2972 | var i = this.collection.data.length;
|
2973 |
|
2974 | while (i--) {
|
2975 | if (viewFunction(this.collection.data[i]) === true) {
|
2976 | result.push(this.collection.data[i]);
|
2977 | }
|
2978 | }
|
2979 |
|
2980 |
|
2981 | return result;
|
2982 | }
|
2983 |
|
2984 | else {
|
2985 |
|
2986 | if (this.filterInitialized) {
|
2987 | var j = this.filteredrows.length;
|
2988 |
|
2989 | while (j--) {
|
2990 | if (viewFunction(this.collection.data[this.filteredrows[j]]) === true) {
|
2991 | result.push(this.filteredrows[j]);
|
2992 | }
|
2993 | }
|
2994 |
|
2995 | this.filteredrows = result;
|
2996 |
|
2997 | return this;
|
2998 | }
|
2999 |
|
3000 | else {
|
3001 | var k = this.collection.data.length;
|
3002 |
|
3003 | while (k--) {
|
3004 | if (viewFunction(this.collection.data[k]) === true) {
|
3005 | result.push(k);
|
3006 | }
|
3007 | }
|
3008 |
|
3009 | this.filteredrows = result;
|
3010 | this.filterInitialized = true;
|
3011 |
|
3012 | return this;
|
3013 | }
|
3014 | }
|
3015 | } catch (err) {
|
3016 | throw err;
|
3017 | }
|
3018 | };
|
3019 |
|
3020 | |
3021 |
|
3022 |
|
3023 |
|
3024 |
|
3025 |
|
3026 | Resultset.prototype.count = function () {
|
3027 | if (this.searchIsChained && this.filterInitialized) {
|
3028 | return this.filteredrows.length;
|
3029 | }
|
3030 | return this.collection.count();
|
3031 | };
|
3032 |
|
3033 | |
3034 |
|
3035 |
|
3036 |
|
3037 |
|
3038 |
|
3039 |
|
3040 |
|
3041 |
|
3042 |
|
3043 |
|
3044 |
|
3045 | Resultset.prototype.data = function (options) {
|
3046 | var result = [],
|
3047 | data = this.collection.data,
|
3048 | len,
|
3049 | i,
|
3050 | method;
|
3051 |
|
3052 | options = options || {};
|
3053 |
|
3054 |
|
3055 | if (this.searchIsChained && !this.filterInitialized) {
|
3056 | if (this.filteredrows.length === 0) {
|
3057 |
|
3058 | if (this.collection.cloneObjects || options.forceClones) {
|
3059 | len = data.length;
|
3060 | method = options.forceCloneMethod || this.collection.cloneMethod;
|
3061 |
|
3062 | for (i = 0; i < len; i++) {
|
3063 | result.push(clone(data[i], method));
|
3064 | }
|
3065 | return result;
|
3066 | }
|
3067 |
|
3068 | else {
|
3069 | return data.slice();
|
3070 | }
|
3071 | } else {
|
3072 |
|
3073 | this.filterInitialized = true;
|
3074 | }
|
3075 | }
|
3076 |
|
3077 | var fr = this.filteredrows;
|
3078 | len = fr.length;
|
3079 |
|
3080 | if (this.collection.cloneObjects || options.forceClones) {
|
3081 | method = options.forceCloneMethod || this.collection.cloneMethod;
|
3082 | for (i = 0; i < len; i++) {
|
3083 | result.push(clone(data[fr[i]], method));
|
3084 | }
|
3085 | } else {
|
3086 | for (i = 0; i < len; i++) {
|
3087 | result.push(data[fr[i]]);
|
3088 | }
|
3089 | }
|
3090 | return result;
|
3091 | };
|
3092 |
|
3093 | |
3094 |
|
3095 |
|
3096 |
|
3097 |
|
3098 |
|
3099 |
|
3100 | Resultset.prototype.update = function (updateFunction) {
|
3101 |
|
3102 | if (typeof (updateFunction) !== "function") {
|
3103 | throw new TypeError('Argument is not a function');
|
3104 | }
|
3105 |
|
3106 |
|
3107 | if (this.searchIsChained && !this.filterInitialized && this.filteredrows.length === 0) {
|
3108 | this.filteredrows = this.collection.prepareFullDocIndex();
|
3109 | }
|
3110 |
|
3111 | var len = this.filteredrows.length,
|
3112 | rcd = this.collection.data;
|
3113 |
|
3114 | for (var idx = 0; idx < len; idx++) {
|
3115 |
|
3116 | updateFunction(rcd[this.filteredrows[idx]]);
|
3117 |
|
3118 |
|
3119 | this.collection.update(rcd[this.filteredrows[idx]]);
|
3120 | }
|
3121 |
|
3122 | return this;
|
3123 | };
|
3124 |
|
3125 | |
3126 |
|
3127 |
|
3128 |
|
3129 |
|
3130 |
|
3131 | Resultset.prototype.remove = function () {
|
3132 |
|
3133 |
|
3134 | if (this.searchIsChained && !this.filterInitialized && this.filteredrows.length === 0) {
|
3135 | this.filteredrows = this.collection.prepareFullDocIndex();
|
3136 | }
|
3137 |
|
3138 | this.collection.remove(this.data());
|
3139 |
|
3140 | this.filteredrows = [];
|
3141 |
|
3142 | return this;
|
3143 | };
|
3144 |
|
3145 | |
3146 |
|
3147 |
|
3148 |
|
3149 |
|
3150 |
|
3151 |
|
3152 |
|
3153 | Resultset.prototype.mapReduce = function (mapFunction, reduceFunction) {
|
3154 | try {
|
3155 | return reduceFunction(this.data().map(mapFunction));
|
3156 | } catch (err) {
|
3157 | throw err;
|
3158 | }
|
3159 | };
|
3160 |
|
3161 | |
3162 |
|
3163 |
|
3164 |
|
3165 |
|
3166 |
|
3167 |
|
3168 |
|
3169 |
|
3170 |
|
3171 | Resultset.prototype.eqJoin = function (joinData, leftJoinKey, rightJoinKey, mapFun) {
|
3172 |
|
3173 | var leftData = [],
|
3174 | leftDataLength,
|
3175 | rightData = [],
|
3176 | rightDataLength,
|
3177 | key,
|
3178 | result = [],
|
3179 | leftKeyisFunction = typeof leftJoinKey === 'function',
|
3180 | rightKeyisFunction = typeof rightJoinKey === 'function',
|
3181 | joinMap = {};
|
3182 |
|
3183 |
|
3184 | leftData = this.data();
|
3185 | leftDataLength = leftData.length;
|
3186 |
|
3187 |
|
3188 | if (joinData instanceof Resultset) {
|
3189 | rightData = joinData.data();
|
3190 | } else if (Array.isArray(joinData)) {
|
3191 | rightData = joinData;
|
3192 | } else {
|
3193 | throw new TypeError('joinData needs to be an array or result set');
|
3194 | }
|
3195 | rightDataLength = rightData.length;
|
3196 |
|
3197 |
|
3198 |
|
3199 | for (var i = 0; i < rightDataLength; i++) {
|
3200 | key = rightKeyisFunction ? rightJoinKey(rightData[i]) : rightData[i][rightJoinKey];
|
3201 | joinMap[key] = rightData[i];
|
3202 | }
|
3203 |
|
3204 | if (!mapFun) {
|
3205 | mapFun = function (left, right) {
|
3206 | return {
|
3207 | left: left,
|
3208 | right: right
|
3209 | };
|
3210 | };
|
3211 | }
|
3212 |
|
3213 |
|
3214 | for (var j = 0; j < leftDataLength; j++) {
|
3215 | key = leftKeyisFunction ? leftJoinKey(leftData[j]) : leftData[j][leftJoinKey];
|
3216 | result.push(mapFun(leftData[j], joinMap[key] || {}));
|
3217 | }
|
3218 |
|
3219 |
|
3220 | this.collection = new Collection('joinData');
|
3221 | this.collection.insert(result);
|
3222 | this.filteredrows = [];
|
3223 | this.filterInitialized = false;
|
3224 |
|
3225 | return this;
|
3226 | };
|
3227 |
|
3228 | Resultset.prototype.map = function (mapFun) {
|
3229 | var data = this.data().map(mapFun);
|
3230 |
|
3231 | this.collection = new Collection('mappedData');
|
3232 | this.collection.insert(data);
|
3233 | this.filteredrows = [];
|
3234 | this.filterInitialized = false;
|
3235 |
|
3236 | return this;
|
3237 | };
|
3238 |
|
3239 | |
3240 |
|
3241 |
|
3242 |
|
3243 |
|
3244 |
|
3245 |
|
3246 |
|
3247 |
|
3248 |
|
3249 |
|
3250 |
|
3251 |
|
3252 |
|
3253 |
|
3254 |
|
3255 |
|
3256 |
|
3257 |
|
3258 |
|
3259 |
|
3260 | function DynamicView(collection, name, options) {
|
3261 | this.collection = collection;
|
3262 | this.name = name;
|
3263 | this.rebuildPending = false;
|
3264 | this.options = options || {};
|
3265 |
|
3266 | if (!this.options.hasOwnProperty('persistent')) {
|
3267 | this.options.persistent = false;
|
3268 | }
|
3269 |
|
3270 |
|
3271 |
|
3272 |
|
3273 | if (!this.options.hasOwnProperty('sortPriority')) {
|
3274 | this.options.sortPriority = 'passive';
|
3275 | }
|
3276 |
|
3277 | if (!this.options.hasOwnProperty('minRebuildInterval')) {
|
3278 | this.options.minRebuildInterval = 1;
|
3279 | }
|
3280 |
|
3281 | this.resultset = new Resultset(collection);
|
3282 | this.resultdata = [];
|
3283 | this.resultsdirty = false;
|
3284 |
|
3285 | this.cachedresultset = null;
|
3286 |
|
3287 |
|
3288 | this.filterPipeline = [];
|
3289 |
|
3290 |
|
3291 |
|
3292 | this.sortFunction = null;
|
3293 | this.sortCriteria = null;
|
3294 | this.sortDirty = false;
|
3295 |
|
3296 |
|
3297 |
|
3298 |
|
3299 | this.events = {
|
3300 | 'rebuild': []
|
3301 | };
|
3302 | }
|
3303 |
|
3304 | DynamicView.prototype = new LokiEventEmitter();
|
3305 |
|
3306 |
|
3307 | |
3308 |
|
3309 |
|
3310 |
|
3311 |
|
3312 |
|
3313 |
|
3314 |
|
3315 |
|
3316 |
|
3317 |
|
3318 | DynamicView.prototype.rematerialize = function (options) {
|
3319 | var fpl,
|
3320 | fpi,
|
3321 | idx;
|
3322 |
|
3323 | options = options || {};
|
3324 |
|
3325 | this.resultdata = [];
|
3326 | this.resultsdirty = true;
|
3327 | this.resultset = new Resultset(this.collection);
|
3328 |
|
3329 | if (this.sortFunction || this.sortCriteria) {
|
3330 | this.sortDirty = true;
|
3331 | }
|
3332 |
|
3333 | if (options.hasOwnProperty('removeWhereFilters')) {
|
3334 |
|
3335 |
|
3336 | fpl = this.filterPipeline.length;
|
3337 | fpi = fpl;
|
3338 | while (fpi--) {
|
3339 | if (this.filterPipeline[fpi].type === 'where') {
|
3340 | if (fpi !== this.filterPipeline.length - 1) {
|
3341 | this.filterPipeline[fpi] = this.filterPipeline[this.filterPipeline.length - 1];
|
3342 | }
|
3343 |
|
3344 | this.filterPipeline.length--;
|
3345 | }
|
3346 | }
|
3347 | }
|
3348 |
|
3349 |
|
3350 | var ofp = this.filterPipeline;
|
3351 | this.filterPipeline = [];
|
3352 |
|
3353 |
|
3354 | fpl = ofp.length;
|
3355 | for (idx = 0; idx < fpl; idx++) {
|
3356 | this.applyFind(ofp[idx].val);
|
3357 | }
|
3358 |
|
3359 |
|
3360 | this.data();
|
3361 |
|
3362 |
|
3363 | this.emit('rebuild', this);
|
3364 |
|
3365 | return this;
|
3366 | };
|
3367 |
|
3368 | |
3369 |
|
3370 |
|
3371 |
|
3372 |
|
3373 |
|
3374 |
|
3375 |
|
3376 |
|
3377 |
|
3378 | DynamicView.prototype.branchResultset = function (transform, parameters) {
|
3379 | var rs = this.resultset.branch();
|
3380 |
|
3381 | if (typeof transform === 'undefined') {
|
3382 | return rs;
|
3383 | }
|
3384 |
|
3385 | return rs.transform(transform, parameters);
|
3386 | };
|
3387 |
|
3388 | |
3389 |
|
3390 |
|
3391 |
|
3392 | DynamicView.prototype.toJSON = function () {
|
3393 | var copy = new DynamicView(this.collection, this.name, this.options);
|
3394 |
|
3395 | copy.resultset = this.resultset;
|
3396 | copy.resultdata = [];
|
3397 | copy.resultsdirty = true;
|
3398 | copy.filterPipeline = this.filterPipeline;
|
3399 | copy.sortFunction = this.sortFunction;
|
3400 | copy.sortCriteria = this.sortCriteria;
|
3401 | copy.sortDirty = this.sortDirty;
|
3402 |
|
3403 |
|
3404 | copy.collection = null;
|
3405 |
|
3406 | return copy;
|
3407 | };
|
3408 |
|
3409 | |
3410 |
|
3411 |
|
3412 |
|
3413 |
|
3414 | DynamicView.prototype.removeFilters = function () {
|
3415 | this.rebuildPending = false;
|
3416 | this.resultset.reset();
|
3417 | this.resultdata = [];
|
3418 | this.resultsdirty = false;
|
3419 |
|
3420 | this.cachedresultset = null;
|
3421 |
|
3422 |
|
3423 | this.filterPipeline = [];
|
3424 |
|
3425 |
|
3426 |
|
3427 | this.sortFunction = null;
|
3428 | this.sortCriteria = null;
|
3429 | this.sortDirty = false;
|
3430 | };
|
3431 |
|
3432 | |
3433 |
|
3434 |
|
3435 |
|
3436 |
|
3437 |
|
3438 |
|
3439 |
|
3440 |
|
3441 |
|
3442 |
|
3443 |
|
3444 |
|
3445 | DynamicView.prototype.applySort = function (comparefun) {
|
3446 | this.sortFunction = comparefun;
|
3447 | this.sortCriteria = null;
|
3448 |
|
3449 | this.queueSortPhase();
|
3450 |
|
3451 | return this;
|
3452 | };
|
3453 |
|
3454 | |
3455 |
|
3456 |
|
3457 |
|
3458 |
|
3459 |
|
3460 |
|
3461 |
|
3462 |
|
3463 |
|
3464 | DynamicView.prototype.applySimpleSort = function (propname, isdesc) {
|
3465 | this.sortCriteria = [
|
3466 | [propname, isdesc || false]
|
3467 | ];
|
3468 | this.sortFunction = null;
|
3469 |
|
3470 | this.queueSortPhase();
|
3471 |
|
3472 | return this;
|
3473 | };
|
3474 |
|
3475 | |
3476 |
|
3477 |
|
3478 |
|
3479 |
|
3480 |
|
3481 |
|
3482 |
|
3483 |
|
3484 |
|
3485 |
|
3486 |
|
3487 |
|
3488 |
|
3489 | DynamicView.prototype.applySortCriteria = function (criteria) {
|
3490 | this.sortCriteria = criteria;
|
3491 | this.sortFunction = null;
|
3492 |
|
3493 | this.queueSortPhase();
|
3494 |
|
3495 | return this;
|
3496 | };
|
3497 |
|
3498 | |
3499 |
|
3500 |
|
3501 |
|
3502 |
|
3503 | DynamicView.prototype.startTransaction = function () {
|
3504 | this.cachedresultset = this.resultset.copy();
|
3505 |
|
3506 | return this;
|
3507 | };
|
3508 |
|
3509 | |
3510 |
|
3511 |
|
3512 |
|
3513 |
|
3514 | DynamicView.prototype.commit = function () {
|
3515 | this.cachedresultset = null;
|
3516 |
|
3517 | return this;
|
3518 | };
|
3519 |
|
3520 | |
3521 |
|
3522 |
|
3523 |
|
3524 |
|
3525 | DynamicView.prototype.rollback = function () {
|
3526 | this.resultset = this.cachedresultset;
|
3527 |
|
3528 | if (this.options.persistent) {
|
3529 |
|
3530 |
|
3531 | this.resultdata = this.resultset.data();
|
3532 |
|
3533 | this.emit('rebuild', this);
|
3534 | }
|
3535 |
|
3536 | return this;
|
3537 | };
|
3538 |
|
3539 |
|
3540 | |
3541 |
|
3542 |
|
3543 |
|
3544 |
|
3545 |
|
3546 |
|
3547 | DynamicView.prototype._indexOfFilterWithId = function (uid) {
|
3548 | if (typeof uid === 'string' || typeof uid === 'number') {
|
3549 | for (var idx = 0, len = this.filterPipeline.length; idx < len; idx += 1) {
|
3550 | if (uid === this.filterPipeline[idx].uid) {
|
3551 | return idx;
|
3552 | }
|
3553 | }
|
3554 | }
|
3555 | return -1;
|
3556 | };
|
3557 |
|
3558 | |
3559 |
|
3560 |
|
3561 |
|
3562 |
|
3563 |
|
3564 | DynamicView.prototype._addFilter = function (filter) {
|
3565 | this.filterPipeline.push(filter);
|
3566 | this.resultset[filter.type](filter.val);
|
3567 | };
|
3568 |
|
3569 | |
3570 |
|
3571 |
|
3572 |
|
3573 |
|
3574 | DynamicView.prototype.reapplyFilters = function () {
|
3575 | this.resultset.reset();
|
3576 |
|
3577 | this.cachedresultset = null;
|
3578 | if (this.options.persistent) {
|
3579 | this.resultdata = [];
|
3580 | this.resultsdirty = true;
|
3581 | }
|
3582 |
|
3583 | var filters = this.filterPipeline;
|
3584 | this.filterPipeline = [];
|
3585 |
|
3586 | for (var idx = 0, len = filters.length; idx < len; idx += 1) {
|
3587 | this._addFilter(filters[idx]);
|
3588 | }
|
3589 |
|
3590 | if (this.sortFunction || this.sortCriteria) {
|
3591 | this.queueSortPhase();
|
3592 | } else {
|
3593 | this.queueRebuildEvent();
|
3594 | }
|
3595 |
|
3596 | return this;
|
3597 | };
|
3598 |
|
3599 | |
3600 |
|
3601 |
|
3602 |
|
3603 |
|
3604 |
|
3605 |
|
3606 |
|
3607 | DynamicView.prototype.applyFilter = function (filter) {
|
3608 | var idx = this._indexOfFilterWithId(filter.uid);
|
3609 | if (idx >= 0) {
|
3610 | this.filterPipeline[idx] = filter;
|
3611 | return this.reapplyFilters();
|
3612 | }
|
3613 |
|
3614 | this.cachedresultset = null;
|
3615 | if (this.options.persistent) {
|
3616 | this.resultdata = [];
|
3617 | this.resultsdirty = true;
|
3618 | }
|
3619 |
|
3620 | this._addFilter(filter);
|
3621 |
|
3622 | if (this.sortFunction || this.sortCriteria) {
|
3623 | this.queueSortPhase();
|
3624 | } else {
|
3625 | this.queueRebuildEvent();
|
3626 | }
|
3627 |
|
3628 | return this;
|
3629 | };
|
3630 |
|
3631 | |
3632 |
|
3633 |
|
3634 |
|
3635 |
|
3636 |
|
3637 |
|
3638 |
|
3639 | DynamicView.prototype.applyFind = function (query, uid) {
|
3640 | this.applyFilter({
|
3641 | type: 'find',
|
3642 | val: query,
|
3643 | uid: uid
|
3644 | });
|
3645 | return this;
|
3646 | };
|
3647 |
|
3648 | |
3649 |
|
3650 |
|
3651 |
|
3652 |
|
3653 |
|
3654 |
|
3655 |
|
3656 | DynamicView.prototype.applyWhere = function (fun, uid) {
|
3657 | this.applyFilter({
|
3658 | type: 'where',
|
3659 | val: fun,
|
3660 | uid: uid
|
3661 | });
|
3662 | return this;
|
3663 | };
|
3664 |
|
3665 | |
3666 |
|
3667 |
|
3668 |
|
3669 |
|
3670 |
|
3671 |
|
3672 | DynamicView.prototype.removeFilter = function (uid) {
|
3673 | var idx = this._indexOfFilterWithId(uid);
|
3674 | if (idx < 0) {
|
3675 | throw new Error("Dynamic view does not contain a filter with ID: " + uid);
|
3676 | }
|
3677 |
|
3678 | this.filterPipeline.splice(idx, 1);
|
3679 | this.reapplyFilters();
|
3680 | return this;
|
3681 | };
|
3682 |
|
3683 | |
3684 |
|
3685 |
|
3686 |
|
3687 |
|
3688 |
|
3689 | DynamicView.prototype.count = function () {
|
3690 | if (this.options.persistent) {
|
3691 | return this.resultdata.length;
|
3692 | }
|
3693 | return this.resultset.count();
|
3694 | };
|
3695 |
|
3696 | |
3697 |
|
3698 |
|
3699 |
|
3700 |
|
3701 |
|
3702 | DynamicView.prototype.data = function () {
|
3703 |
|
3704 | if (this.sortDirty || this.resultsdirty) {
|
3705 | this.performSortPhase({
|
3706 | suppressRebuildEvent: true
|
3707 | });
|
3708 | }
|
3709 | return (this.options.persistent) ? (this.resultdata) : (this.resultset.data());
|
3710 | };
|
3711 |
|
3712 | |
3713 |
|
3714 |
|
3715 |
|
3716 | DynamicView.prototype.queueRebuildEvent = function () {
|
3717 | if (this.rebuildPending) {
|
3718 | return;
|
3719 | }
|
3720 | this.rebuildPending = true;
|
3721 |
|
3722 | var self = this;
|
3723 | setTimeout(function () {
|
3724 | if (self.rebuildPending) {
|
3725 | self.rebuildPending = false;
|
3726 | self.emit('rebuild', self);
|
3727 | }
|
3728 | }, this.options.minRebuildInterval);
|
3729 | };
|
3730 |
|
3731 | |
3732 |
|
3733 |
|
3734 |
|
3735 |
|
3736 | DynamicView.prototype.queueSortPhase = function () {
|
3737 |
|
3738 | if (this.sortDirty) {
|
3739 | return;
|
3740 | }
|
3741 | this.sortDirty = true;
|
3742 |
|
3743 | var self = this;
|
3744 | if (this.options.sortPriority === "active") {
|
3745 |
|
3746 | setTimeout(function () {
|
3747 | self.performSortPhase();
|
3748 | }, this.options.minRebuildInterval);
|
3749 | } else {
|
3750 |
|
3751 |
|
3752 | this.queueRebuildEvent();
|
3753 | }
|
3754 | };
|
3755 |
|
3756 | |
3757 |
|
3758 |
|
3759 |
|
3760 | DynamicView.prototype.performSortPhase = function (options) {
|
3761 |
|
3762 | if (!this.sortDirty && !this.resultsdirty) {
|
3763 | return;
|
3764 | }
|
3765 |
|
3766 | options = options || {};
|
3767 |
|
3768 | if (this.sortDirty) {
|
3769 | if (this.sortFunction) {
|
3770 | this.resultset.sort(this.sortFunction);
|
3771 | } else if (this.sortCriteria) {
|
3772 | this.resultset.compoundsort(this.sortCriteria);
|
3773 | }
|
3774 |
|
3775 | this.sortDirty = false;
|
3776 | }
|
3777 |
|
3778 | if (this.options.persistent) {
|
3779 |
|
3780 | this.resultdata = this.resultset.data();
|
3781 | this.resultsdirty = false;
|
3782 | }
|
3783 |
|
3784 | if (!options.suppressRebuildEvent) {
|
3785 | this.emit('rebuild', this);
|
3786 | }
|
3787 | };
|
3788 |
|
3789 | |
3790 |
|
3791 |
|
3792 |
|
3793 |
|
3794 |
|
3795 |
|
3796 | DynamicView.prototype.evaluateDocument = function (objIndex, isNew) {
|
3797 |
|
3798 | if (!this.resultset.filterInitialized) {
|
3799 | if (this.options.persistent) {
|
3800 | this.resultdata = this.resultset.data();
|
3801 | }
|
3802 |
|
3803 | if (this.sortFunction || this.sortCriteria) {
|
3804 | this.queueSortPhase();
|
3805 | } else {
|
3806 | this.queueRebuildEvent();
|
3807 | }
|
3808 | return;
|
3809 | }
|
3810 |
|
3811 | var ofr = this.resultset.filteredrows;
|
3812 | var oldPos = (isNew) ? (-1) : (ofr.indexOf(+objIndex));
|
3813 | var oldlen = ofr.length;
|
3814 |
|
3815 |
|
3816 |
|
3817 | var evalResultset = new Resultset(this.collection);
|
3818 | evalResultset.filteredrows = [objIndex];
|
3819 | evalResultset.filterInitialized = true;
|
3820 | var filter;
|
3821 | for (var idx = 0, len = this.filterPipeline.length; idx < len; idx++) {
|
3822 | filter = this.filterPipeline[idx];
|
3823 | evalResultset[filter.type](filter.val);
|
3824 | }
|
3825 |
|
3826 |
|
3827 | var newPos = (evalResultset.filteredrows.length === 0) ? -1 : 0;
|
3828 |
|
3829 |
|
3830 | if (oldPos === -1 && newPos === -1) return;
|
3831 |
|
3832 |
|
3833 | if (oldPos === -1 && newPos !== -1) {
|
3834 | ofr.push(objIndex);
|
3835 |
|
3836 | if (this.options.persistent) {
|
3837 | this.resultdata.push(this.collection.data[objIndex]);
|
3838 | }
|
3839 |
|
3840 |
|
3841 | if (this.sortFunction || this.sortCriteria) {
|
3842 | this.queueSortPhase();
|
3843 | } else {
|
3844 | this.queueRebuildEvent();
|
3845 | }
|
3846 |
|
3847 | return;
|
3848 | }
|
3849 |
|
3850 |
|
3851 | if (oldPos !== -1 && newPos === -1) {
|
3852 | if (oldPos < oldlen - 1) {
|
3853 | ofr.splice(oldPos, 1);
|
3854 |
|
3855 | if (this.options.persistent) {
|
3856 | this.resultdata.splice(oldPos, 1);
|
3857 | }
|
3858 | } else {
|
3859 | ofr.length = oldlen - 1;
|
3860 |
|
3861 | if (this.options.persistent) {
|
3862 | this.resultdata.length = oldlen - 1;
|
3863 | }
|
3864 | }
|
3865 |
|
3866 |
|
3867 | if (this.sortFunction || this.sortCriteria) {
|
3868 | this.queueSortPhase();
|
3869 | } else {
|
3870 | this.queueRebuildEvent();
|
3871 | }
|
3872 |
|
3873 | return;
|
3874 | }
|
3875 |
|
3876 |
|
3877 | if (oldPos !== -1 && newPos !== -1) {
|
3878 | if (this.options.persistent) {
|
3879 |
|
3880 | this.resultdata[oldPos] = this.collection.data[objIndex];
|
3881 | }
|
3882 |
|
3883 |
|
3884 | if (this.sortFunction || this.sortCriteria) {
|
3885 | this.queueSortPhase();
|
3886 | } else {
|
3887 | this.queueRebuildEvent();
|
3888 | }
|
3889 |
|
3890 | return;
|
3891 | }
|
3892 | };
|
3893 |
|
3894 | |
3895 |
|
3896 |
|
3897 | DynamicView.prototype.removeDocument = function (objIndex) {
|
3898 |
|
3899 | if (!this.resultset.filterInitialized) {
|
3900 | if (this.options.persistent) {
|
3901 | this.resultdata = this.resultset.data();
|
3902 | }
|
3903 |
|
3904 | if (this.sortFunction || this.sortCriteria) {
|
3905 | this.queueSortPhase();
|
3906 | } else {
|
3907 | this.queueRebuildEvent();
|
3908 | }
|
3909 | return;
|
3910 | }
|
3911 |
|
3912 | var ofr = this.resultset.filteredrows;
|
3913 | var oldPos = ofr.indexOf(+objIndex);
|
3914 | var oldlen = ofr.length;
|
3915 | var idx;
|
3916 |
|
3917 | if (oldPos !== -1) {
|
3918 |
|
3919 | if (oldPos < oldlen - 1) {
|
3920 | ofr[oldPos] = ofr[oldlen - 1];
|
3921 | ofr.length = oldlen - 1;
|
3922 |
|
3923 | if (this.options.persistent) {
|
3924 | this.resultdata[oldPos] = this.resultdata[oldlen - 1];
|
3925 | this.resultdata.length = oldlen - 1;
|
3926 | }
|
3927 | }
|
3928 |
|
3929 | else {
|
3930 | ofr.length = oldlen - 1;
|
3931 |
|
3932 | if (this.options.persistent) {
|
3933 | this.resultdata.length = oldlen - 1;
|
3934 | }
|
3935 | }
|
3936 |
|
3937 |
|
3938 | if (this.sortFunction || this.sortCriteria) {
|
3939 | this.queueSortPhase();
|
3940 | } else {
|
3941 | this.queueRebuildEvent();
|
3942 | }
|
3943 | }
|
3944 |
|
3945 |
|
3946 |
|
3947 |
|
3948 | oldlen = ofr.length;
|
3949 | for (idx = 0; idx < oldlen; idx++) {
|
3950 | if (ofr[idx] > objIndex) {
|
3951 | ofr[idx]--;
|
3952 | }
|
3953 | }
|
3954 | };
|
3955 |
|
3956 | |
3957 |
|
3958 |
|
3959 |
|
3960 |
|
3961 |
|
3962 |
|
3963 |
|
3964 | DynamicView.prototype.mapReduce = function (mapFunction, reduceFunction) {
|
3965 | try {
|
3966 | return reduceFunction(this.data().map(mapFunction));
|
3967 | } catch (err) {
|
3968 | throw err;
|
3969 | }
|
3970 | };
|
3971 |
|
3972 |
|
3973 | |
3974 |
|
3975 |
|
3976 |
|
3977 |
|
3978 |
|
3979 |
|
3980 |
|
3981 |
|
3982 |
|
3983 |
|
3984 |
|
3985 |
|
3986 |
|
3987 |
|
3988 |
|
3989 |
|
3990 |
|
3991 | function Collection(name, options) {
|
3992 |
|
3993 |
|
3994 | this.name = name;
|
3995 |
|
3996 | this.data = [];
|
3997 | this.idIndex = [];
|
3998 | this.binaryIndices = {};
|
3999 | this.constraints = {
|
4000 | unique: {},
|
4001 | exact: {}
|
4002 | };
|
4003 |
|
4004 |
|
4005 |
|
4006 | this.uniqueNames = [];
|
4007 |
|
4008 |
|
4009 |
|
4010 | this.transforms = {};
|
4011 |
|
4012 |
|
4013 | this.objType = name;
|
4014 |
|
4015 |
|
4016 |
|
4017 |
|
4018 | this.dirty = true;
|
4019 |
|
4020 |
|
4021 | this.cachedIndex = null;
|
4022 | this.cachedBinaryIndex = null;
|
4023 | this.cachedData = null;
|
4024 | var self = this;
|
4025 |
|
4026 |
|
4027 | options = options || {};
|
4028 |
|
4029 |
|
4030 | if (options.hasOwnProperty('unique')) {
|
4031 | if (!Array.isArray(options.unique)) {
|
4032 | options.unique = [options.unique];
|
4033 | }
|
4034 | options.unique.forEach(function (prop) {
|
4035 | self.uniqueNames.push(prop);
|
4036 | self.constraints.unique[prop] = new UniqueIndex(prop);
|
4037 | });
|
4038 | }
|
4039 |
|
4040 | if (options.hasOwnProperty('exact')) {
|
4041 | options.exact.forEach(function (prop) {
|
4042 | self.constraints.exact[prop] = new ExactIndex(prop);
|
4043 | });
|
4044 | }
|
4045 |
|
4046 |
|
4047 |
|
4048 | this.adaptiveBinaryIndices = options.hasOwnProperty('adaptiveBinaryIndices') ? options.adaptiveBinaryIndices : true;
|
4049 |
|
4050 |
|
4051 | this.transactional = options.hasOwnProperty('transactional') ? options.transactional : false;
|
4052 |
|
4053 |
|
4054 | this.cloneObjects = options.hasOwnProperty('clone') ? options.clone : false;
|
4055 |
|
4056 |
|
4057 | this.cloneMethod = options.hasOwnProperty('cloneMethod') ? options.cloneMethod : "parse-stringify";
|
4058 |
|
4059 |
|
4060 | this.asyncListeners = options.hasOwnProperty('asyncListeners') ? options.asyncListeners : false;
|
4061 |
|
4062 |
|
4063 | this.disableChangesApi = options.hasOwnProperty('disableChangesApi') ? options.disableChangesApi : true;
|
4064 |
|
4065 |
|
4066 | this.autoupdate = options.hasOwnProperty('autoupdate') ? options.autoupdate : false;
|
4067 |
|
4068 |
|
4069 | this.ttl = {
|
4070 | age: null,
|
4071 | ttlInterval: null,
|
4072 | daemon: null
|
4073 | };
|
4074 | this.setTTL(options.ttl || -1, options.ttlInterval);
|
4075 |
|
4076 |
|
4077 | this.maxId = 0;
|
4078 |
|
4079 | this.DynamicViews = [];
|
4080 |
|
4081 |
|
4082 | this.events = {
|
4083 | 'insert': [],
|
4084 | 'update': [],
|
4085 | 'pre-insert': [],
|
4086 | 'pre-update': [],
|
4087 | 'close': [],
|
4088 | 'flushbuffer': [],
|
4089 | 'error': [],
|
4090 | 'delete': [],
|
4091 | 'warning': []
|
4092 | };
|
4093 |
|
4094 |
|
4095 | this.changes = [];
|
4096 |
|
4097 |
|
4098 | this.ensureId();
|
4099 | var indices = [];
|
4100 |
|
4101 | if (options && options.indices) {
|
4102 | if (Object.prototype.toString.call(options.indices) === '[object Array]') {
|
4103 | indices = options.indices;
|
4104 | } else if (typeof options.indices === 'string') {
|
4105 | indices = [options.indices];
|
4106 | } else {
|
4107 | throw new TypeError('Indices needs to be a string or an array of strings');
|
4108 | }
|
4109 | }
|
4110 |
|
4111 | for (var idx = 0; idx < indices.length; idx++) {
|
4112 | this.ensureIndex(indices[idx]);
|
4113 | }
|
4114 |
|
4115 | function observerCallback(changes) {
|
4116 |
|
4117 | var changedObjects = typeof Set === 'function' ? new Set() : [];
|
4118 |
|
4119 | if (!changedObjects.add)
|
4120 | changedObjects.add = function (object) {
|
4121 | if (this.indexOf(object) === -1)
|
4122 | this.push(object);
|
4123 | return this;
|
4124 | };
|
4125 |
|
4126 | changes.forEach(function (change) {
|
4127 | changedObjects.add(change.object);
|
4128 | });
|
4129 |
|
4130 | changedObjects.forEach(function (object) {
|
4131 | if (!hasOwnProperty.call(object, '$loki'))
|
4132 | return self.removeAutoUpdateObserver(object);
|
4133 | try {
|
4134 | self.update(object);
|
4135 | } catch (err) {}
|
4136 | });
|
4137 | }
|
4138 |
|
4139 | this.observerCallback = observerCallback;
|
4140 |
|
4141 | |
4142 |
|
4143 |
|
4144 |
|
4145 | function createChange(name, op, obj) {
|
4146 | self.changes.push({
|
4147 | name: name,
|
4148 | operation: op,
|
4149 | obj: JSON.parse(JSON.stringify(obj))
|
4150 | });
|
4151 | }
|
4152 |
|
4153 |
|
4154 | function flushChanges() {
|
4155 | self.changes = [];
|
4156 | }
|
4157 |
|
4158 | this.getChanges = function () {
|
4159 | return self.changes;
|
4160 | };
|
4161 |
|
4162 | this.flushChanges = flushChanges;
|
4163 |
|
4164 | |
4165 |
|
4166 |
|
4167 | function insertMeta(obj) {
|
4168 | if (!obj) {
|
4169 | return;
|
4170 | }
|
4171 | if (!obj.meta) {
|
4172 | obj.meta = {};
|
4173 | }
|
4174 |
|
4175 | obj.meta.created = (new Date()).getTime();
|
4176 | obj.meta.revision = 0;
|
4177 | }
|
4178 |
|
4179 | function updateMeta(obj) {
|
4180 | if (!obj) {
|
4181 | return;
|
4182 | }
|
4183 | obj.meta.updated = (new Date()).getTime();
|
4184 | obj.meta.revision += 1;
|
4185 | }
|
4186 |
|
4187 | function createInsertChange(obj) {
|
4188 | createChange(self.name, 'I', obj);
|
4189 | }
|
4190 |
|
4191 | function createUpdateChange(obj) {
|
4192 | createChange(self.name, 'U', obj);
|
4193 | }
|
4194 |
|
4195 | function insertMetaWithChange(obj) {
|
4196 | insertMeta(obj);
|
4197 | createInsertChange(obj);
|
4198 | }
|
4199 |
|
4200 | function updateMetaWithChange(obj) {
|
4201 | updateMeta(obj);
|
4202 | createUpdateChange(obj);
|
4203 | }
|
4204 |
|
4205 |
|
4206 |
|
4207 | var insertHandler, updateHandler;
|
4208 |
|
4209 | function setHandlers() {
|
4210 | insertHandler = self.disableChangesApi ? insertMeta : insertMetaWithChange;
|
4211 | updateHandler = self.disableChangesApi ? updateMeta : updateMetaWithChange;
|
4212 | }
|
4213 |
|
4214 | setHandlers();
|
4215 |
|
4216 | this.setChangesApi = function (enabled) {
|
4217 | self.disableChangesApi = !enabled;
|
4218 | setHandlers();
|
4219 | };
|
4220 | |
4221 |
|
4222 |
|
4223 | this.on('insert', function insertCallback(obj) {
|
4224 | insertHandler(obj);
|
4225 | });
|
4226 |
|
4227 | this.on('update', function updateCallback(obj) {
|
4228 | updateHandler(obj);
|
4229 | });
|
4230 |
|
4231 | this.on('delete', function deleteCallback(obj) {
|
4232 | if (!self.disableChangesApi) {
|
4233 | createChange(self.name, 'R', obj);
|
4234 | }
|
4235 | });
|
4236 |
|
4237 | this.on('warning', function (warning) {
|
4238 | self.console.warn(warning);
|
4239 | });
|
4240 |
|
4241 | flushChanges();
|
4242 | }
|
4243 |
|
4244 | Collection.prototype = new LokiEventEmitter();
|
4245 |
|
4246 | Collection.prototype.console = {
|
4247 | log: function () {},
|
4248 | warn: function () {},
|
4249 | error: function () {},
|
4250 | };
|
4251 |
|
4252 | Collection.prototype.addAutoUpdateObserver = function (object) {
|
4253 | if (!this.autoupdate || typeof Object.observe !== 'function')
|
4254 | return;
|
4255 |
|
4256 | Object.observe(object, this.observerCallback, ['add', 'update', 'delete', 'reconfigure', 'setPrototype']);
|
4257 | };
|
4258 |
|
4259 | Collection.prototype.removeAutoUpdateObserver = function (object) {
|
4260 | if (!this.autoupdate || typeof Object.observe !== 'function')
|
4261 | return;
|
4262 |
|
4263 | Object.unobserve(object, this.observerCallback);
|
4264 | };
|
4265 |
|
4266 | |
4267 |
|
4268 |
|
4269 |
|
4270 |
|
4271 |
|
4272 | Collection.prototype.addTransform = function (name, transform) {
|
4273 | if (this.transforms.hasOwnProperty(name)) {
|
4274 | throw new Error("a transform by that name already exists");
|
4275 | }
|
4276 |
|
4277 | this.transforms[name] = transform;
|
4278 | };
|
4279 |
|
4280 | |
4281 |
|
4282 |
|
4283 |
|
4284 |
|
4285 |
|
4286 | Collection.prototype.setTransform = function (name, transform) {
|
4287 | this.transforms[name] = transform;
|
4288 | };
|
4289 |
|
4290 | |
4291 |
|
4292 |
|
4293 |
|
4294 |
|
4295 | Collection.prototype.removeTransform = function (name) {
|
4296 | delete this.transforms[name];
|
4297 | };
|
4298 |
|
4299 | Collection.prototype.byExample = function (template) {
|
4300 | var k, obj, query;
|
4301 | query = [];
|
4302 | for (k in template) {
|
4303 | if (!template.hasOwnProperty(k)) continue;
|
4304 | query.push((
|
4305 | obj = {},
|
4306 | obj[k] = template[k],
|
4307 | obj
|
4308 | ));
|
4309 | }
|
4310 | return {
|
4311 | '$and': query
|
4312 | };
|
4313 | };
|
4314 |
|
4315 | Collection.prototype.findObject = function (template) {
|
4316 | return this.findOne(this.byExample(template));
|
4317 | };
|
4318 |
|
4319 | Collection.prototype.findObjects = function (template) {
|
4320 | return this.find(this.byExample(template));
|
4321 | };
|
4322 |
|
4323 | |
4324 |
|
4325 |
|
4326 | Collection.prototype.ttlDaemonFuncGen = function () {
|
4327 | var collection = this;
|
4328 | var age = this.ttl.age;
|
4329 | return function ttlDaemon() {
|
4330 | var now = Date.now();
|
4331 | var toRemove = collection.chain().where(function daemonFilter(member) {
|
4332 | var timestamp = member.meta.updated || member.meta.created;
|
4333 | var diff = now - timestamp;
|
4334 | return age < diff;
|
4335 | });
|
4336 | toRemove.remove();
|
4337 | };
|
4338 | };
|
4339 |
|
4340 | Collection.prototype.setTTL = function (age, interval) {
|
4341 | if (age < 0) {
|
4342 | clearInterval(this.ttl.daemon);
|
4343 | } else {
|
4344 | this.ttl.age = age;
|
4345 | this.ttl.ttlInterval = interval;
|
4346 | this.ttl.daemon = setInterval(this.ttlDaemonFuncGen(), interval);
|
4347 | }
|
4348 | };
|
4349 |
|
4350 | |
4351 |
|
4352 |
|
4353 |
|
4354 | |
4355 |
|
4356 |
|
4357 | Collection.prototype.prepareFullDocIndex = function () {
|
4358 | var len = this.data.length;
|
4359 | var indexes = new Array(len);
|
4360 | for (var i = 0; i < len; i += 1) {
|
4361 | indexes[i] = i;
|
4362 | }
|
4363 | return indexes;
|
4364 | };
|
4365 |
|
4366 | |
4367 |
|
4368 |
|
4369 |
|
4370 |
|
4371 | Collection.prototype.configureOptions = function (options) {
|
4372 | options = options || {};
|
4373 |
|
4374 | if (options.hasOwnProperty('adaptiveBinaryIndices')) {
|
4375 | this.adaptiveBinaryIndices = options.adaptiveBinaryIndices;
|
4376 |
|
4377 |
|
4378 | if (this.adaptiveBinaryIndices) {
|
4379 | this.ensureAllIndexes();
|
4380 | }
|
4381 | }
|
4382 | };
|
4383 |
|
4384 | |
4385 |
|
4386 |
|
4387 |
|
4388 |
|
4389 |
|
4390 | Collection.prototype.ensureIndex = function (property, force) {
|
4391 |
|
4392 | if (typeof (force) === 'undefined') {
|
4393 | force = false;
|
4394 | }
|
4395 |
|
4396 | if (property === null || property === undefined) {
|
4397 | throw new Error('Attempting to set index without an associated property');
|
4398 | }
|
4399 |
|
4400 | if (this.binaryIndices[property] && !force) {
|
4401 | if (!this.binaryIndices[property].dirty) return;
|
4402 | }
|
4403 |
|
4404 | var index = {
|
4405 | 'name': property,
|
4406 | 'dirty': true,
|
4407 | 'values': this.prepareFullDocIndex()
|
4408 | };
|
4409 | this.binaryIndices[property] = index;
|
4410 |
|
4411 | var wrappedComparer =
|
4412 | (function (p, data) {
|
4413 | return function (a, b) {
|
4414 | var objAp = data[a][p],
|
4415 | objBp = data[b][p];
|
4416 | if (objAp !== objBp) {
|
4417 | if (ltHelper(objAp, objBp, false)) return -1;
|
4418 | if (gtHelper(objAp, objBp, false)) return 1;
|
4419 | }
|
4420 | return 0;
|
4421 | };
|
4422 | })(property, this.data);
|
4423 |
|
4424 | index.values.sort(wrappedComparer);
|
4425 | index.dirty = false;
|
4426 |
|
4427 | this.dirty = true;
|
4428 | };
|
4429 |
|
4430 | Collection.prototype.getSequencedIndexValues = function (property) {
|
4431 | var idx, idxvals = this.binaryIndices[property].values;
|
4432 | var result = "";
|
4433 |
|
4434 | for (idx = 0; idx < idxvals.length; idx++) {
|
4435 | result += " [" + idx + "] " + this.data[idxvals[idx]][property];
|
4436 | }
|
4437 |
|
4438 | return result;
|
4439 | };
|
4440 |
|
4441 | Collection.prototype.ensureUniqueIndex = function (field) {
|
4442 | var index = this.constraints.unique[field];
|
4443 | if (!index) {
|
4444 |
|
4445 | if (this.uniqueNames.indexOf(field) == -1) {
|
4446 | this.uniqueNames.push(field);
|
4447 | }
|
4448 | }
|
4449 |
|
4450 |
|
4451 | this.constraints.unique[field] = index = new UniqueIndex(field);
|
4452 | this.data.forEach(function (obj) {
|
4453 | index.set(obj);
|
4454 | });
|
4455 | return index;
|
4456 | };
|
4457 |
|
4458 | |
4459 |
|
4460 |
|
4461 | Collection.prototype.ensureAllIndexes = function (force) {
|
4462 | var key, bIndices = this.binaryIndices;
|
4463 | for (key in bIndices) {
|
4464 | if (hasOwnProperty.call(bIndices, key)) {
|
4465 | this.ensureIndex(key, force);
|
4466 | }
|
4467 | }
|
4468 | };
|
4469 |
|
4470 | Collection.prototype.flagBinaryIndexesDirty = function () {
|
4471 | var key, bIndices = this.binaryIndices;
|
4472 | for (key in bIndices) {
|
4473 | if (hasOwnProperty.call(bIndices, key)) {
|
4474 | bIndices[key].dirty = true;
|
4475 | }
|
4476 | }
|
4477 | };
|
4478 |
|
4479 | Collection.prototype.flagBinaryIndexDirty = function (index) {
|
4480 | if (this.binaryIndices[index])
|
4481 | this.binaryIndices[index].dirty = true;
|
4482 | };
|
4483 |
|
4484 | |
4485 |
|
4486 |
|
4487 |
|
4488 |
|
4489 |
|
4490 | Collection.prototype.count = function (query) {
|
4491 | if (!query) {
|
4492 | return this.data.length;
|
4493 | }
|
4494 |
|
4495 | return this.chain().find(query).filteredrows.length;
|
4496 | };
|
4497 |
|
4498 | |
4499 |
|
4500 |
|
4501 | Collection.prototype.ensureId = function () {
|
4502 | var len = this.data.length,
|
4503 | i = 0;
|
4504 |
|
4505 | this.idIndex = [];
|
4506 | for (i; i < len; i += 1) {
|
4507 | this.idIndex.push(this.data[i].$loki);
|
4508 | }
|
4509 | };
|
4510 |
|
4511 | |
4512 |
|
4513 |
|
4514 | Collection.prototype.ensureIdAsync = function (callback) {
|
4515 | this.async(function () {
|
4516 | this.ensureId();
|
4517 | }, callback);
|
4518 | };
|
4519 |
|
4520 | |
4521 |
|
4522 |
|
4523 |
|
4524 |
|
4525 |
|
4526 |
|
4527 |
|
4528 |
|
4529 |
|
4530 |
|
4531 | Collection.prototype.addDynamicView = function (name, options) {
|
4532 | var dv = new DynamicView(this, name, options);
|
4533 | this.DynamicViews.push(dv);
|
4534 |
|
4535 | return dv;
|
4536 | };
|
4537 |
|
4538 | |
4539 |
|
4540 |
|
4541 |
|
4542 |
|
4543 | Collection.prototype.removeDynamicView = function (name) {
|
4544 | for (var idx = 0; idx < this.DynamicViews.length; idx++) {
|
4545 | if (this.DynamicViews[idx].name === name) {
|
4546 | this.DynamicViews.splice(idx, 1);
|
4547 | }
|
4548 | }
|
4549 | };
|
4550 |
|
4551 | |
4552 |
|
4553 |
|
4554 |
|
4555 |
|
4556 |
|
4557 | Collection.prototype.getDynamicView = function (name) {
|
4558 | for (var idx = 0; idx < this.DynamicViews.length; idx++) {
|
4559 | if (this.DynamicViews[idx].name === name) {
|
4560 | return this.DynamicViews[idx];
|
4561 | }
|
4562 | }
|
4563 |
|
4564 | return null;
|
4565 | };
|
4566 |
|
4567 | |
4568 |
|
4569 |
|
4570 |
|
4571 |
|
4572 |
|
4573 |
|
4574 |
|
4575 | Collection.prototype.findAndUpdate = function (filterObject, updateFunction) {
|
4576 | if (typeof (filterObject) === "function") {
|
4577 | this.updateWhere(filterObject, updateFunction);
|
4578 | }
|
4579 | else {
|
4580 | this.chain().find(filterObject).update(updateFunction);
|
4581 | }
|
4582 | };
|
4583 |
|
4584 | |
4585 |
|
4586 |
|
4587 |
|
4588 |
|
4589 |
|
4590 | Collection.prototype.findAndRemove = function(filterObject) {
|
4591 | this.chain().find(filterObject).remove();
|
4592 | };
|
4593 |
|
4594 | |
4595 |
|
4596 |
|
4597 |
|
4598 |
|
4599 |
|
4600 | Collection.prototype.insert = function (doc) {
|
4601 | if (!Array.isArray(doc)) {
|
4602 | return this.insertOne(doc);
|
4603 | }
|
4604 |
|
4605 |
|
4606 | var obj;
|
4607 | var results = [];
|
4608 |
|
4609 | this.emit('pre-insert', doc);
|
4610 | for (var i = 0, len = doc.length; i < len; i++) {
|
4611 | obj = this.insertOne(doc[i], true);
|
4612 | if (!obj) {
|
4613 | return undefined;
|
4614 | }
|
4615 | results.push(obj);
|
4616 | }
|
4617 | this.emit('insert', doc);
|
4618 | return results.length === 1 ? results[0] : results;
|
4619 | };
|
4620 |
|
4621 | |
4622 |
|
4623 |
|
4624 |
|
4625 |
|
4626 |
|
4627 |
|
4628 | Collection.prototype.insertOne = function (doc, bulkInsert) {
|
4629 | var err = null;
|
4630 | var returnObj;
|
4631 |
|
4632 | if (typeof doc !== 'object') {
|
4633 | err = new TypeError('Document needs to be an object');
|
4634 | } else if (doc === null) {
|
4635 | err = new TypeError('Object cannot be null');
|
4636 | }
|
4637 |
|
4638 | if (err !== null) {
|
4639 | this.emit('error', err);
|
4640 | throw err;
|
4641 | }
|
4642 |
|
4643 |
|
4644 | var obj = this.cloneObjects ? clone(doc, this.cloneMethod) : doc;
|
4645 |
|
4646 | if (typeof obj.meta === 'undefined') {
|
4647 | obj.meta = {
|
4648 | revision: 0,
|
4649 | created: 0
|
4650 | };
|
4651 | }
|
4652 |
|
4653 |
|
4654 | returnObj = this.cloneObjects ? clone(obj, this.cloneMethod) : obj;
|
4655 |
|
4656 |
|
4657 | if (!bulkInsert) {
|
4658 | this.emit('pre-insert', obj);
|
4659 | }
|
4660 | if (!this.add(obj)) {
|
4661 | return undefined;
|
4662 | }
|
4663 |
|
4664 | this.addAutoUpdateObserver(returnObj);
|
4665 | if (!bulkInsert) {
|
4666 | this.emit('insert', returnObj);
|
4667 | }
|
4668 | return returnObj;
|
4669 | };
|
4670 |
|
4671 | |
4672 |
|
4673 |
|
4674 |
|
4675 | Collection.prototype.clear = function () {
|
4676 | this.data = [];
|
4677 | this.idIndex = [];
|
4678 | this.binaryIndices = {};
|
4679 | this.cachedIndex = null;
|
4680 | this.cachedBinaryIndex = null;
|
4681 | this.cachedData = null;
|
4682 | this.maxId = 0;
|
4683 | this.DynamicViews = [];
|
4684 | this.dirty = true;
|
4685 | };
|
4686 |
|
4687 | |
4688 |
|
4689 |
|
4690 |
|
4691 |
|
4692 | Collection.prototype.update = function (doc) {
|
4693 | if (Array.isArray(doc)) {
|
4694 | var k = 0,
|
4695 | len = doc.length;
|
4696 | for (k; k < len; k += 1) {
|
4697 | this.update(doc[k]);
|
4698 | }
|
4699 | return;
|
4700 | }
|
4701 |
|
4702 |
|
4703 | if (!hasOwnProperty.call(doc, '$loki')) {
|
4704 | throw new Error('Trying to update unsynced document. Please save the document first by using insert() or addMany()');
|
4705 | }
|
4706 | try {
|
4707 | this.startTransaction();
|
4708 | var arr = this.get(doc.$loki, true),
|
4709 | oldInternal,
|
4710 | newInternal,
|
4711 | position,
|
4712 | self = this;
|
4713 |
|
4714 | if (!arr) {
|
4715 | throw new Error('Trying to update a document not in collection.');
|
4716 | }
|
4717 |
|
4718 | oldInternal = arr[0];
|
4719 | position = arr[1];
|
4720 |
|
4721 |
|
4722 | newInternal = this.cloneObjects ? clone(doc, this.cloneMethod) : doc;
|
4723 |
|
4724 | this.emit('pre-update', doc);
|
4725 |
|
4726 | Object.keys(this.constraints.unique).forEach(function (key) {
|
4727 | self.constraints.unique[key].update(oldInternal, newInternal);
|
4728 | });
|
4729 |
|
4730 |
|
4731 | this.data[position] = newInternal;
|
4732 |
|
4733 | if (newInternal !== doc) {
|
4734 | this.addAutoUpdateObserver(doc);
|
4735 | }
|
4736 |
|
4737 |
|
4738 |
|
4739 | for (var idx = 0; idx < this.DynamicViews.length; idx++) {
|
4740 | this.DynamicViews[idx].evaluateDocument(position, false);
|
4741 | }
|
4742 |
|
4743 | var key;
|
4744 | if (this.adaptiveBinaryIndices) {
|
4745 |
|
4746 | var bIndices = this.binaryIndices;
|
4747 | for (key in bIndices) {
|
4748 | this.adaptiveBinaryIndexUpdate(position, key);
|
4749 | }
|
4750 | }
|
4751 | else {
|
4752 | this.flagBinaryIndexesDirty();
|
4753 | }
|
4754 |
|
4755 | this.idIndex[position] = newInternal.$loki;
|
4756 |
|
4757 |
|
4758 | this.commit();
|
4759 | this.dirty = true;
|
4760 |
|
4761 | this.emit('update', doc, this.cloneObjects ? clone(oldInternal, this.cloneMethod) : null);
|
4762 | return doc;
|
4763 | } catch (err) {
|
4764 | this.rollback();
|
4765 | this.console.error(err.message);
|
4766 | this.emit('error', err);
|
4767 | throw (err);
|
4768 | }
|
4769 | };
|
4770 |
|
4771 | |
4772 |
|
4773 |
|
4774 | Collection.prototype.add = function (obj) {
|
4775 |
|
4776 | if ('object' !== typeof obj) {
|
4777 | throw new TypeError('Object being added needs to be an object');
|
4778 | }
|
4779 |
|
4780 |
|
4781 |
|
4782 | if (typeof (obj.$loki) !== 'undefined') {
|
4783 | throw new Error('Document is already in collection, please use update()');
|
4784 | }
|
4785 |
|
4786 | |
4787 |
|
4788 |
|
4789 | try {
|
4790 | this.startTransaction();
|
4791 | this.maxId++;
|
4792 |
|
4793 | if (isNaN(this.maxId)) {
|
4794 | this.maxId = (this.data[this.data.length - 1].$loki + 1);
|
4795 | }
|
4796 |
|
4797 | obj.$loki = this.maxId;
|
4798 | obj.meta.version = 0;
|
4799 |
|
4800 | var key, constrUnique = this.constraints.unique;
|
4801 | for (key in constrUnique) {
|
4802 | if (hasOwnProperty.call(constrUnique, key)) {
|
4803 | constrUnique[key].set(obj);
|
4804 | }
|
4805 | }
|
4806 |
|
4807 |
|
4808 | this.idIndex.push(obj.$loki);
|
4809 |
|
4810 |
|
4811 | this.data.push(obj);
|
4812 |
|
4813 | var addedPos = this.data.length - 1;
|
4814 |
|
4815 |
|
4816 |
|
4817 | var dvlen = this.DynamicViews.length;
|
4818 | for (var i = 0; i < dvlen; i++) {
|
4819 | this.DynamicViews[i].evaluateDocument(addedPos, true);
|
4820 | }
|
4821 |
|
4822 | if (this.adaptiveBinaryIndices) {
|
4823 |
|
4824 | var bIndices = this.binaryIndices;
|
4825 | for (key in bIndices) {
|
4826 | this.adaptiveBinaryIndexInsert(addedPos, key);
|
4827 | }
|
4828 | }
|
4829 | else {
|
4830 | this.flagBinaryIndexesDirty();
|
4831 | }
|
4832 |
|
4833 | this.commit();
|
4834 | this.dirty = true;
|
4835 |
|
4836 | return (this.cloneObjects) ? (clone(obj, this.cloneMethod)) : (obj);
|
4837 | } catch (err) {
|
4838 | this.rollback();
|
4839 | this.console.error(err.message);
|
4840 | this.emit('error', err);
|
4841 | throw (err);
|
4842 | }
|
4843 | };
|
4844 |
|
4845 | |
4846 |
|
4847 |
|
4848 |
|
4849 |
|
4850 |
|
4851 |
|
4852 | Collection.prototype.updateWhere = function(filterFunction, updateFunction) {
|
4853 | var results = this.where(filterFunction),
|
4854 | i = 0,
|
4855 | obj;
|
4856 | try {
|
4857 | for (i; i < results.length; i++) {
|
4858 | obj = updateFunction(results[i]);
|
4859 | this.update(obj);
|
4860 | }
|
4861 |
|
4862 | } catch (err) {
|
4863 | this.rollback();
|
4864 | this.console.error(err.message);
|
4865 | }
|
4866 | };
|
4867 |
|
4868 | |
4869 |
|
4870 |
|
4871 |
|
4872 |
|
4873 |
|
4874 | Collection.prototype.removeWhere = function (query) {
|
4875 | var list;
|
4876 | if (typeof query === 'function') {
|
4877 | list = this.data.filter(query);
|
4878 | this.remove(list);
|
4879 | } else {
|
4880 | this.chain().find(query).remove();
|
4881 | }
|
4882 | };
|
4883 |
|
4884 | Collection.prototype.removeDataOnly = function () {
|
4885 | this.remove(this.data.slice());
|
4886 | };
|
4887 |
|
4888 | |
4889 |
|
4890 |
|
4891 |
|
4892 |
|
4893 | Collection.prototype.remove = function (doc) {
|
4894 | if (typeof doc === 'number') {
|
4895 | doc = this.get(doc);
|
4896 | }
|
4897 |
|
4898 | if ('object' !== typeof doc) {
|
4899 | throw new Error('Parameter is not an object');
|
4900 | }
|
4901 | if (Array.isArray(doc)) {
|
4902 | var k = 0,
|
4903 | len = doc.length;
|
4904 | for (k; k < len; k += 1) {
|
4905 | this.remove(doc[k]);
|
4906 | }
|
4907 | return;
|
4908 | }
|
4909 |
|
4910 | if (!hasOwnProperty.call(doc, '$loki')) {
|
4911 | throw new Error('Object is not a document stored in the collection');
|
4912 | }
|
4913 |
|
4914 | try {
|
4915 | this.startTransaction();
|
4916 | var arr = this.get(doc.$loki, true),
|
4917 |
|
4918 | position = arr[1];
|
4919 | var self = this;
|
4920 | Object.keys(this.constraints.unique).forEach(function (key) {
|
4921 | if (doc[key] !== null && typeof doc[key] !== 'undefined') {
|
4922 | self.constraints.unique[key].remove(doc[key]);
|
4923 | }
|
4924 | });
|
4925 |
|
4926 |
|
4927 | for (var idx = 0; idx < this.DynamicViews.length; idx++) {
|
4928 | this.DynamicViews[idx].removeDocument(position);
|
4929 | }
|
4930 |
|
4931 | if (this.adaptiveBinaryIndices) {
|
4932 |
|
4933 | var key, bIndices = this.binaryIndices;
|
4934 | for (key in bIndices) {
|
4935 | this.adaptiveBinaryIndexRemove(position, key);
|
4936 | }
|
4937 | }
|
4938 | else {
|
4939 | this.flagBinaryIndexesDirty();
|
4940 | }
|
4941 |
|
4942 | this.data.splice(position, 1);
|
4943 | this.removeAutoUpdateObserver(doc);
|
4944 |
|
4945 |
|
4946 | this.idIndex.splice(position, 1);
|
4947 |
|
4948 | this.commit();
|
4949 | this.dirty = true;
|
4950 | this.emit('delete', arr[0]);
|
4951 | delete doc.$loki;
|
4952 | delete doc.meta;
|
4953 | return doc;
|
4954 |
|
4955 | } catch (err) {
|
4956 | this.rollback();
|
4957 | this.console.error(err.message);
|
4958 | this.emit('error', err);
|
4959 | return null;
|
4960 | }
|
4961 | };
|
4962 |
|
4963 | |
4964 |
|
4965 |
|
4966 |
|
4967 | |
4968 |
|
4969 |
|
4970 |
|
4971 |
|
4972 |
|
4973 |
|
4974 |
|
4975 | Collection.prototype.get = function (id, returnPosition) {
|
4976 | var retpos = returnPosition || false,
|
4977 | data = this.idIndex,
|
4978 | max = data.length - 1,
|
4979 | min = 0,
|
4980 | mid = (min + max) >> 1;
|
4981 |
|
4982 | id = typeof id === 'number' ? id : parseInt(id, 10);
|
4983 |
|
4984 | if (isNaN(id)) {
|
4985 | throw new TypeError('Passed id is not an integer');
|
4986 | }
|
4987 |
|
4988 | while (data[min] < data[max]) {
|
4989 | mid = (min + max) >> 1;
|
4990 |
|
4991 | if (data[mid] < id) {
|
4992 | min = mid + 1;
|
4993 | } else {
|
4994 | max = mid;
|
4995 | }
|
4996 | }
|
4997 |
|
4998 | if (max === min && data[min] === id) {
|
4999 | if (retpos) {
|
5000 | return [this.data[min], min];
|
5001 | }
|
5002 | return this.data[min];
|
5003 | }
|
5004 | return null;
|
5005 |
|
5006 | };
|
5007 |
|
5008 | |
5009 |
|
5010 |
|
5011 |
|
5012 |
|
5013 |
|
5014 |
|
5015 | Collection.prototype.getBinaryIndexPosition = function(dataPosition, binaryIndexName) {
|
5016 | var val = this.data[dataPosition][binaryIndexName];
|
5017 | var index = this.binaryIndices[binaryIndexName].values;
|
5018 |
|
5019 |
|
5020 |
|
5021 |
|
5022 | var range = this.calculateRange("$eq", binaryIndexName, val);
|
5023 |
|
5024 | if (range[0] === 0 && range[1] === -1) {
|
5025 |
|
5026 | return null;
|
5027 | }
|
5028 |
|
5029 | var min = range[0];
|
5030 | var max = range[1];
|
5031 |
|
5032 |
|
5033 |
|
5034 |
|
5035 | for(var idx = min; idx <= max; idx++) {
|
5036 | if (index[idx] === dataPosition) return idx;
|
5037 | }
|
5038 |
|
5039 |
|
5040 | return null;
|
5041 | };
|
5042 |
|
5043 | |
5044 |
|
5045 |
|
5046 |
|
5047 |
|
5048 | Collection.prototype.adaptiveBinaryIndexInsert = function(dataPosition, binaryIndexName) {
|
5049 | var index = this.binaryIndices[binaryIndexName].values;
|
5050 | var val = this.data[dataPosition][binaryIndexName];
|
5051 |
|
5052 | var idxPos = this.calculateRangeStart(binaryIndexName, val);
|
5053 |
|
5054 |
|
5055 |
|
5056 | this.binaryIndices[binaryIndexName].values.splice(idxPos, 0, dataPosition);
|
5057 | };
|
5058 |
|
5059 | |
5060 |
|
5061 |
|
5062 |
|
5063 |
|
5064 | Collection.prototype.adaptiveBinaryIndexUpdate = function(dataPosition, binaryIndexName) {
|
5065 |
|
5066 |
|
5067 | var idxPos,
|
5068 | index = this.binaryIndices[binaryIndexName].values,
|
5069 | len=index.length;
|
5070 |
|
5071 | for(idxPos=0; idxPos < len; idxPos++) {
|
5072 | if (index[idxPos] === dataPosition) break;
|
5073 | }
|
5074 |
|
5075 |
|
5076 | this.binaryIndices[binaryIndexName].values.splice(idxPos, 1);
|
5077 |
|
5078 |
|
5079 | this.adaptiveBinaryIndexInsert(dataPosition, binaryIndexName);
|
5080 | };
|
5081 |
|
5082 | |
5083 |
|
5084 |
|
5085 |
|
5086 |
|
5087 | Collection.prototype.adaptiveBinaryIndexRemove = function(dataPosition, binaryIndexName, removedFromIndexOnly) {
|
5088 | var idxPos = this.getBinaryIndexPosition(dataPosition, binaryIndexName);
|
5089 | var index = this.binaryIndices[binaryIndexName].values;
|
5090 | var len,
|
5091 | idx;
|
5092 |
|
5093 | if (idxPos === null) {
|
5094 |
|
5095 | return null;
|
5096 | }
|
5097 |
|
5098 |
|
5099 | this.binaryIndices[binaryIndexName].values.splice(idxPos, 1);
|
5100 |
|
5101 |
|
5102 |
|
5103 | if (removedFromIndexOnly === true) {
|
5104 | return;
|
5105 | }
|
5106 |
|
5107 |
|
5108 |
|
5109 | len = index.length;
|
5110 | for (idx = 0; idx < len; idx++) {
|
5111 | if (index[idx] > dataPosition) {
|
5112 | index[idx]--;
|
5113 | }
|
5114 | }
|
5115 | };
|
5116 |
|
5117 | |
5118 |
|
5119 |
|
5120 |
|
5121 | Collection.prototype.calculateRangeStart = function (prop, val) {
|
5122 | var rcd = this.data;
|
5123 | var index = this.binaryIndices[prop].values;
|
5124 | var min = 0;
|
5125 | var max = index.length - 1;
|
5126 | var mid = 0;
|
5127 |
|
5128 | if (index.length === 0) {
|
5129 | return 0;
|
5130 | }
|
5131 |
|
5132 | var minVal = rcd[index[min]][prop];
|
5133 | var maxVal = rcd[index[max]][prop];
|
5134 |
|
5135 |
|
5136 | while (min < max) {
|
5137 | mid = (min + max) >> 1;
|
5138 |
|
5139 | if (ltHelper(rcd[index[mid]][prop], val, false)) {
|
5140 | min = mid + 1;
|
5141 | } else {
|
5142 | max = mid;
|
5143 | }
|
5144 | }
|
5145 |
|
5146 | var lbound = min;
|
5147 |
|
5148 | if (ltHelper(rcd[index[lbound]][prop], val, false)) {
|
5149 | return lbound+1;
|
5150 | }
|
5151 | else {
|
5152 | return lbound;
|
5153 | }
|
5154 | };
|
5155 |
|
5156 | |
5157 |
|
5158 |
|
5159 |
|
5160 | Collection.prototype.calculateRangeEnd = function (prop, val) {
|
5161 | var rcd = this.data;
|
5162 | var index = this.binaryIndices[prop].values;
|
5163 | var min = 0;
|
5164 | var max = index.length - 1;
|
5165 | var mid = 0;
|
5166 |
|
5167 | if (index.length === 0) {
|
5168 | return 0;
|
5169 | }
|
5170 |
|
5171 | var minVal = rcd[index[min]][prop];
|
5172 | var maxVal = rcd[index[max]][prop];
|
5173 |
|
5174 |
|
5175 | while (min < max) {
|
5176 | mid = (min + max) >> 1;
|
5177 |
|
5178 | if (ltHelper(val, rcd[index[mid]][prop], false)) {
|
5179 | max = mid;
|
5180 | } else {
|
5181 | min = mid + 1;
|
5182 | }
|
5183 | }
|
5184 |
|
5185 | var ubound = max;
|
5186 |
|
5187 | if (gtHelper(rcd[index[ubound]][prop], val, false)) {
|
5188 | return ubound-1;
|
5189 | }
|
5190 | else {
|
5191 | return ubound;
|
5192 | }
|
5193 | };
|
5194 |
|
5195 | |
5196 |
|
5197 |
|
5198 |
|
5199 |
|
5200 |
|
5201 |
|
5202 |
|
5203 |
|
5204 |
|
5205 | Collection.prototype.calculateRange = function (op, prop, val) {
|
5206 | var rcd = this.data;
|
5207 | var index = this.binaryIndices[prop].values;
|
5208 | var min = 0;
|
5209 | var max = index.length - 1;
|
5210 | var mid = 0;
|
5211 |
|
5212 |
|
5213 | if (rcd.length === 0) {
|
5214 | return [0, -1];
|
5215 | }
|
5216 |
|
5217 | var minVal = rcd[index[min]][prop];
|
5218 | var maxVal = rcd[index[max]][prop];
|
5219 |
|
5220 |
|
5221 | switch (op) {
|
5222 | case '$eq':
|
5223 | case '$aeq':
|
5224 | if (ltHelper(val, minVal, false) || gtHelper(val, maxVal, false)) {
|
5225 | return [0, -1];
|
5226 | }
|
5227 | break;
|
5228 | case '$dteq':
|
5229 | if (ltHelper(val, minVal, false) || gtHelper(val, maxVal, false)) {
|
5230 | return [0, -1];
|
5231 | }
|
5232 | break;
|
5233 | case '$gt':
|
5234 | if (gtHelper(val, maxVal, true)) {
|
5235 | return [0, -1];
|
5236 | }
|
5237 | break;
|
5238 | case '$gte':
|
5239 | if (gtHelper(val, maxVal, false)) {
|
5240 | return [0, -1];
|
5241 | }
|
5242 | break;
|
5243 | case '$lt':
|
5244 | if (ltHelper(val, minVal, true)) {
|
5245 | return [0, -1];
|
5246 | }
|
5247 | if (ltHelper(maxVal, val, false)) {
|
5248 | return [0, rcd.length - 1];
|
5249 | }
|
5250 | break;
|
5251 | case '$lte':
|
5252 | if (ltHelper(val, minVal, false)) {
|
5253 | return [0, -1];
|
5254 | }
|
5255 | if (ltHelper(maxVal, val, true)) {
|
5256 | return [0, rcd.length - 1];
|
5257 | }
|
5258 | break;
|
5259 | case '$between':
|
5260 | return ([this.calculateRangeStart(prop, val[0]), this.calculateRangeEnd(prop, val[1])]);
|
5261 | case '$in':
|
5262 | var idxset = [],
|
5263 | segResult = [];
|
5264 |
|
5265 | for (var j = 0, len = val.length; j < len; j++) {
|
5266 | var seg = this.calculateRange('$eq', prop, val[j]);
|
5267 |
|
5268 | for (var i = seg[0]; i <= seg[1]; i++) {
|
5269 | if (idxset[i] === undefined) {
|
5270 | idxset[i] = true;
|
5271 | segResult.push(i);
|
5272 | }
|
5273 | }
|
5274 | }
|
5275 | return segResult;
|
5276 | }
|
5277 |
|
5278 |
|
5279 | while (min < max) {
|
5280 | mid = (min + max) >> 1;
|
5281 |
|
5282 | if (ltHelper(rcd[index[mid]][prop], val, false)) {
|
5283 | min = mid + 1;
|
5284 | } else {
|
5285 | max = mid;
|
5286 | }
|
5287 | }
|
5288 |
|
5289 | var lbound = min;
|
5290 |
|
5291 |
|
5292 | max = index.length - 1;
|
5293 |
|
5294 |
|
5295 | while (min < max) {
|
5296 | mid = (min + max) >> 1;
|
5297 |
|
5298 | if (ltHelper(val, rcd[index[mid]][prop], false)) {
|
5299 | max = mid;
|
5300 | } else {
|
5301 | min = mid + 1;
|
5302 | }
|
5303 | }
|
5304 |
|
5305 | var ubound = max;
|
5306 |
|
5307 | var lval = rcd[index[lbound]][prop];
|
5308 | var uval = rcd[index[ubound]][prop];
|
5309 |
|
5310 | switch (op) {
|
5311 | case '$eq':
|
5312 | if (lval !== val) {
|
5313 | return [0, -1];
|
5314 | }
|
5315 | if (uval !== val) {
|
5316 | ubound--;
|
5317 | }
|
5318 |
|
5319 | return [lbound, ubound];
|
5320 | case '$dteq':
|
5321 | if (lval > val || lval < val) {
|
5322 | return [0, -1];
|
5323 | }
|
5324 | if (uval > val || uval < val) {
|
5325 | ubound--;
|
5326 | }
|
5327 |
|
5328 | return [lbound, ubound];
|
5329 |
|
5330 |
|
5331 | case '$gt':
|
5332 | if (ltHelper(uval, val, true)) {
|
5333 | return [0, -1];
|
5334 | }
|
5335 |
|
5336 | return [ubound, rcd.length - 1];
|
5337 |
|
5338 | case '$gte':
|
5339 | if (ltHelper(lval, val, false)) {
|
5340 | return [0, -1];
|
5341 | }
|
5342 |
|
5343 | return [lbound, rcd.length - 1];
|
5344 |
|
5345 | case '$lt':
|
5346 | if (lbound === 0 && ltHelper(lval, val, false)) {
|
5347 | return [0, 0];
|
5348 | }
|
5349 | return [0, lbound - 1];
|
5350 |
|
5351 | case '$lte':
|
5352 | if (uval !== val) {
|
5353 | ubound--;
|
5354 | }
|
5355 |
|
5356 | if (ubound === 0 && ltHelper(uval, val, false)) {
|
5357 | return [0, 0];
|
5358 | }
|
5359 | return [0, ubound];
|
5360 |
|
5361 | default:
|
5362 | return [0, rcd.length - 1];
|
5363 | }
|
5364 | };
|
5365 |
|
5366 | |
5367 |
|
5368 |
|
5369 |
|
5370 |
|
5371 |
|
5372 |
|
5373 | Collection.prototype.by = function (field, value) {
|
5374 | var self;
|
5375 | if (value === undefined) {
|
5376 | self = this;
|
5377 | return function (value) {
|
5378 | return self.by(field, value);
|
5379 | };
|
5380 | }
|
5381 |
|
5382 | var result = this.constraints.unique[field].get(value);
|
5383 | if (!this.cloneObjects) {
|
5384 | return result;
|
5385 | } else {
|
5386 | return clone(result, this.cloneMethod);
|
5387 | }
|
5388 | };
|
5389 |
|
5390 | |
5391 |
|
5392 |
|
5393 |
|
5394 |
|
5395 |
|
5396 | Collection.prototype.findOne = function (query) {
|
5397 | query = query || {};
|
5398 |
|
5399 |
|
5400 | var result = new Resultset(this, {
|
5401 | queryObj: query,
|
5402 | firstOnly: true
|
5403 | });
|
5404 |
|
5405 | if (Array.isArray(result) && result.length === 0) {
|
5406 | return null;
|
5407 | } else {
|
5408 | if (!this.cloneObjects) {
|
5409 | return result;
|
5410 | } else {
|
5411 | return clone(result, this.cloneMethod);
|
5412 | }
|
5413 | }
|
5414 | };
|
5415 |
|
5416 | |
5417 |
|
5418 |
|
5419 |
|
5420 |
|
5421 |
|
5422 |
|
5423 |
|
5424 |
|
5425 | Collection.prototype.chain = function (transform, parameters) {
|
5426 | var rs = new Resultset(this);
|
5427 |
|
5428 | if (typeof transform === 'undefined') {
|
5429 | return rs;
|
5430 | }
|
5431 |
|
5432 | return rs.transform(transform, parameters);
|
5433 | };
|
5434 |
|
5435 | |
5436 |
|
5437 |
|
5438 |
|
5439 |
|
5440 |
|
5441 |
|
5442 |
|
5443 | Collection.prototype.find = function (query) {
|
5444 | if (typeof (query) === 'undefined') {
|
5445 | query = 'getAll';
|
5446 | }
|
5447 |
|
5448 | var results = new Resultset(this, {
|
5449 | queryObj: query
|
5450 | });
|
5451 | if (!this.cloneObjects) {
|
5452 | return results;
|
5453 | } else {
|
5454 | return cloneObjectArray(results, this.cloneMethod);
|
5455 | }
|
5456 | };
|
5457 |
|
5458 | |
5459 |
|
5460 |
|
5461 |
|
5462 | Collection.prototype.findOneUnindexed = function (prop, value) {
|
5463 | var i = this.data.length,
|
5464 | doc;
|
5465 | while (i--) {
|
5466 | if (this.data[i][prop] === value) {
|
5467 | doc = this.data[i];
|
5468 | return doc;
|
5469 | }
|
5470 | }
|
5471 | return null;
|
5472 | };
|
5473 |
|
5474 | |
5475 |
|
5476 |
|
5477 |
|
5478 |
|
5479 | Collection.prototype.startTransaction = function () {
|
5480 | if (this.transactional) {
|
5481 | this.cachedData = clone(this.data, this.cloneMethod);
|
5482 | this.cachedIndex = this.idIndex;
|
5483 | this.cachedBinaryIndex = this.binaryIndices;
|
5484 |
|
5485 |
|
5486 | for (var idx = 0; idx < this.DynamicViews.length; idx++) {
|
5487 | this.DynamicViews[idx].startTransaction();
|
5488 | }
|
5489 | }
|
5490 | };
|
5491 |
|
5492 |
|
5493 | Collection.prototype.commit = function () {
|
5494 | if (this.transactional) {
|
5495 | this.cachedData = null;
|
5496 | this.cachedIndex = null;
|
5497 | this.cachedBinaryIndex = null;
|
5498 |
|
5499 |
|
5500 | for (var idx = 0; idx < this.DynamicViews.length; idx++) {
|
5501 | this.DynamicViews[idx].commit();
|
5502 | }
|
5503 | }
|
5504 | };
|
5505 |
|
5506 |
|
5507 | Collection.prototype.rollback = function () {
|
5508 | if (this.transactional) {
|
5509 | if (this.cachedData !== null && this.cachedIndex !== null) {
|
5510 | this.data = this.cachedData;
|
5511 | this.idIndex = this.cachedIndex;
|
5512 | this.binaryIndices = this.cachedBinaryIndex;
|
5513 | }
|
5514 |
|
5515 |
|
5516 | for (var idx = 0; idx < this.DynamicViews.length; idx++) {
|
5517 | this.DynamicViews[idx].rollback();
|
5518 | }
|
5519 | }
|
5520 | };
|
5521 |
|
5522 |
|
5523 | Collection.prototype.async = function (fun, callback) {
|
5524 | setTimeout(function () {
|
5525 | if (typeof fun === 'function') {
|
5526 | fun();
|
5527 | callback();
|
5528 | } else {
|
5529 | throw new TypeError('Argument passed for async execution is not a function');
|
5530 | }
|
5531 | }, 0);
|
5532 | };
|
5533 |
|
5534 | |
5535 |
|
5536 |
|
5537 |
|
5538 |
|
5539 |
|
5540 |
|
5541 |
|
5542 |
|
5543 |
|
5544 |
|
5545 | Collection.prototype.where = function (fun) {
|
5546 | var results = new Resultset(this, {
|
5547 | queryFunc: fun
|
5548 | });
|
5549 | if (!this.cloneObjects) {
|
5550 | return results;
|
5551 | } else {
|
5552 | return cloneObjectArray(results, this.cloneMethod);
|
5553 | }
|
5554 | };
|
5555 |
|
5556 | |
5557 |
|
5558 |
|
5559 |
|
5560 |
|
5561 |
|
5562 |
|
5563 |
|
5564 | Collection.prototype.mapReduce = function (mapFunction, reduceFunction) {
|
5565 | try {
|
5566 | return reduceFunction(this.data.map(mapFunction));
|
5567 | } catch (err) {
|
5568 | throw err;
|
5569 | }
|
5570 | };
|
5571 |
|
5572 | |
5573 |
|
5574 |
|
5575 |
|
5576 |
|
5577 |
|
5578 |
|
5579 |
|
5580 |
|
5581 |
|
5582 | Collection.prototype.eqJoin = function (joinData, leftJoinProp, rightJoinProp, mapFun) {
|
5583 |
|
5584 | return new Resultset(this).eqJoin(joinData, leftJoinProp, rightJoinProp, mapFun);
|
5585 | };
|
5586 |
|
5587 |
|
5588 | |
5589 |
|
5590 |
|
5591 |
|
5592 | Collection.prototype.stages = {};
|
5593 |
|
5594 | |
5595 |
|
5596 |
|
5597 |
|
5598 | Collection.prototype.getStage = function (name) {
|
5599 | if (!this.stages[name]) {
|
5600 | this.stages[name] = {};
|
5601 | }
|
5602 | return this.stages[name];
|
5603 | };
|
5604 | |
5605 |
|
5606 |
|
5607 | Collection.prototype.commitLog = [];
|
5608 |
|
5609 | |
5610 |
|
5611 |
|
5612 |
|
5613 | Collection.prototype.stage = function (stageName, obj) {
|
5614 | var copy = JSON.parse(JSON.stringify(obj));
|
5615 | this.getStage(stageName)[obj.$loki] = copy;
|
5616 | return copy;
|
5617 | };
|
5618 |
|
5619 | |
5620 |
|
5621 |
|
5622 |
|
5623 |
|
5624 |
|
5625 |
|
5626 | Collection.prototype.commitStage = function (stageName, message) {
|
5627 | var stage = this.getStage(stageName),
|
5628 | prop,
|
5629 | timestamp = new Date().getTime();
|
5630 |
|
5631 | for (prop in stage) {
|
5632 |
|
5633 | this.update(stage[prop]);
|
5634 | this.commitLog.push({
|
5635 | timestamp: timestamp,
|
5636 | message: message,
|
5637 | data: JSON.parse(JSON.stringify(stage[prop]))
|
5638 | });
|
5639 | }
|
5640 | this.stages[stageName] = {};
|
5641 | };
|
5642 |
|
5643 | Collection.prototype.no_op = function () {
|
5644 | return;
|
5645 | };
|
5646 |
|
5647 | |
5648 |
|
5649 |
|
5650 | Collection.prototype.extract = function (field) {
|
5651 | var i = 0,
|
5652 | len = this.data.length,
|
5653 | isDotNotation = isDeepProperty(field),
|
5654 | result = [];
|
5655 | for (i; i < len; i += 1) {
|
5656 | result.push(deepProperty(this.data[i], field, isDotNotation));
|
5657 | }
|
5658 | return result;
|
5659 | };
|
5660 |
|
5661 | |
5662 |
|
5663 |
|
5664 | Collection.prototype.max = function (field) {
|
5665 | return Math.max.apply(null, this.extract(field));
|
5666 | };
|
5667 |
|
5668 | |
5669 |
|
5670 |
|
5671 | Collection.prototype.min = function (field) {
|
5672 | return Math.min.apply(null, this.extract(field));
|
5673 | };
|
5674 |
|
5675 | |
5676 |
|
5677 |
|
5678 | Collection.prototype.maxRecord = function (field) {
|
5679 | var i = 0,
|
5680 | len = this.data.length,
|
5681 | deep = isDeepProperty(field),
|
5682 | result = {
|
5683 | index: 0,
|
5684 | value: undefined
|
5685 | },
|
5686 | max;
|
5687 |
|
5688 | for (i; i < len; i += 1) {
|
5689 | if (max !== undefined) {
|
5690 | if (max < deepProperty(this.data[i], field, deep)) {
|
5691 | max = deepProperty(this.data[i], field, deep);
|
5692 | result.index = this.data[i].$loki;
|
5693 | }
|
5694 | } else {
|
5695 | max = deepProperty(this.data[i], field, deep);
|
5696 | result.index = this.data[i].$loki;
|
5697 | }
|
5698 | }
|
5699 | result.value = max;
|
5700 | return result;
|
5701 | };
|
5702 |
|
5703 | |
5704 |
|
5705 |
|
5706 | Collection.prototype.minRecord = function (field) {
|
5707 | var i = 0,
|
5708 | len = this.data.length,
|
5709 | deep = isDeepProperty(field),
|
5710 | result = {
|
5711 | index: 0,
|
5712 | value: undefined
|
5713 | },
|
5714 | min;
|
5715 |
|
5716 | for (i; i < len; i += 1) {
|
5717 | if (min !== undefined) {
|
5718 | if (min > deepProperty(this.data[i], field, deep)) {
|
5719 | min = deepProperty(this.data[i], field, deep);
|
5720 | result.index = this.data[i].$loki;
|
5721 | }
|
5722 | } else {
|
5723 | min = deepProperty(this.data[i], field, deep);
|
5724 | result.index = this.data[i].$loki;
|
5725 | }
|
5726 | }
|
5727 | result.value = min;
|
5728 | return result;
|
5729 | };
|
5730 |
|
5731 | |
5732 |
|
5733 |
|
5734 | Collection.prototype.extractNumerical = function (field) {
|
5735 | return this.extract(field).map(parseBase10).filter(Number).filter(function (n) {
|
5736 | return !(isNaN(n));
|
5737 | });
|
5738 | };
|
5739 |
|
5740 | |
5741 |
|
5742 |
|
5743 |
|
5744 |
|
5745 |
|
5746 |
|
5747 | Collection.prototype.avg = function (field) {
|
5748 | return average(this.extractNumerical(field));
|
5749 | };
|
5750 |
|
5751 | |
5752 |
|
5753 |
|
5754 |
|
5755 |
|
5756 | Collection.prototype.stdDev = function (field) {
|
5757 | return standardDeviation(this.extractNumerical(field));
|
5758 | };
|
5759 |
|
5760 | |
5761 |
|
5762 |
|
5763 |
|
5764 | Collection.prototype.mode = function (field) {
|
5765 | var dict = {},
|
5766 | data = this.extract(field);
|
5767 | data.forEach(function (obj) {
|
5768 | if (dict[obj]) {
|
5769 | dict[obj] += 1;
|
5770 | } else {
|
5771 | dict[obj] = 1;
|
5772 | }
|
5773 | });
|
5774 | var max,
|
5775 | prop, mode;
|
5776 | for (prop in dict) {
|
5777 | if (max) {
|
5778 | if (max < dict[prop]) {
|
5779 | mode = prop;
|
5780 | }
|
5781 | } else {
|
5782 | mode = prop;
|
5783 | max = dict[prop];
|
5784 | }
|
5785 | }
|
5786 | return mode;
|
5787 | };
|
5788 |
|
5789 | |
5790 |
|
5791 |
|
5792 |
|
5793 | Collection.prototype.median = function (field) {
|
5794 | var values = this.extractNumerical(field);
|
5795 | values.sort(sub);
|
5796 |
|
5797 | var half = Math.floor(values.length / 2);
|
5798 |
|
5799 | if (values.length % 2) {
|
5800 | return values[half];
|
5801 | } else {
|
5802 | return (values[half - 1] + values[half]) / 2.0;
|
5803 | }
|
5804 | };
|
5805 |
|
5806 | |
5807 |
|
5808 |
|
5809 | function isDeepProperty(field) {
|
5810 | return field.indexOf('.') !== -1;
|
5811 | }
|
5812 |
|
5813 | function parseBase10(num) {
|
5814 | return parseFloat(num, 10);
|
5815 | }
|
5816 |
|
5817 | function isNotUndefined(obj) {
|
5818 | return obj !== undefined;
|
5819 | }
|
5820 |
|
5821 | function add(a, b) {
|
5822 | return a + b;
|
5823 | }
|
5824 |
|
5825 | function sub(a, b) {
|
5826 | return a - b;
|
5827 | }
|
5828 |
|
5829 | function median(values) {
|
5830 | values.sort(sub);
|
5831 | var half = Math.floor(values.length / 2);
|
5832 | return (values.length % 2) ? values[half] : ((values[half - 1] + values[half]) / 2.0);
|
5833 | }
|
5834 |
|
5835 | function average(array) {
|
5836 | return (array.reduce(add, 0)) / array.length;
|
5837 | }
|
5838 |
|
5839 | function standardDeviation(values) {
|
5840 | var avg = average(values);
|
5841 | var squareDiffs = values.map(function (value) {
|
5842 | var diff = value - avg;
|
5843 | var sqrDiff = diff * diff;
|
5844 | return sqrDiff;
|
5845 | });
|
5846 |
|
5847 | var avgSquareDiff = average(squareDiffs);
|
5848 |
|
5849 | var stdDev = Math.sqrt(avgSquareDiff);
|
5850 | return stdDev;
|
5851 | }
|
5852 |
|
5853 | function deepProperty(obj, property, isDeep) {
|
5854 | if (isDeep === false) {
|
5855 |
|
5856 | return obj[property];
|
5857 | }
|
5858 | var pieces = property.split('.'),
|
5859 | root = obj;
|
5860 | while (pieces.length > 0) {
|
5861 | root = root[pieces.shift()];
|
5862 | }
|
5863 | return root;
|
5864 | }
|
5865 |
|
5866 | function binarySearch(array, item, fun) {
|
5867 | var lo = 0,
|
5868 | hi = array.length,
|
5869 | compared,
|
5870 | mid;
|
5871 | while (lo < hi) {
|
5872 | mid = (lo + hi) >> 1;
|
5873 | compared = fun.apply(null, [item, array[mid]]);
|
5874 | if (compared === 0) {
|
5875 | return {
|
5876 | found: true,
|
5877 | index: mid
|
5878 | };
|
5879 | } else if (compared < 0) {
|
5880 | hi = mid;
|
5881 | } else {
|
5882 | lo = mid + 1;
|
5883 | }
|
5884 | }
|
5885 | return {
|
5886 | found: false,
|
5887 | index: hi
|
5888 | };
|
5889 | }
|
5890 |
|
5891 | function BSonSort(fun) {
|
5892 | return function (array, item) {
|
5893 | return binarySearch(array, item, fun);
|
5894 | };
|
5895 | }
|
5896 |
|
5897 | function KeyValueStore() {}
|
5898 |
|
5899 | KeyValueStore.prototype = {
|
5900 | keys: [],
|
5901 | values: [],
|
5902 | sort: function (a, b) {
|
5903 | return (a < b) ? -1 : ((a > b) ? 1 : 0);
|
5904 | },
|
5905 | setSort: function (fun) {
|
5906 | this.bs = new BSonSort(fun);
|
5907 | },
|
5908 | bs: function () {
|
5909 | return new BSonSort(this.sort);
|
5910 | },
|
5911 | set: function (key, value) {
|
5912 | var pos = this.bs(this.keys, key);
|
5913 | if (pos.found) {
|
5914 | this.values[pos.index] = value;
|
5915 | } else {
|
5916 | this.keys.splice(pos.index, 0, key);
|
5917 | this.values.splice(pos.index, 0, value);
|
5918 | }
|
5919 | },
|
5920 | get: function (key) {
|
5921 | return this.values[binarySearch(this.keys, key, this.sort).index];
|
5922 | }
|
5923 | };
|
5924 |
|
5925 | function UniqueIndex(uniqueField) {
|
5926 | this.field = uniqueField;
|
5927 | this.keyMap = {};
|
5928 | this.lokiMap = {};
|
5929 | }
|
5930 | UniqueIndex.prototype.keyMap = {};
|
5931 | UniqueIndex.prototype.lokiMap = {};
|
5932 | UniqueIndex.prototype.set = function (obj) {
|
5933 | var fieldValue = obj[this.field];
|
5934 | if (fieldValue !== null && typeof (fieldValue) !== 'undefined') {
|
5935 | if (this.keyMap[fieldValue]) {
|
5936 | throw new Error('Duplicate key for property ' + this.field + ': ' + fieldValue);
|
5937 | } else {
|
5938 | this.keyMap[fieldValue] = obj;
|
5939 | this.lokiMap[obj.$loki] = fieldValue;
|
5940 | }
|
5941 | }
|
5942 | };
|
5943 | UniqueIndex.prototype.get = function (key) {
|
5944 | return this.keyMap[key];
|
5945 | };
|
5946 |
|
5947 | UniqueIndex.prototype.byId = function (id) {
|
5948 | return this.keyMap[this.lokiMap[id]];
|
5949 | };
|
5950 | |
5951 |
|
5952 |
|
5953 |
|
5954 |
|
5955 | UniqueIndex.prototype.update = function (obj, doc) {
|
5956 | if (this.lokiMap[obj.$loki] !== doc[this.field]) {
|
5957 | var old = this.lokiMap[obj.$loki];
|
5958 | this.set(doc);
|
5959 |
|
5960 | this.keyMap[old] = undefined;
|
5961 | } else {
|
5962 | this.keyMap[obj[this.field]] = doc;
|
5963 | }
|
5964 | };
|
5965 | UniqueIndex.prototype.remove = function (key) {
|
5966 | var obj = this.keyMap[key];
|
5967 | if (obj !== null && typeof obj !== 'undefined') {
|
5968 | this.keyMap[key] = undefined;
|
5969 | this.lokiMap[obj.$loki] = undefined;
|
5970 | } else {
|
5971 | throw new Error('Key is not in unique index: ' + this.field);
|
5972 | }
|
5973 | };
|
5974 | UniqueIndex.prototype.clear = function () {
|
5975 | this.keyMap = {};
|
5976 | this.lokiMap = {};
|
5977 | };
|
5978 |
|
5979 | function ExactIndex(exactField) {
|
5980 | this.index = {};
|
5981 | this.field = exactField;
|
5982 | }
|
5983 |
|
5984 |
|
5985 | ExactIndex.prototype = {
|
5986 | set: function add(key, val) {
|
5987 | if (this.index[key]) {
|
5988 | this.index[key].push(val);
|
5989 | } else {
|
5990 | this.index[key] = [val];
|
5991 | }
|
5992 | },
|
5993 |
|
5994 |
|
5995 | remove: function remove(key, val) {
|
5996 | var idxSet = this.index[key];
|
5997 | for (var i in idxSet) {
|
5998 | if (idxSet[i] == val) {
|
5999 | idxSet.splice(i, 1);
|
6000 | }
|
6001 | }
|
6002 | if (idxSet.length < 1) {
|
6003 | this.index[key] = undefined;
|
6004 | }
|
6005 | },
|
6006 |
|
6007 |
|
6008 | get: function get(key) {
|
6009 | return this.index[key];
|
6010 | },
|
6011 |
|
6012 |
|
6013 | clear: function clear(key) {
|
6014 | this.index = {};
|
6015 | }
|
6016 | };
|
6017 |
|
6018 | function SortedIndex(sortedField) {
|
6019 | this.field = sortedField;
|
6020 | }
|
6021 |
|
6022 | SortedIndex.prototype = {
|
6023 | keys: [],
|
6024 | values: [],
|
6025 |
|
6026 | sort: function (a, b) {
|
6027 | return (a < b) ? -1 : ((a > b) ? 1 : 0);
|
6028 | },
|
6029 | bs: function () {
|
6030 | return new BSonSort(this.sort);
|
6031 | },
|
6032 |
|
6033 | setSort: function (fun) {
|
6034 | this.bs = new BSonSort(fun);
|
6035 | },
|
6036 |
|
6037 | set: function (key, value) {
|
6038 | var pos = binarySearch(this.keys, key, this.sort);
|
6039 | if (pos.found) {
|
6040 | this.values[pos.index].push(value);
|
6041 | } else {
|
6042 | this.keys.splice(pos.index, 0, key);
|
6043 | this.values.splice(pos.index, 0, [value]);
|
6044 | }
|
6045 | },
|
6046 |
|
6047 | get: function (key) {
|
6048 | var bsr = binarySearch(this.keys, key, this.sort);
|
6049 | if (bsr.found) {
|
6050 | return this.values[bsr.index];
|
6051 | } else {
|
6052 | return [];
|
6053 | }
|
6054 | },
|
6055 |
|
6056 | getLt: function (key) {
|
6057 | var bsr = binarySearch(this.keys, key, this.sort);
|
6058 | var pos = bsr.index;
|
6059 | if (bsr.found) pos--;
|
6060 | return this.getAll(key, 0, pos);
|
6061 | },
|
6062 |
|
6063 | getGt: function (key) {
|
6064 | var bsr = binarySearch(this.keys, key, this.sort);
|
6065 | var pos = bsr.index;
|
6066 | if (bsr.found) pos++;
|
6067 | return this.getAll(key, pos, this.keys.length);
|
6068 | },
|
6069 |
|
6070 |
|
6071 | getAll: function (key, start, end) {
|
6072 | var results = [];
|
6073 | for (var i = start; i < end; i++) {
|
6074 | results = results.concat(this.values[i]);
|
6075 | }
|
6076 | return results;
|
6077 | },
|
6078 |
|
6079 | getPos: function (key) {
|
6080 | return binarySearch(this.keys, key, this.sort);
|
6081 | },
|
6082 |
|
6083 | remove: function (key, value) {
|
6084 | var pos = binarySearch(this.keys, key, this.sort).index;
|
6085 | var idxSet = this.values[pos];
|
6086 | for (var i in idxSet) {
|
6087 | if (idxSet[i] == value) idxSet.splice(i, 1);
|
6088 | }
|
6089 | if (idxSet.length < 1) {
|
6090 | this.keys.splice(pos, 1);
|
6091 | this.values.splice(pos, 1);
|
6092 | }
|
6093 | },
|
6094 |
|
6095 | clear: function () {
|
6096 | this.keys = [];
|
6097 | this.values = [];
|
6098 | }
|
6099 | };
|
6100 |
|
6101 |
|
6102 | Loki.LokiOps = LokiOps;
|
6103 | Loki.Collection = Collection;
|
6104 | Loki.KeyValueStore = KeyValueStore;
|
6105 | Loki.LokiMemoryAdapter = LokiMemoryAdapter;
|
6106 | Loki.LokiPartitioningAdapter = LokiPartitioningAdapter;
|
6107 | Loki.LokiLocalStorageAdapter = LokiLocalStorageAdapter;
|
6108 | Loki.LokiFsAdapter = LokiFsAdapter;
|
6109 | Loki.persistenceAdapters = {
|
6110 | fs: LokiFsAdapter,
|
6111 | localStorage: LokiLocalStorageAdapter
|
6112 | };
|
6113 | return Loki;
|
6114 | }());
|
6115 |
|
6116 | }));
|