Path: blob/trunk/third_party/closure/goog/db/objectstore.js
2868 views
// Copyright 2011 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Wrapper for an IndexedDB object store.16*17*/181920goog.provide('goog.db.ObjectStore');2122goog.require('goog.async.Deferred');23goog.require('goog.db.Cursor');24goog.require('goog.db.Error');25goog.require('goog.db.Index');26goog.require('goog.db.KeyRange');27goog.require('goog.debug');28goog.require('goog.events');29303132/**33* Creates an IDBObjectStore wrapper object. Object stores have methods for34* storing and retrieving records, and are accessed through a transaction35* object. They also have methods for creating indexes associated with the36* object store. They can only be created when setting the version of the37* database. Should not be created directly, access object stores through38* transactions.39* @see goog.db.UpgradeNeededCallback40* @see goog.db.Transaction#objectStore41*42* @param {!IDBObjectStore} store The backing IndexedDb object.43* @constructor44* @final45*46* TODO(arthurhsu): revisit msg in exception and errors in this class. In newer47* Chrome (v22+) the error/request come with a DOM error string that is48* already very descriptive.49*/50goog.db.ObjectStore = function(store) {51/**52* Underlying IndexedDB object store object.53*54* @type {!IDBObjectStore}55* @private56*/57this.store_ = store;58};596061/**62* @return {string} The name of the object store.63*/64goog.db.ObjectStore.prototype.getName = function() {65return this.store_.name;66};676869/**70* Helper function for put and add.71*72* @param {string} fn Function name to call on the object store.73* @param {string} msg Message to give to the error.74* @param {*} value Value to insert into the object store.75* @param {IDBKeyType=} opt_key The key to use.76* @return {!goog.async.Deferred} The resulting deferred request.77* @private78*/79goog.db.ObjectStore.prototype.insert_ = function(fn, msg, value, opt_key) {80// TODO(user): refactor wrapping an IndexedDB request in a Deferred by81// creating a higher-level abstraction for it (mostly affects here and82// goog.db.Index)83var d = new goog.async.Deferred();84var request;85try {86// put or add with (value, undefined) throws an error, so we need to check87// for undefined ourselves88if (opt_key) {89request = this.store_[fn](value, opt_key);90} else {91request = this.store_[fn](value);92}93} catch (ex) {94msg += goog.debug.deepExpose(value);95if (opt_key) {96msg += ', with key ' + goog.debug.deepExpose(opt_key);97}98d.errback(goog.db.Error.fromException(ex, msg));99return d;100}101request.onsuccess = function(ev) {102d.callback(ev.target.result);103};104request.onerror = function(ev) {105msg += goog.debug.deepExpose(value);106if (opt_key) {107msg += ', with key ' + goog.debug.deepExpose(opt_key);108}109d.errback(goog.db.Error.fromRequest(ev.target, msg));110};111return d;112};113114115/**116* Adds an object to the object store. Replaces existing objects with the117* same key.118*119* @param {*} value The value to put.120* @param {IDBKeyType=} opt_key The key to use. Cannot be used if the121* keyPath was specified for the object store. If the keyPath was not122* specified but autoIncrement was not enabled, it must be used.123* @return {!goog.async.Deferred} The deferred put request.124*/125goog.db.ObjectStore.prototype.put = function(value, opt_key) {126return this.insert_(127'put', 'putting into ' + this.getName() + ' with value', value, opt_key);128};129130131/**132* Adds an object to the object store. Requires that there is no object with133* the same key already present.134*135* @param {*} value The value to add.136* @param {IDBKeyType=} opt_key The key to use. Cannot be used if the137* keyPath was specified for the object store. If the keyPath was not138* specified but autoIncrement was not enabled, it must be used.139* @return {!goog.async.Deferred} The deferred add request.140*/141goog.db.ObjectStore.prototype.add = function(value, opt_key) {142return this.insert_(143'add', 'adding into ' + this.getName() + ' with value ', value, opt_key);144};145146147/**148* Removes an object from the store. No-op if there is no object present with149* the given key.150*151* @param {IDBKeyType|!goog.db.KeyRange} keyOrRange The key or range to remove152* objects under.153* @return {!goog.async.Deferred} The deferred remove request.154*/155goog.db.ObjectStore.prototype.remove = function(keyOrRange) {156var d = new goog.async.Deferred();157var request;158try {159request = this.store_['delete'](160keyOrRange instanceof goog.db.KeyRange ? keyOrRange.range() :161keyOrRange);162} catch (err) {163var msg = 'removing from ' + this.getName() + ' with key ' +164goog.debug.deepExpose(keyOrRange);165d.errback(goog.db.Error.fromException(err, msg));166return d;167}168request.onsuccess = function(ev) { d.callback(); };169var self = this;170request.onerror = function(ev) {171var msg = 'removing from ' + self.getName() + ' with key ' +172goog.debug.deepExpose(keyOrRange);173d.errback(goog.db.Error.fromRequest(ev.target, msg));174};175return d;176};177178179/**180* Gets an object from the store. If no object is present with that key181* the result is {@code undefined}.182*183* @param {IDBKeyType} key The key to look up.184* @return {!goog.async.Deferred} The deferred get request.185*/186goog.db.ObjectStore.prototype.get = function(key) {187var d = new goog.async.Deferred();188var request;189try {190request = this.store_.get(key);191} catch (err) {192var msg = 'getting from ' + this.getName() + ' with key ' +193goog.debug.deepExpose(key);194d.errback(goog.db.Error.fromException(err, msg));195return d;196}197request.onsuccess = function(ev) { d.callback(ev.target.result); };198var self = this;199request.onerror = function(ev) {200var msg = 'getting from ' + self.getName() + ' with key ' +201goog.debug.deepExpose(key);202d.errback(goog.db.Error.fromRequest(ev.target, msg));203};204return d;205};206207208/**209* Gets all objects from the store and returns them as an array.210*211* @param {!goog.db.KeyRange=} opt_range The key range. If undefined iterates212* over the whole object store.213* @param {!goog.db.Cursor.Direction=} opt_direction The direction. If undefined214* moves in a forward direction with duplicates.215* @return {!goog.async.Deferred} The deferred getAll request.216*/217goog.db.ObjectStore.prototype.getAll = function(opt_range, opt_direction) {218var d = new goog.async.Deferred();219var cursor;220try {221cursor = this.openCursor(opt_range, opt_direction);222} catch (err) {223d.errback(err);224return d;225}226227var result = [];228goog.events.listen(cursor, goog.db.Cursor.EventType.NEW_DATA, function() {229result.push(cursor.getValue());230cursor.next();231});232233goog.events.listenOnce(234cursor,235[goog.db.Cursor.EventType.ERROR, goog.db.Cursor.EventType.COMPLETE],236function(evt) {237cursor.dispose();238if (evt.type == goog.db.Cursor.EventType.COMPLETE) {239d.callback(result);240} else {241d.errback();242}243});244return d;245};246247248/**249* Opens a cursor over the specified key range. Returns a cursor object which is250* able to iterate over the given range.251*252* Example usage:253*254* <code>255* var cursor = objectStore.openCursor(goog.db.Range.bound('a', 'c'));256*257* var key = goog.events.listen(258* cursor, goog.db.Cursor.EventType.NEW_DATA, function() {259* // Do something with data.260* cursor.next();261* });262*263* goog.events.listenOnce(264* cursor, goog.db.Cursor.EventType.COMPLETE, function() {265* // Clean up listener, and perform a finishing operation on the data.266* goog.events.unlistenByKey(key);267* });268* </code>269*270* @param {!goog.db.KeyRange=} opt_range The key range. If undefined iterates271* over the whole object store.272* @param {!goog.db.Cursor.Direction=} opt_direction The direction. If undefined273* moves in a forward direction with duplicates.274* @return {!goog.db.Cursor} The cursor.275* @throws {goog.db.Error} If there was a problem opening the cursor.276*/277goog.db.ObjectStore.prototype.openCursor = function(opt_range, opt_direction) {278return goog.db.Cursor.openCursor(this.store_, opt_range, opt_direction);279};280281282/**283* Deletes all objects from the store.284*285* @return {!goog.async.Deferred} The deferred clear request.286*/287goog.db.ObjectStore.prototype.clear = function() {288var msg = 'clearing store ' + this.getName();289var d = new goog.async.Deferred();290var request;291try {292request = this.store_.clear();293} catch (err) {294d.errback(goog.db.Error.fromException(err, msg));295return d;296}297request.onsuccess = function(ev) { d.callback(); };298request.onerror = function(ev) {299d.errback(goog.db.Error.fromRequest(ev.target, msg));300};301return d;302};303304305/**306* Creates an index in this object store. Can only be called inside a307* {@link goog.db.UpgradeNeededCallback}.308*309* @param {string} name Name of the index to create.310* @param {string} keyPath Attribute to index on.311* @param {!Object=} opt_parameters Optional parameters object. The only312* available option is unique, which defaults to false. If unique is true,313* the index will enforce that there is only ever one object in the object314* store for each unique value it indexes on.315* @return {!goog.db.Index} The newly created, wrapped index.316* @throws {goog.db.Error} In case of an error creating the index.317*/318goog.db.ObjectStore.prototype.createIndex = function(319name, keyPath, opt_parameters) {320try {321return new goog.db.Index(322this.store_.createIndex(name, keyPath, opt_parameters));323} catch (ex) {324var msg = 'creating new index ' + name + ' with key path ' + keyPath;325throw goog.db.Error.fromException(ex, msg);326}327};328329330/**331* Gets an index.332*333* @param {string} name Name of the index to fetch.334* @return {!goog.db.Index} The requested wrapped index.335* @throws {goog.db.Error} In case of an error getting the index.336*/337goog.db.ObjectStore.prototype.getIndex = function(name) {338try {339return new goog.db.Index(this.store_.index(name));340} catch (ex) {341var msg = 'getting index ' + name;342throw goog.db.Error.fromException(ex, msg);343}344};345346347/**348* Deletes an index from the object store. Can only be called inside a349* {@link goog.db.UpgradeNeededCallback}.350*351* @param {string} name Name of the index to delete.352* @throws {goog.db.Error} In case of an error deleting the index.353*/354goog.db.ObjectStore.prototype.deleteIndex = function(name) {355try {356this.store_.deleteIndex(name);357} catch (ex) {358var msg = 'deleting index ' + name;359throw goog.db.Error.fromException(ex, msg);360}361};362363364/**365* Gets number of records within a key range.366*367* @param {!goog.db.KeyRange=} opt_range The key range. If undefined, this will368* count all records in the object store.369* @return {!goog.async.Deferred} The deferred number of records.370*/371goog.db.ObjectStore.prototype.count = function(opt_range) {372var d = new goog.async.Deferred();373374try {375var range = opt_range ? opt_range.range() : null;376var request = this.store_.count(range);377request.onsuccess = function(ev) { d.callback(ev.target.result); };378var self = this;379request.onerror = function(ev) {380d.errback(goog.db.Error.fromRequest(ev.target, self.getName()));381};382} catch (ex) {383d.errback(goog.db.Error.fromException(ex, this.getName()));384}385386return d;387};388389390