123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389 |
- // Copyright 2011 The Closure Library Authors. All Rights Reserved.
- //
- // Licensed under the Apache License, Version 2.0 (the "License");
- // you may not use this file except in compliance with the License.
- // You may obtain a copy of the License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software
- // distributed under the License is distributed on an "AS-IS" BASIS,
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- // See the License for the specific language governing permissions and
- // limitations under the License.
- /**
- * @fileoverview Wrapper for an IndexedDB object store.
- *
- */
- goog.provide('goog.db.ObjectStore');
- goog.require('goog.async.Deferred');
- goog.require('goog.db.Cursor');
- goog.require('goog.db.Error');
- goog.require('goog.db.Index');
- goog.require('goog.db.KeyRange');
- goog.require('goog.debug');
- goog.require('goog.events');
- /**
- * Creates an IDBObjectStore wrapper object. Object stores have methods for
- * storing and retrieving records, and are accessed through a transaction
- * object. They also have methods for creating indexes associated with the
- * object store. They can only be created when setting the version of the
- * database. Should not be created directly, access object stores through
- * transactions.
- * @see goog.db.UpgradeNeededCallback
- * @see goog.db.Transaction#objectStore
- *
- * @param {!IDBObjectStore} store The backing IndexedDb object.
- * @constructor
- * @final
- *
- * TODO(arthurhsu): revisit msg in exception and errors in this class. In newer
- * Chrome (v22+) the error/request come with a DOM error string that is
- * already very descriptive.
- */
- goog.db.ObjectStore = function(store) {
- /**
- * Underlying IndexedDB object store object.
- *
- * @type {!IDBObjectStore}
- * @private
- */
- this.store_ = store;
- };
- /**
- * @return {string} The name of the object store.
- */
- goog.db.ObjectStore.prototype.getName = function() {
- return this.store_.name;
- };
- /**
- * Helper function for put and add.
- *
- * @param {string} fn Function name to call on the object store.
- * @param {string} msg Message to give to the error.
- * @param {*} value Value to insert into the object store.
- * @param {IDBKeyType=} opt_key The key to use.
- * @return {!goog.async.Deferred} The resulting deferred request.
- * @private
- */
- goog.db.ObjectStore.prototype.insert_ = function(fn, msg, value, opt_key) {
- // TODO(user): refactor wrapping an IndexedDB request in a Deferred by
- // creating a higher-level abstraction for it (mostly affects here and
- // goog.db.Index)
- var d = new goog.async.Deferred();
- var request;
- try {
- // put or add with (value, undefined) throws an error, so we need to check
- // for undefined ourselves
- if (opt_key) {
- request = this.store_[fn](value, opt_key);
- } else {
- request = this.store_[fn](value);
- }
- } catch (ex) {
- msg += goog.debug.deepExpose(value);
- if (opt_key) {
- msg += ', with key ' + goog.debug.deepExpose(opt_key);
- }
- d.errback(goog.db.Error.fromException(ex, msg));
- return d;
- }
- request.onsuccess = function(ev) {
- d.callback(ev.target.result);
- };
- request.onerror = function(ev) {
- msg += goog.debug.deepExpose(value);
- if (opt_key) {
- msg += ', with key ' + goog.debug.deepExpose(opt_key);
- }
- d.errback(goog.db.Error.fromRequest(ev.target, msg));
- };
- return d;
- };
- /**
- * Adds an object to the object store. Replaces existing objects with the
- * same key.
- *
- * @param {*} value The value to put.
- * @param {IDBKeyType=} opt_key The key to use. Cannot be used if the
- * keyPath was specified for the object store. If the keyPath was not
- * specified but autoIncrement was not enabled, it must be used.
- * @return {!goog.async.Deferred} The deferred put request.
- */
- goog.db.ObjectStore.prototype.put = function(value, opt_key) {
- return this.insert_(
- 'put', 'putting into ' + this.getName() + ' with value', value, opt_key);
- };
- /**
- * Adds an object to the object store. Requires that there is no object with
- * the same key already present.
- *
- * @param {*} value The value to add.
- * @param {IDBKeyType=} opt_key The key to use. Cannot be used if the
- * keyPath was specified for the object store. If the keyPath was not
- * specified but autoIncrement was not enabled, it must be used.
- * @return {!goog.async.Deferred} The deferred add request.
- */
- goog.db.ObjectStore.prototype.add = function(value, opt_key) {
- return this.insert_(
- 'add', 'adding into ' + this.getName() + ' with value ', value, opt_key);
- };
- /**
- * Removes an object from the store. No-op if there is no object present with
- * the given key.
- *
- * @param {IDBKeyType|!goog.db.KeyRange} keyOrRange The key or range to remove
- * objects under.
- * @return {!goog.async.Deferred} The deferred remove request.
- */
- goog.db.ObjectStore.prototype.remove = function(keyOrRange) {
- var d = new goog.async.Deferred();
- var request;
- try {
- request = this.store_['delete'](
- keyOrRange instanceof goog.db.KeyRange ? keyOrRange.range() :
- keyOrRange);
- } catch (err) {
- var msg = 'removing from ' + this.getName() + ' with key ' +
- goog.debug.deepExpose(keyOrRange);
- d.errback(goog.db.Error.fromException(err, msg));
- return d;
- }
- request.onsuccess = function(ev) { d.callback(); };
- var self = this;
- request.onerror = function(ev) {
- var msg = 'removing from ' + self.getName() + ' with key ' +
- goog.debug.deepExpose(keyOrRange);
- d.errback(goog.db.Error.fromRequest(ev.target, msg));
- };
- return d;
- };
- /**
- * Gets an object from the store. If no object is present with that key
- * the result is {@code undefined}.
- *
- * @param {IDBKeyType} key The key to look up.
- * @return {!goog.async.Deferred} The deferred get request.
- */
- goog.db.ObjectStore.prototype.get = function(key) {
- var d = new goog.async.Deferred();
- var request;
- try {
- request = this.store_.get(key);
- } catch (err) {
- var msg = 'getting from ' + this.getName() + ' with key ' +
- goog.debug.deepExpose(key);
- d.errback(goog.db.Error.fromException(err, msg));
- return d;
- }
- request.onsuccess = function(ev) { d.callback(ev.target.result); };
- var self = this;
- request.onerror = function(ev) {
- var msg = 'getting from ' + self.getName() + ' with key ' +
- goog.debug.deepExpose(key);
- d.errback(goog.db.Error.fromRequest(ev.target, msg));
- };
- return d;
- };
- /**
- * Gets all objects from the store and returns them as an array.
- *
- * @param {!goog.db.KeyRange=} opt_range The key range. If undefined iterates
- * over the whole object store.
- * @param {!goog.db.Cursor.Direction=} opt_direction The direction. If undefined
- * moves in a forward direction with duplicates.
- * @return {!goog.async.Deferred} The deferred getAll request.
- */
- goog.db.ObjectStore.prototype.getAll = function(opt_range, opt_direction) {
- var d = new goog.async.Deferred();
- var cursor;
- try {
- cursor = this.openCursor(opt_range, opt_direction);
- } catch (err) {
- d.errback(err);
- return d;
- }
- var result = [];
- goog.events.listen(cursor, goog.db.Cursor.EventType.NEW_DATA, function() {
- result.push(cursor.getValue());
- cursor.next();
- });
- goog.events.listenOnce(
- cursor,
- [goog.db.Cursor.EventType.ERROR, goog.db.Cursor.EventType.COMPLETE],
- function(evt) {
- cursor.dispose();
- if (evt.type == goog.db.Cursor.EventType.COMPLETE) {
- d.callback(result);
- } else {
- d.errback();
- }
- });
- return d;
- };
- /**
- * Opens a cursor over the specified key range. Returns a cursor object which is
- * able to iterate over the given range.
- *
- * Example usage:
- *
- * <code>
- * var cursor = objectStore.openCursor(goog.db.Range.bound('a', 'c'));
- *
- * var key = goog.events.listen(
- * cursor, goog.db.Cursor.EventType.NEW_DATA, function() {
- * // Do something with data.
- * cursor.next();
- * });
- *
- * goog.events.listenOnce(
- * cursor, goog.db.Cursor.EventType.COMPLETE, function() {
- * // Clean up listener, and perform a finishing operation on the data.
- * goog.events.unlistenByKey(key);
- * });
- * </code>
- *
- * @param {!goog.db.KeyRange=} opt_range The key range. If undefined iterates
- * over the whole object store.
- * @param {!goog.db.Cursor.Direction=} opt_direction The direction. If undefined
- * moves in a forward direction with duplicates.
- * @return {!goog.db.Cursor} The cursor.
- * @throws {goog.db.Error} If there was a problem opening the cursor.
- */
- goog.db.ObjectStore.prototype.openCursor = function(opt_range, opt_direction) {
- return goog.db.Cursor.openCursor(this.store_, opt_range, opt_direction);
- };
- /**
- * Deletes all objects from the store.
- *
- * @return {!goog.async.Deferred} The deferred clear request.
- */
- goog.db.ObjectStore.prototype.clear = function() {
- var msg = 'clearing store ' + this.getName();
- var d = new goog.async.Deferred();
- var request;
- try {
- request = this.store_.clear();
- } catch (err) {
- d.errback(goog.db.Error.fromException(err, msg));
- return d;
- }
- request.onsuccess = function(ev) { d.callback(); };
- request.onerror = function(ev) {
- d.errback(goog.db.Error.fromRequest(ev.target, msg));
- };
- return d;
- };
- /**
- * Creates an index in this object store. Can only be called inside a
- * {@link goog.db.UpgradeNeededCallback}.
- *
- * @param {string} name Name of the index to create.
- * @param {string|!Array<string>} keyPath Attribute or array of attributes to
- * index on.
- * @param {!Object=} opt_parameters Optional parameters object. The only
- * available option is unique, which defaults to false. If unique is true,
- * the index will enforce that there is only ever one object in the object
- * store for each unique value it indexes on.
- * @return {!goog.db.Index} The newly created, wrapped index.
- * @throws {goog.db.Error} In case of an error creating the index.
- */
- goog.db.ObjectStore.prototype.createIndex = function(
- name, keyPath, opt_parameters) {
- try {
- return new goog.db.Index(
- this.store_.createIndex(name, keyPath, opt_parameters));
- } catch (ex) {
- var msg = 'creating new index ' + name + ' with key path ' + keyPath;
- throw goog.db.Error.fromException(ex, msg);
- }
- };
- /**
- * Gets an index.
- *
- * @param {string} name Name of the index to fetch.
- * @return {!goog.db.Index} The requested wrapped index.
- * @throws {goog.db.Error} In case of an error getting the index.
- */
- goog.db.ObjectStore.prototype.getIndex = function(name) {
- try {
- return new goog.db.Index(this.store_.index(name));
- } catch (ex) {
- var msg = 'getting index ' + name;
- throw goog.db.Error.fromException(ex, msg);
- }
- };
- /**
- * Deletes an index from the object store. Can only be called inside a
- * {@link goog.db.UpgradeNeededCallback}.
- *
- * @param {string} name Name of the index to delete.
- * @throws {goog.db.Error} In case of an error deleting the index.
- */
- goog.db.ObjectStore.prototype.deleteIndex = function(name) {
- try {
- this.store_.deleteIndex(name);
- } catch (ex) {
- var msg = 'deleting index ' + name;
- throw goog.db.Error.fromException(ex, msg);
- }
- };
- /**
- * Gets number of records within a key range.
- *
- * @param {!goog.db.KeyRange=} opt_range The key range. If undefined, this will
- * count all records in the object store.
- * @return {!goog.async.Deferred} The deferred number of records.
- */
- goog.db.ObjectStore.prototype.count = function(opt_range) {
- var d = new goog.async.Deferred();
- try {
- var range = opt_range ? opt_range.range() : null;
- var request = this.store_.count(range);
- request.onsuccess = function(ev) { d.callback(ev.target.result); };
- var self = this;
- request.onerror = function(ev) {
- d.errback(goog.db.Error.fromRequest(ev.target, self.getName()));
- };
- } catch (ex) {
- d.errback(goog.db.Error.fromException(ex, this.getName()));
- }
- return d;
- };
|