index.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview Wrapper for an IndexedDB index.
  16. *
  17. */
  18. goog.provide('goog.db.Index');
  19. goog.require('goog.async.Deferred');
  20. goog.require('goog.db.Cursor');
  21. goog.require('goog.db.Error');
  22. goog.require('goog.db.KeyRange');
  23. goog.require('goog.debug');
  24. /**
  25. * Creates an IDBIndex wrapper object. Indexes are associated with object
  26. * stores and provide methods for looking up objects based on their non-key
  27. * properties. Should not be created directly, access through the object store
  28. * it belongs to.
  29. * @see goog.db.ObjectStore#getIndex
  30. *
  31. * @param {!IDBIndex} index Underlying IDBIndex object.
  32. * @constructor
  33. * @final
  34. */
  35. goog.db.Index = function(index) {
  36. /**
  37. * Underlying IndexedDB index object.
  38. *
  39. * @type {!IDBIndex}
  40. * @private
  41. */
  42. this.index_ = index;
  43. };
  44. /**
  45. * @return {string} Name of the index.
  46. */
  47. goog.db.Index.prototype.getName = function() {
  48. return this.index_.name;
  49. };
  50. /**
  51. * @return {*} Key path of the index.
  52. */
  53. goog.db.Index.prototype.getKeyPath = function() {
  54. return this.index_.keyPath;
  55. };
  56. /**
  57. * @return {boolean} True if the index enforces that there is only one object
  58. * for each unique value it indexes on.
  59. */
  60. goog.db.Index.prototype.isUnique = function() {
  61. return this.index_.unique;
  62. };
  63. /**
  64. * Helper function for get and getKey.
  65. *
  66. * @param {string} fn Function name to call on the index to get the request.
  67. * @param {string} msg Message to give to the error.
  68. * @param {IDBKeyType} key The key to look up in the index.
  69. * @return {!goog.async.Deferred} The resulting deferred object.
  70. * @private
  71. */
  72. goog.db.Index.prototype.get_ = function(fn, msg, key) {
  73. var d = new goog.async.Deferred();
  74. var request;
  75. try {
  76. request = this.index_[fn](key);
  77. } catch (err) {
  78. msg += ' with key ' + goog.debug.deepExpose(key);
  79. d.errback(goog.db.Error.fromException(err, msg));
  80. return d;
  81. }
  82. request.onsuccess = function(ev) { d.callback(ev.target.result); };
  83. request.onerror = function(ev) {
  84. msg += ' with key ' + goog.debug.deepExpose(key);
  85. d.errback(goog.db.Error.fromRequest(ev.target, msg));
  86. };
  87. return d;
  88. };
  89. /**
  90. * Fetches a single object from the object store. Even if there are multiple
  91. * objects that match the given key, this method will get only one of them.
  92. *
  93. * @param {IDBKeyType} key Key to look up in the index.
  94. * @return {!goog.async.Deferred} The deferred object for the given record.
  95. */
  96. goog.db.Index.prototype.get = function(key) {
  97. return this.get_('get', 'getting from index ' + this.getName(), key);
  98. };
  99. /**
  100. * Looks up a single object from the object store and gives back the key that
  101. * it's listed under in the object store. Even if there are multiple records
  102. * that match the given key, this method returns the first.
  103. *
  104. * @param {IDBKeyType} key Key to look up in the index.
  105. * @return {!goog.async.Deferred} The deferred key for the record that matches
  106. * the key.
  107. */
  108. goog.db.Index.prototype.getKey = function(key) {
  109. return this.get_('getKey', 'getting key from index ' + this.getName(), key);
  110. };
  111. /**
  112. * Helper function for getAll and getAllKeys.
  113. *
  114. * @param {string} fn Function name to call on the index to get the request.
  115. * @param {string} msg Message to give to the error.
  116. * @param {IDBKeyType=} opt_key Key to look up in the index.
  117. * @return {!goog.async.Deferred} The resulting deferred array of objects.
  118. * @private
  119. */
  120. goog.db.Index.prototype.getAll_ = function(fn, msg, opt_key) {
  121. // This is the most common use of IDBKeyRange. If more specific uses of
  122. // cursors are needed then a full wrapper should be created.
  123. var IDBKeyRange = goog.global.IDBKeyRange || goog.global.webkitIDBKeyRange;
  124. var d = new goog.async.Deferred();
  125. var request;
  126. try {
  127. if (opt_key) {
  128. request = this.index_[fn](IDBKeyRange.only(opt_key));
  129. } else {
  130. request = this.index_[fn]();
  131. }
  132. } catch (err) {
  133. if (opt_key) {
  134. msg += ' for key ' + goog.debug.deepExpose(opt_key);
  135. }
  136. d.errback(goog.db.Error.fromException(err, msg));
  137. return d;
  138. }
  139. var result = [];
  140. request.onsuccess = function(ev) {
  141. var cursor = ev.target.result;
  142. if (cursor) {
  143. if (fn == 'openKeyCursor') {
  144. // openKeyCursor returns a cursor with undefined key/value. See
  145. // https://w3c.github.io/IndexedDB/#dom-idbobjectstore-openkeycursor.
  146. result.push(cursor.primaryKey);
  147. } else {
  148. result.push(cursor.value);
  149. }
  150. cursor['continue']();
  151. } else {
  152. d.callback(result);
  153. }
  154. };
  155. request.onerror = function(ev) {
  156. if (opt_key) {
  157. msg += ' for key ' + goog.debug.deepExpose(opt_key);
  158. }
  159. d.errback(goog.db.Error.fromRequest(ev.target, msg));
  160. };
  161. return d;
  162. };
  163. /**
  164. * Gets all indexed objects. If the key is provided, gets all indexed objects
  165. * that match the key instead.
  166. *
  167. * @param {IDBKeyType=} opt_key Key to look up in the index.
  168. * @return {!goog.async.Deferred} A deferred array of objects that match the
  169. * key.
  170. */
  171. goog.db.Index.prototype.getAll = function(opt_key) {
  172. return this.getAll_(
  173. 'openCursor', 'getting all from index ' + this.getName(), opt_key);
  174. };
  175. /**
  176. * Gets the keys to look up all the indexed objects. If the key is provided,
  177. * gets all records for objects that match the key instead.
  178. *
  179. * @param {IDBKeyType=} opt_key Key to look up in the index.
  180. * @return {!goog.async.Deferred} A deferred array of keys for objects that
  181. * match the key.
  182. */
  183. goog.db.Index.prototype.getAllKeys = function(opt_key) {
  184. return this.getAll_(
  185. 'openKeyCursor', 'getting all keys from index ' + this.getName(),
  186. opt_key);
  187. };
  188. /**
  189. * Opens a cursor over the specified key range. Returns a cursor object which is
  190. * able to iterate over the given range.
  191. *
  192. * Example usage:
  193. *
  194. * <code>
  195. * var cursor = index.openCursor(goog.db.KeyRange.bound('a', 'c'));
  196. *
  197. * var key = goog.events.listen(
  198. * cursor, goog.db.Cursor.EventType.NEW_DATA,
  199. * function() {
  200. * // Do something with data.
  201. * cursor.next();
  202. * });
  203. *
  204. * goog.events.listenOnce(
  205. * cursor, goog.db.Cursor.EventType.COMPLETE,
  206. * function() {
  207. * // Clean up listener, and perform a finishing operation on the data.
  208. * goog.events.unlistenByKey(key);
  209. * });
  210. * </code>
  211. *
  212. * @param {!goog.db.KeyRange=} opt_range The key range. If undefined iterates
  213. * over the whole object store.
  214. * @param {!goog.db.Cursor.Direction=} opt_direction The direction. If undefined
  215. * moves in a forward direction with duplicates.
  216. * @return {!goog.db.Cursor} The cursor.
  217. * @throws {goog.db.Error} If there was a problem opening the cursor.
  218. */
  219. goog.db.Index.prototype.openCursor = function(opt_range, opt_direction) {
  220. return goog.db.Cursor.openCursor(this.index_, opt_range, opt_direction);
  221. };