Skip to content

Instantly share code, notes, and snippets.

@odoe
Last active December 25, 2015 03:39
Show Gist options
  • Save odoe/6911676 to your computer and use it in GitHub Desktop.
Save odoe/6911676 to your computer and use it in GitHub Desktop.
IndexedDBStore and PouchDBStorefor holding ArcGIS Features.
/*global define */
/*jshint browser:true, laxcomma:true, newcap:false*/
/** Heavily influenced by https://github.com/pjekel/indexedDB **/
define([
'dojo/Deferred',
'dojo/_base/declare',
'dojo/_base/array',
'dojo/store/util/QueryResults',
'dojo/store/util/SimpleQueryEngine'
], function (Deferred, declare, array, QueryResults, SimpleQueryEngine) {
'use strict';
/** Remove null/undefined values from an array. */
function cleanArgs(args) {
return array.filter(args, function(arg) {
return !!arg;
});
}
/** Return transaction mode based on method. */
function transactionType(method) {
if ('add,put,delete'.indexOf(method) > -1) {
return 'readwrite';
} else {
return 'readonly';
}
}
/** Convert arguments to array. */
function toArray(args) {
return Array.prototype.slice.call(args, 1);
}
return declare(null, {
database: null,
name: null,
idbSupported: false,
keyPath: 'id',
indexes: null,
queryEngine: SimpleQueryEngine,
_db: null,
/**
* @example
* var indexedDBStore = new IndexedDBStore('csd', 'test', {
* version: 3,
* keyPath: 'attributes.AIN',
* indexes: ['attributes.AIN'],
* indexnames: ['ain'],
* indexOptions: { unique: true }
* });
*
* @constructor
* @param {string} database - database name.
* @param {string} name - objectstore name.
* @param {object} options - IndexedDB parameters.
*/
constructor: function (database, name, options) {
this.options = this.options || {};
this.database = database;
this.name = name;
this._init();
},
// public methods
/**
* @example
* indexedDBStore.add(feature).then(function(result) {
* logger.debug('added my feature to indexedDB', result);
* });
*
* @param {object} object- Item to add to database.
* @param {object} options - key or id to use in add function.
*/
add: function (object, options) {
var key = options ? options.key || options.id : null;
return this._exec('add', object, key);
},
/**
* @example:
* indexedDBStore.get('3142007045').then(function(result) {
* logger.debug('did i find it in indexedDB', result);
* });
*
* @param {object} options - key or id to use in add function.
*/
get: function (key) {
return this._exec('get', key);
},
/**
* @example:
* indexedDBStore.query(function(o) {
* return o.attributes.TRA === '7080';
* }).then(function(results) {
* logger.debug('indexeddb query results: ', results);
* });
*
* @param {Function} qry - Query function to filter results.
* @param {object} options - options for query.
*/
query: function (qry, options) {
var deferred = new Deferred()
, self = this
, results = []
, trans = this._db.transaction([this.name], 'readonly')
, store = trans.objectStore(this.name)
, cursor = store.openCursor();
cursor.onsuccess = function (e) {
var res = e.target.result;
if (res) {
results.push(res.value);
res.continue();
}
};
cursor.onerror = function (e) {
deferred.reject(e);
};
trans.oncomplete = function () {
deferred.resolve(self.queryEngine(qry, options)(results));
};
return QueryResults(deferred.promise);
},
// private methods
// based on https://github.com/pjekel/indexedDB/blob/master/store/Store.js
_exec: function(method) {
var deferred = new Deferred()
, transMode = transactionType(method);
try {
var trans = this._db.transaction([this.name], transMode)
, store = trans.objectStore(this.name)
, _method = store[method]
, args = toArray(arguments)
, request = _method.apply(store, cleanArgs(args));
request.onsuccess = function() {
deferred.resolve(this.result);
};
request.onerror = function() {
deferred.reject(this.error);
};
} catch(err) {
console.warn('transaction error:', err);
deferred.reject(err);
}
return deferred.promise;
},
_init: function () {
var indexedDB = window.indexedDB || window.webkitIndexedDB ||
window.mozIndexedDB;
this.idbSupported = !!indexedDB;
if (this.idbSupported) {
alert('IndexedDB Supported');
if (this.database && this.name) {
// testing
//indexedDB.deleteDatabase(this.database);
var openRequest = indexedDB.open(this.database, this.version || 1)
, self = this;
openRequest.onupgradeneeded = function () {
var db = this.result
, store
, trans;
// testing
if (db.objectStoreNames.contains(self.name)) {
//db.deleteObjectStore(this.name);
}
if (!db.objectStoreNames.contains(self.name)) {
store = db.createObjectStore(self.name, { keyPath: self.keyPath });
store.createIndex(self.indexnames.toString(), self.indexes,
self.indexOptions || {});
}
trans = db.transaction(self.name);
if (!store) {
store = trans.objectStore(self.name);
}
self.indexes = store.indexNames;
self.keyPath = store.keyPath;
};
openRequest.onsuccess = function () {
self._db = this.result;
};
openRequest.onerror = function () {
console.warn(this.error);
};
} else {
throw new Error('IndexedDBStore#constructor() requires database and store name parameters');
}
} else {
// TODO - gracefully fall back to local storage
//throw new Error('IndexedDBStore#constructor() IndexedDB is not supported on your browser');
alert('IndexedDB not supported');
}
}
});
});
/*global define, alert */
/*jshint browser:true, laxcomma:true, newcap:false*/
define([
'dojo/Deferred',
'dojo/_base/declare',
'dojo/_base/array',
'dojo/store/util/QueryResults'
], function (
Deferred,
declare, arrayUtil,
QueryResults
) {
'use strict';
return declare(null, {
database: null,
name: null,
_db: null,
/**
* @example
* var pouchDBStore = new PouchStore('dbname', 'test', {
* version: 3,
* keyPath: 'attributes.AIN',
* indexes: ['attributes.AIN'],
* indexnames: ['ain'],
* indexOptions: { unique: true }
* });
*
* @constructor
* @param {string} database - database name.
* @param {string} name - objectstore name.
* @param {object} options - IndexedDB parameters.
*/
constructor: function (database, name, options) {
this.options = options || {};
this.database = database;
this.name = name;
this._init();
},
// public methods
/**
* @example
* pouchStore.add(feature).then(function(result) {
* logger.debug('added my feature to indexedDB', result);
* });
*
* @param {object} object- Item to add to database.
*/
add: function (object) {
var deferred = new Deferred();
this._db.put({
_id: new Date().toISOString(),
item: object
}, function (err, result) {
if (!err) {
//console.log('successfully add', result);
alert('Item saved locally');
deferred.resolve(result);
} else {
//console.warn('error adding', err);
alert('Error saving item locally: ' + err.message);
deferred.reject(err);
}
});
return deferred.promise;
},
getAll: function () {
var deferred = new Deferred();
this._db.allDocs({ include_docs: true }, function (err, response) {
if (!err) {
//console.warn('PouchStore#getAll - response: ', response);
alert('local data retrieved', response);
deferred.resolve(response.rows);
} else {
//console.warn('PouchStore#getAll - error: ', err);
alert('Error retrieving local data: ' + err.message);
deferred.reject(err);
}
});
return QueryResults(deferred.promise);
},
_init: function () {
this._db = new window.PouchDB(this.database);
}
});
});
@wlatif
Copy link

wlatif commented May 28, 2014

Hello Rene,

Can you provide a client application or sample code to use IndexedDBStore?

Regards,
Waseem

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment