1 | <!DOCTYPE html>
|
2 | <html lang="en">
|
3 | <head>
|
4 | <meta charset="utf-8">
|
5 | <title>JSDoc: Source: loki-indexed-adapter.js</title>
|
6 |
|
7 | <script src="scripts/prettify/prettify.js"> </script>
|
8 | <script src="scripts/prettify/lang-css.js"> </script>
|
9 | |
10 |
|
11 |
|
12 | <link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
|
13 | <link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
|
14 | </head>
|
15 |
|
16 | <body>
|
17 |
|
18 | <div id="main">
|
19 |
|
20 | <h1 class="page-title">Source: loki-indexed-adapter.js</h1>
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | <section>
|
28 | <article>
|
29 | <pre class="prettyprint source linenums"><code>/*
|
30 | Loki IndexedDb Adapter (need to include this script to use it)
|
31 |
|
32 | Console Usage can be used for management/diagnostic, here are a few examples :
|
33 | adapter.getDatabaseList(); // with no callback passed, this method will log results to console
|
34 | adapter.saveDatabase('UserDatabase', JSON.stringify(myDb));
|
35 | adapter.loadDatabase('UserDatabase'); // will log the serialized db to console
|
36 | adapter.deleteDatabase('UserDatabase');
|
37 | */
|
38 |
|
39 | (function (root, factory) {
|
40 | if (typeof define === 'function' && define.amd) {
|
41 | // AMD
|
42 | define([], factory);
|
43 | } else if (typeof exports === 'object') {
|
44 | // Node, CommonJS-like
|
45 | module.exports = factory();
|
46 | } else {
|
47 | // Browser globals (root is window)
|
48 | root.LokiIndexedAdapter = factory();
|
49 | }
|
50 | }(this, function () {
|
51 | return (function() {
|
52 |
|
53 | /**
|
54 | * Loki persistence adapter class for indexedDb.
|
55 | * This class fulfills abstract adapter interface which can be applied to other storage methods.
|
56 | * Utilizes the included LokiCatalog app/key/value database for actual database persistence.
|
57 | * Indexeddb is highly async, but this adapter has been made 'console-friendly' as well.
|
58 | * Anywhere a callback is omitted, it should return results (if applicable) to console.
|
59 | * IndexedDb storage is provided per-domain, so we implement app/key/value database to
|
60 | * allow separate contexts for separate apps within a domain.
|
61 | *
|
62 | * @example
|
63 | * var idbAdapter = new LokiIndexedAdapter('finance');
|
64 | *
|
65 | * @constructor LokiIndexedAdapter
|
66 | *
|
67 | * @param {string} appname - (Optional) Application name context can be used to distinguish subdomains, 'loki' by default
|
68 | */
|
69 | function LokiIndexedAdapter(appname)
|
70 | {
|
71 | this.app = 'loki';
|
72 |
|
73 | if (typeof (appname) !== 'undefined')
|
74 | {
|
75 | this.app = appname;
|
76 | }
|
77 |
|
78 | // keep reference to catalog class for base AKV operations
|
79 | this.catalog = null;
|
80 |
|
81 | if (!this.checkAvailability()) {
|
82 | throw new Error('indexedDB does not seem to be supported for your environment');
|
83 | }
|
84 | }
|
85 |
|
86 | /**
|
87 | * Used to check if adapter is available
|
88 | *
|
89 | * @returns {boolean} true if indexeddb is available, false if not.
|
90 | * @memberof LokiIndexedAdapter
|
91 | */
|
92 | LokiIndexedAdapter.prototype.checkAvailability = function()
|
93 | {
|
94 | if (typeof indexedDB !== 'undefined' && indexedDB) return true;
|
95 |
|
96 | return false;
|
97 | };
|
98 |
|
99 | /**
|
100 | * Retrieves a serialized db string from the catalog.
|
101 | *
|
102 | * @example
|
103 | * // LOAD
|
104 | * var idbAdapter = new LokiIndexedAdapter('finance');
|
105 | * var db = new loki('test', { adapter: idbAdapter });
|
106 | * db.loadDatabase(function(result) {
|
107 | * console.log('done');
|
108 | * });
|
109 | *
|
110 | * @param {string} dbname - the name of the database to retrieve.
|
111 | * @param {function} callback - callback should accept string param containing serialized db string.
|
112 | * @memberof LokiIndexedAdapter
|
113 | */
|
114 | LokiIndexedAdapter.prototype.loadDatabase = function(dbname, callback)
|
115 | {
|
116 | var appName = this.app;
|
117 | var adapter = this;
|
118 |
|
119 | // lazy open/create db reference so dont -need- callback in constructor
|
120 | if (this.catalog === null || this.catalog.db === null) {
|
121 | this.catalog = new LokiCatalog(function(cat) {
|
122 | adapter.catalog = cat;
|
123 |
|
124 | adapter.loadDatabase(dbname, callback);
|
125 | });
|
126 |
|
127 | return;
|
128 | }
|
129 |
|
130 | // lookup up db string in AKV db
|
131 | this.catalog.getAppKey(appName, dbname, function(result) {
|
132 | if (typeof (callback) === 'function') {
|
133 | if (result.id === 0) {
|
134 | callback(null);
|
135 | return;
|
136 | }
|
137 | callback(result.val);
|
138 | }
|
139 | else {
|
140 | // support console use of api
|
141 | console.log(result.val);
|
142 | }
|
143 | });
|
144 | };
|
145 |
|
146 | // alias
|
147 | LokiIndexedAdapter.prototype.loadKey = LokiIndexedAdapter.prototype.loadDatabase;
|
148 |
|
149 | /**
|
150 | * Saves a serialized db to the catalog.
|
151 | *
|
152 | * @example
|
153 | * // SAVE : will save App/Key/Val as 'finance'/'test'/{serializedDb}
|
154 | * var idbAdapter = new LokiIndexedAdapter('finance');
|
155 | * var db = new loki('test', { adapter: idbAdapter });
|
156 | * var coll = db.addCollection('testColl');
|
157 | * coll.insert({test: 'val'});
|
158 | * db.saveDatabase(); // could pass callback if needed for async complete
|
159 | *
|
160 | * @param {string} dbname - the name to give the serialized database within the catalog.
|
161 | * @param {string} dbstring - the serialized db string to save.
|
162 | * @param {function} callback - (Optional) callback passed obj.success with true or false
|
163 | * @memberof LokiIndexedAdapter
|
164 | */
|
165 | LokiIndexedAdapter.prototype.saveDatabase = function(dbname, dbstring, callback)
|
166 | {
|
167 | var appName = this.app;
|
168 | var adapter = this;
|
169 |
|
170 | function saveCallback(result) {
|
171 | if (result && result.success === true) {
|
172 | callback(null);
|
173 | }
|
174 | else {
|
175 | callback(new Error("Error saving database"));
|
176 | }
|
177 | }
|
178 |
|
179 | // lazy open/create db reference so dont -need- callback in constructor
|
180 | if (this.catalog === null || this.catalog.db === null) {
|
181 | this.catalog = new LokiCatalog(function(cat) {
|
182 | adapter.catalog = cat;
|
183 |
|
184 | // now that catalog has been initialized, set (add/update) the AKV entry
|
185 | cat.setAppKey(appName, dbname, dbstring, saveCallback);
|
186 | });
|
187 |
|
188 | return;
|
189 | }
|
190 |
|
191 | // set (add/update) entry to AKV database
|
192 | this.catalog.setAppKey(appName, dbname, dbstring, saveCallback);
|
193 | };
|
194 |
|
195 | // alias
|
196 | LokiIndexedAdapter.prototype.saveKey = LokiIndexedAdapter.prototype.saveDatabase;
|
197 |
|
198 | /**
|
199 | * Deletes a serialized db from the catalog.
|
200 | *
|
201 | * @example
|
202 | * // DELETE DATABASE
|
203 | * // delete 'finance'/'test' value from catalog
|
204 | * idbAdapter.deleteDatabase('test', function {
|
205 | * // database deleted
|
206 | * });
|
207 | *
|
208 | * @param {string} dbname - the name of the database to delete from the catalog.
|
209 | * @param {function=} callback - (Optional) executed on database delete
|
210 | * @memberof LokiIndexedAdapter
|
211 | */
|
212 | LokiIndexedAdapter.prototype.deleteDatabase = function(dbname, callback)
|
213 | {
|
214 | var appName = this.app;
|
215 | var adapter = this;
|
216 |
|
217 | // lazy open/create db reference and pass callback ahead
|
218 | if (this.catalog === null || this.catalog.db === null) {
|
219 | this.catalog = new LokiCatalog(function(cat) {
|
220 | adapter.catalog = cat;
|
221 |
|
222 | adapter.deleteDatabase(dbname, callback);
|
223 | });
|
224 |
|
225 | return;
|
226 | }
|
227 |
|
228 | // catalog was already initialized, so just lookup object and delete by id
|
229 | this.catalog.getAppKey(appName, dbname, function(result) {
|
230 | var id = result.id;
|
231 |
|
232 | if (id !== 0) {
|
233 | adapter.catalog.deleteAppKey(id);
|
234 | }
|
235 |
|
236 | if (typeof (callback) === 'function') {
|
237 | callback();
|
238 | }
|
239 | });
|
240 | };
|
241 |
|
242 | // alias
|
243 | LokiIndexedAdapter.prototype.deleteKey = LokiIndexedAdapter.prototype.deleteDatabase;
|
244 |
|
245 | /**
|
246 | * Removes all database partitions and pages with the base filename passed in.
|
247 | * This utility method does not (yet) guarantee async deletions will be completed before returning
|
248 | *
|
249 | * @param {string} dbname - the base filename which container, partitions, or pages are derived
|
250 | * @memberof LokiIndexedAdapter
|
251 | */
|
252 | LokiIndexedAdapter.prototype.deleteDatabasePartitions = function(dbname) {
|
253 | var self=this;
|
254 | this.getDatabaseList(function(result) {
|
255 | result.forEach(function(str) {
|
256 | if (str.startsWith(dbname)) {
|
257 | self.deleteDatabase(str);
|
258 | }
|
259 | });
|
260 | });
|
261 | };
|
262 |
|
263 | /**
|
264 | * Retrieves object array of catalog entries for current app.
|
265 | *
|
266 | * @example
|
267 | * idbAdapter.getDatabaseList(function(result) {
|
268 | * // result is array of string names for that appcontext ('finance')
|
269 | * result.forEach(function(str) {
|
270 | * console.log(str);
|
271 | * });
|
272 | * });
|
273 | *
|
274 | * @param {function} callback - should accept array of database names in the catalog for current app.
|
275 | * @memberof LokiIndexedAdapter
|
276 | */
|
277 | LokiIndexedAdapter.prototype.getDatabaseList = function(callback)
|
278 | {
|
279 | var appName = this.app;
|
280 | var adapter = this;
|
281 |
|
282 | // lazy open/create db reference so dont -need- callback in constructor
|
283 | if (this.catalog === null || this.catalog.db === null) {
|
284 | this.catalog = new LokiCatalog(function(cat) {
|
285 | adapter.catalog = cat;
|
286 |
|
287 | adapter.getDatabaseList(callback);
|
288 | });
|
289 |
|
290 | return;
|
291 | }
|
292 |
|
293 | // catalog already initialized
|
294 | // get all keys for current appName, and transpose results so just string array
|
295 | this.catalog.getAppKeys(appName, function(results) {
|
296 | var names = [];
|
297 |
|
298 | for(var idx = 0; idx < results.length; idx++) {
|
299 | names.push(results[idx].key);
|
300 | }
|
301 |
|
302 | if (typeof (callback) === 'function') {
|
303 | callback(names);
|
304 | }
|
305 | else {
|
306 | names.forEach(function(obj) {
|
307 | console.log(obj);
|
308 | });
|
309 | }
|
310 | });
|
311 | };
|
312 |
|
313 | // alias
|
314 | LokiIndexedAdapter.prototype.getKeyList = LokiIndexedAdapter.prototype.getDatabaseList;
|
315 |
|
316 | /**
|
317 | * Allows retrieval of list of all keys in catalog along with size
|
318 | *
|
319 | * @param {function} callback - (Optional) callback to accept result array.
|
320 | * @memberof LokiIndexedAdapter
|
321 | */
|
322 | LokiIndexedAdapter.prototype.getCatalogSummary = function(callback)
|
323 | {
|
324 | var appName = this.app;
|
325 | var adapter = this;
|
326 |
|
327 | // lazy open/create db reference
|
328 | if (this.catalog === null || this.catalog.db === null) {
|
329 | this.catalog = new LokiCatalog(function(cat) {
|
330 | adapter.catalog = cat;
|
331 |
|
332 | adapter.getCatalogSummary(callback);
|
333 | });
|
334 |
|
335 | return;
|
336 | }
|
337 |
|
338 | // catalog already initialized
|
339 | // get all keys for current appName, and transpose results so just string array
|
340 | this.catalog.getAllKeys(function(results) {
|
341 | var entries = [];
|
342 | var obj,
|
343 | size,
|
344 | oapp,
|
345 | okey,
|
346 | oval;
|
347 |
|
348 | for(var idx = 0; idx < results.length; idx++) {
|
349 | obj = results[idx];
|
350 | oapp = obj.app || '';
|
351 | okey = obj.key || '';
|
352 | oval = obj.val || '';
|
353 |
|
354 | // app and key are composited into an appkey column so we will mult by 2
|
355 | size = oapp.length * 2 + okey.length * 2 + oval.length + 1;
|
356 |
|
357 | entries.push({ "app": obj.app, "key": obj.key, "size": size });
|
358 | }
|
359 |
|
360 | if (typeof (callback) === 'function') {
|
361 | callback(entries);
|
362 | }
|
363 | else {
|
364 | entries.forEach(function(obj) {
|
365 | console.log(obj);
|
366 | });
|
367 | }
|
368 | });
|
369 | };
|
370 |
|
371 | /**
|
372 | * LokiCatalog - underlying App/Key/Value catalog persistence
|
373 | * This non-interface class implements the actual persistence.
|
374 | * Used by the IndexedAdapter class.
|
375 | */
|
376 | function LokiCatalog(callback)
|
377 | {
|
378 | this.db = null;
|
379 | this.initializeLokiCatalog(callback);
|
380 | }
|
381 |
|
382 | LokiCatalog.prototype.initializeLokiCatalog = function(callback) {
|
383 | var openRequest = indexedDB.open('LokiCatalog', 1);
|
384 | var cat = this;
|
385 |
|
386 | // If database doesn't exist yet or its version is lower than our version specified above (2nd param in line above)
|
387 | openRequest.onupgradeneeded = function(e) {
|
388 | var thisDB = e.target.result;
|
389 | if (thisDB.objectStoreNames.contains('LokiAKV')) {
|
390 | thisDB.deleteObjectStore('LokiAKV');
|
391 | }
|
392 |
|
393 | if(!thisDB.objectStoreNames.contains('LokiAKV')) {
|
394 | var objectStore = thisDB.createObjectStore('LokiAKV', { keyPath: 'id', autoIncrement:true });
|
395 | objectStore.createIndex('app', 'app', {unique:false});
|
396 | objectStore.createIndex('key', 'key', {unique:false});
|
397 | // hack to simulate composite key since overhead is low (main size should be in val field)
|
398 | // user (me) required to duplicate the app and key into comma delimited appkey field off object
|
399 | // This will allow retrieving single record with that composite key as well as
|
400 | // still supporting opening cursors on app or key alone
|
401 | objectStore.createIndex('appkey', 'appkey', {unique:true});
|
402 | }
|
403 | };
|
404 |
|
405 | openRequest.onsuccess = function(e) {
|
406 | cat.db = e.target.result;
|
407 |
|
408 | if (typeof (callback) === 'function') callback(cat);
|
409 | };
|
410 |
|
411 | openRequest.onerror = function(e) {
|
412 | throw e;
|
413 | };
|
414 | };
|
415 |
|
416 | LokiCatalog.prototype.getAppKey = function(app, key, callback) {
|
417 | var transaction = this.db.transaction(['LokiAKV'], 'readonly');
|
418 | var store = transaction.objectStore('LokiAKV');
|
419 | var index = store.index('appkey');
|
420 | var appkey = app + "," + key;
|
421 | var request = index.get(appkey);
|
422 |
|
423 | request.onsuccess = (function(usercallback) {
|
424 | return function(e) {
|
425 | var lres = e.target.result;
|
426 |
|
427 | if (lres === null || typeof(lres) === 'undefined') {
|
428 | lres = {
|
429 | id: 0,
|
430 | success: false
|
431 | };
|
432 | }
|
433 |
|
434 | if (typeof(usercallback) === 'function') {
|
435 | usercallback(lres);
|
436 | }
|
437 | else {
|
438 | console.log(lres);
|
439 | }
|
440 | };
|
441 | })(callback);
|
442 |
|
443 | request.onerror = (function(usercallback) {
|
444 | return function(e) {
|
445 | if (typeof(usercallback) === 'function') {
|
446 | usercallback({ id: 0, success: false });
|
447 | }
|
448 | else {
|
449 | throw e;
|
450 | }
|
451 | };
|
452 | })(callback);
|
453 | };
|
454 |
|
455 | LokiCatalog.prototype.getAppKeyById = function (id, callback, data) {
|
456 | var transaction = this.db.transaction(['LokiAKV'], 'readonly');
|
457 | var store = transaction.objectStore('LokiAKV');
|
458 | var request = store.get(id);
|
459 |
|
460 | request.onsuccess = (function(data, usercallback){
|
461 | return function(e) {
|
462 | if (typeof(usercallback) === 'function') {
|
463 | usercallback(e.target.result, data);
|
464 | }
|
465 | else {
|
466 | console.log(e.target.result);
|
467 | }
|
468 | };
|
469 | })(data, callback);
|
470 | };
|
471 |
|
472 | LokiCatalog.prototype.setAppKey = function (app, key, val, callback) {
|
473 | var transaction = this.db.transaction(['LokiAKV'], 'readwrite');
|
474 | var store = transaction.objectStore('LokiAKV');
|
475 | var index = store.index('appkey');
|
476 | var appkey = app + "," + key;
|
477 | var request = index.get(appkey);
|
478 |
|
479 | // first try to retrieve an existing object by that key
|
480 | // need to do this because to update an object you need to have id in object, otherwise it will append id with new autocounter and clash the unique index appkey
|
481 | request.onsuccess = function(e) {
|
482 | var res = e.target.result;
|
483 |
|
484 | if (res === null || res === undefined) {
|
485 | res = {
|
486 | app:app,
|
487 | key:key,
|
488 | appkey: app + ',' + key,
|
489 | val:val
|
490 | };
|
491 | }
|
492 | else {
|
493 | res.val = val;
|
494 | }
|
495 |
|
496 | var requestPut = store.put(res);
|
497 |
|
498 | requestPut.onerror = (function(usercallback) {
|
499 | return function(e) {
|
500 | if (typeof(usercallback) === 'function') {
|
501 | usercallback({ success: false });
|
502 | }
|
503 | else {
|
504 | console.error('LokiCatalog.setAppKey (set) onerror');
|
505 | console.error(request.error);
|
506 | }
|
507 | };
|
508 |
|
509 | })(callback);
|
510 |
|
511 | requestPut.onsuccess = (function(usercallback) {
|
512 | return function(e) {
|
513 | if (typeof(usercallback) === 'function') {
|
514 | usercallback({ success: true });
|
515 | }
|
516 | };
|
517 | })(callback);
|
518 | };
|
519 |
|
520 | request.onerror = (function(usercallback) {
|
521 | return function(e) {
|
522 | if (typeof(usercallback) === 'function') {
|
523 | usercallback({ success: false });
|
524 | }
|
525 | else {
|
526 | console.error('LokiCatalog.setAppKey (get) onerror');
|
527 | console.error(request.error);
|
528 | }
|
529 | };
|
530 | })(callback);
|
531 | };
|
532 |
|
533 | LokiCatalog.prototype.deleteAppKey = function (id, callback) {
|
534 | var transaction = this.db.transaction(['LokiAKV'], 'readwrite');
|
535 | var store = transaction.objectStore('LokiAKV');
|
536 | var request = store.delete(id);
|
537 |
|
538 | request.onsuccess = (function(usercallback) {
|
539 | return function(evt) {
|
540 | if (typeof(usercallback) === 'function') usercallback({ success: true });
|
541 | };
|
542 | })(callback);
|
543 |
|
544 | request.onerror = (function(usercallback) {
|
545 | return function(evt) {
|
546 | if (typeof(usercallback) === 'function') {
|
547 | usercallback(false);
|
548 | }
|
549 | else {
|
550 | console.error('LokiCatalog.deleteAppKey raised onerror');
|
551 | console.error(request.error);
|
552 | }
|
553 | };
|
554 | })(callback);
|
555 | };
|
556 |
|
557 | LokiCatalog.prototype.getAppKeys = function(app, callback) {
|
558 | var transaction = this.db.transaction(['LokiAKV'], 'readonly');
|
559 | var store = transaction.objectStore('LokiAKV');
|
560 | var index = store.index('app');
|
561 |
|
562 | // We want cursor to all values matching our (single) app param
|
563 | var singleKeyRange = IDBKeyRange.only(app);
|
564 |
|
565 | // To use one of the key ranges, pass it in as the first argument of openCursor()/openKeyCursor()
|
566 | var cursor = index.openCursor(singleKeyRange);
|
567 |
|
568 | // cursor internally, pushing results into this.data[] and return
|
569 | // this.data[] when done (similar to service)
|
570 | var localdata = [];
|
571 |
|
572 | cursor.onsuccess = (function(data, callback) {
|
573 | return function(e) {
|
574 | var cursor = e.target.result;
|
575 | if (cursor) {
|
576 | var currObject = cursor.value;
|
577 |
|
578 | data.push(currObject);
|
579 |
|
580 | cursor.continue();
|
581 | }
|
582 | else {
|
583 | if (typeof(callback) === 'function') {
|
584 | callback(data);
|
585 | }
|
586 | else {
|
587 | console.log(data);
|
588 | }
|
589 | }
|
590 | };
|
591 | })(localdata, callback);
|
592 |
|
593 | cursor.onerror = (function(usercallback) {
|
594 | return function(e) {
|
595 | if (typeof(usercallback) === 'function') {
|
596 | usercallback(null);
|
597 | }
|
598 | else {
|
599 | console.error('LokiCatalog.getAppKeys raised onerror');
|
600 | console.error(e);
|
601 | }
|
602 | };
|
603 | })(callback);
|
604 |
|
605 | };
|
606 |
|
607 | // Hide 'cursoring' and return array of { id: id, key: key }
|
608 | LokiCatalog.prototype.getAllKeys = function (callback) {
|
609 | var transaction = this.db.transaction(['LokiAKV'], 'readonly');
|
610 | var store = transaction.objectStore('LokiAKV');
|
611 | var cursor = store.openCursor();
|
612 |
|
613 | var localdata = [];
|
614 |
|
615 | cursor.onsuccess = (function(data, callback) {
|
616 | return function(e) {
|
617 | var cursor = e.target.result;
|
618 | if (cursor) {
|
619 | var currObject = cursor.value;
|
620 |
|
621 | data.push(currObject);
|
622 |
|
623 | cursor.continue();
|
624 | }
|
625 | else {
|
626 | if (typeof(callback) === 'function') {
|
627 | callback(data);
|
628 | }
|
629 | else {
|
630 | console.log(data);
|
631 | }
|
632 | }
|
633 | };
|
634 | })(localdata, callback);
|
635 |
|
636 | cursor.onerror = (function(usercallback) {
|
637 | return function(e) {
|
638 | if (typeof(usercallback) === 'function') usercallback(null);
|
639 | };
|
640 | })(callback);
|
641 |
|
642 | };
|
643 |
|
644 | return LokiIndexedAdapter;
|
645 |
|
646 | }());
|
647 | }));
|
648 | </code></pre>
|
649 | </article>
|
650 | </section>
|
651 |
|
652 |
|
653 |
|
654 |
|
655 | </div>
|
656 |
|
657 | <nav>
|
658 | <h2><a href="index.html">Home</a></h2><h3>Classes</h3><ul><li><a href="Collection.html">Collection</a></li><li><a href="DynamicView.html">DynamicView</a></li><li><a href="Loki.html">Loki</a></li><li><a href="LokiEventEmitter.html">LokiEventEmitter</a></li><li><a href="LokiFsAdapter.html">LokiFsAdapter</a></li><li><a href="LokiFsStructuredAdapter.html">LokiFsStructuredAdapter</a></li><li><a href="LokiIndexedAdapter.html">LokiIndexedAdapter</a></li><li><a href="LokiLocalStorageAdapter.html">LokiLocalStorageAdapter</a></li><li><a href="LokiMemoryAdapter.html">LokiMemoryAdapter</a></li><li><a href="LokiPartitioningAdapter.html">LokiPartitioningAdapter</a></li><li><a href="Resultset.html">Resultset</a></li></ul><h3>Tutorials</h3><ul><li><a href="tutorial-Autoupdating Collections.html">Autoupdating Collections</a></li><li><a href="tutorial-Changes API.html">Changes API</a></li><li><a href="tutorial-Collection Transforms.html">Collection Transforms</a></li><li><a href="tutorial-Indexing and Query performance.html">Indexing and Query performance</a></li><li><a href="tutorial-Loki Angular.html">Loki Angular</a></li><li><a href="tutorial-Persistence Adapters.html">Persistence Adapters</a></li><li><a href="tutorial-Query Examples.html">Query Examples</a></li></ul>
|
659 | </nav>
|
660 |
|
661 | <br class="clear">
|
662 |
|
663 | <footer>
|
664 | Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.4.0</a> on Sun Dec 18 2016 19:39:51 GMT-0500 (Eastern Standard Time)
|
665 | </footer>
|
666 |
|
667 | <script> prettyPrint(); </script>
|
668 | <script src="scripts/linenumber.js"> </script>
|
669 | </body>
|
670 | </html>
|