Commit ea9a9ec4 authored by Romain Courteaud's avatar Romain Courteaud

[indexeddb] Support database indexes handling

Thanks to Preet Batth for his work on this topic.
parent 96bf766f
...@@ -43,14 +43,15 @@ ...@@ -43,14 +43,15 @@
/*jslint nomen: true */ /*jslint nomen: true */
/*global indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, /*global indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest,
DOMError, Event*/ DOMError, Set*/
(function (indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, (function (indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest,
DOMError) { DOMError, Set) {
"use strict"; "use strict";
// Read only as changing it can lead to data corruption // Read only as changing it can lead to data corruption
var UNITE = 2000000; var UNITE = 2000000,
INDEX_PREFIX = 'doc.';
function IndexedDBStorage(description) { function IndexedDBStorage(description) {
if (typeof description.database !== "string" || if (typeof description.database !== "string" ||
...@@ -59,6 +60,8 @@ ...@@ -59,6 +60,8 @@
"must be a non-empty string"); "must be a non-empty string");
} }
this._database_name = "jio:" + description.database; this._database_name = "jio:" + description.database;
this._version = description.version;
this._index_key_list = description.index_key_list || [];
} }
IndexedDBStorage.prototype.hasCapacity = function (name) { IndexedDBStorage.prototype.hasCapacity = function (name) {
...@@ -69,35 +72,65 @@ ...@@ -69,35 +72,65 @@
return key_list.join("_"); return key_list.join("_");
} }
function handleUpgradeNeeded(evt) { function handleUpgradeNeeded(evt, index_key_list) {
var db = evt.target.result, var db = evt.target.result,
store; store,
current_store_list = Array.from(db.objectStoreNames),
current_index_list,
i,
index_key;
if (current_store_list.indexOf("metadata") === -1) {
store = db.createObjectStore("metadata", {
keyPath: "_id",
autoIncrement: false
});
// It is not possible to use openKeyCursor on keypath directly
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=19955
store.createIndex("_id", "_id", {unique: true});
} else {
store = evt.target.transaction.objectStore("metadata");
}
store = db.createObjectStore("metadata", { current_index_list = new Set(store.indexNames);
keyPath: "_id", current_index_list.delete("_id");
autoIncrement: false for (i = 0; i < index_key_list.length; i += 1) {
}); // Prefix the index name to prevent conflict with _id
// It is not possible to use openKeyCursor on keypath directly index_key = INDEX_PREFIX + index_key_list[i];
// https://www.w3.org/Bugs/Public/show_bug.cgi?id=19955 if (current_index_list.has(index_key)) {
store.createIndex("_id", "_id", {unique: true}); current_index_list.delete(index_key);
} else {
store.createIndex(index_key, index_key,
{unique: false});
}
}
current_index_list = Array.from(current_index_list);
for (i = 0; i < current_index_list.length; i += 1) {
store.deleteIndex(current_index_list[i]);
}
store = db.createObjectStore("attachment", { if (current_store_list.indexOf("attachment") === -1) {
keyPath: "_key_path", store = db.createObjectStore("attachment", {
autoIncrement: false keyPath: "_key_path",
}); autoIncrement: false
store.createIndex("_id", "_id", {unique: false}); });
store.createIndex("_id", "_id", {unique: false});
}
store = db.createObjectStore("blob", { if (current_store_list.indexOf("blob") === -1) {
keyPath: "_key_path", store = db.createObjectStore("blob", {
autoIncrement: false keyPath: "_key_path",
}); autoIncrement: false
store.createIndex("_id_attachment", });
["_id", "_attachment"], {unique: false}); store.createIndex("_id_attachment",
store.createIndex("_id", "_id", {unique: false}); ["_id", "_attachment"], {unique: false});
store.createIndex("_id", "_id", {unique: false});
}
} }
function waitForOpenIndexedDB(db_name, callback) { function waitForOpenIndexedDB(storage, callback) {
var request; var request,
db_name = storage._database_name;
function canceller() { function canceller() {
if ((request !== undefined) && (request.result !== undefined)) { if ((request !== undefined) && (request.result !== undefined)) {
...@@ -107,7 +140,7 @@ ...@@ -107,7 +140,7 @@
function resolver(resolve, reject) { function resolver(resolve, reject) {
// Open DB // // Open DB //
request = indexedDB.open(db_name); request = indexedDB.open(db_name, storage._version);
request.onerror = function (error) { request.onerror = function (error) {
canceller(); canceller();
if ((error !== undefined) && if ((error !== undefined) &&
...@@ -135,7 +168,9 @@ ...@@ -135,7 +168,9 @@
}; };
// Create DB if necessary // // Create DB if necessary //
request.onupgradeneeded = handleUpgradeNeeded; request.onupgradeneeded = function (evt) {
handleUpgradeNeeded(evt, storage._index_key_list);
};
request.onversionchange = function () { request.onversionchange = function () {
canceller(); canceller();
...@@ -233,7 +268,7 @@ ...@@ -233,7 +268,7 @@
function pushIncludedMetadata(cursor) { function pushIncludedMetadata(cursor) {
result_list.push({ result_list.push({
"id": cursor.key, "id": cursor.primaryKey,
"value": {}, "value": {},
"doc": cursor.value.doc "doc": cursor.value.doc
}); });
...@@ -241,24 +276,25 @@ ...@@ -241,24 +276,25 @@
function pushMetadata(cursor) { function pushMetadata(cursor) {
result_list.push({ result_list.push({
"id": cursor.key, "id": cursor.primaryKey,
"value": {} "value": {}
}); });
} }
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(context._database_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["metadata"], "readonly", return waitForTransaction(db, ["metadata"], "readonly",
function (tx) { function (tx) {
var key = "_id";
if (options.include_docs === true) { if (options.include_docs === true) {
return waitForAllSynchronousCursor( return waitForAllSynchronousCursor(
tx.objectStore("metadata").index("_id").openCursor(), tx.objectStore("metadata").index(key).openCursor(),
pushIncludedMetadata pushIncludedMetadata
); );
} }
return waitForAllSynchronousCursor( return waitForAllSynchronousCursor(
tx.objectStore("metadata").index("_id").openKeyCursor(), tx.objectStore("metadata").index(key).openKeyCursor(),
pushMetadata pushMetadata
); );
}); });
...@@ -273,7 +309,7 @@ ...@@ -273,7 +309,7 @@
var context = this; var context = this;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(context._database_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["metadata"], "readonly", return waitForTransaction(db, ["metadata"], "readonly",
function (tx) { function (tx) {
return waitForIDBRequest(tx.objectStore("metadata").get(id)); return waitForIDBRequest(tx.objectStore("metadata").get(id));
...@@ -301,7 +337,7 @@ ...@@ -301,7 +337,7 @@
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(context._database_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["metadata", "attachment"], "readonly", return waitForTransaction(db, ["metadata", "attachment"], "readonly",
function (tx) { function (tx) {
return RSVP.all([ return RSVP.all([
...@@ -330,7 +366,7 @@ ...@@ -330,7 +366,7 @@
}; };
IndexedDBStorage.prototype.put = function (id, metadata) { IndexedDBStorage.prototype.put = function (id, metadata) {
return waitForOpenIndexedDB(this._database_name, function (db) { return waitForOpenIndexedDB(this, function (db) {
return waitForTransaction(db, ["metadata"], "readwrite", return waitForTransaction(db, ["metadata"], "readwrite",
function (tx) { function (tx) {
return waitForIDBRequest(tx.objectStore("metadata").put({ return waitForIDBRequest(tx.objectStore("metadata").put({
...@@ -342,7 +378,7 @@ ...@@ -342,7 +378,7 @@
}; };
IndexedDBStorage.prototype.remove = function (id) { IndexedDBStorage.prototype.remove = function (id) {
return waitForOpenIndexedDB(this._database_name, function (db) { return waitForOpenIndexedDB(this, function (db) {
return waitForTransaction(db, ["metadata", "attachment", "blob"], return waitForTransaction(db, ["metadata", "attachment", "blob"],
"readwrite", function (tx) { "readwrite", function (tx) {
...@@ -386,10 +422,10 @@ ...@@ -386,10 +422,10 @@
if (options === undefined) { if (options === undefined) {
options = {}; options = {};
} }
var db_name = this._database_name, var start,
start,
end, end,
array_buffer_list = []; array_buffer_list = [],
context = this;
start = options.start || 0; start = options.start || 0;
end = options.end; end = options.end;
...@@ -410,7 +446,7 @@ ...@@ -410,7 +446,7 @@
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(db_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["blob"], "readonly", return waitForTransaction(db, ["blob"], "readonly",
function (tx) { function (tx) {
var key_path = buildKeyPath([id, name]), var key_path = buildKeyPath([id, name]),
...@@ -488,7 +524,7 @@ ...@@ -488,7 +524,7 @@
// Request the full blob // Request the full blob
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
return waitForOpenIndexedDB(db_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["attachment", "blob"], "readonly", return waitForTransaction(db, ["attachment", "blob"], "readonly",
function (tx) { function (tx) {
var key_path = buildKeyPath([id, name]), var key_path = buildKeyPath([id, name]),
...@@ -555,7 +591,7 @@ ...@@ -555,7 +591,7 @@
}; };
IndexedDBStorage.prototype.putAttachment = function (id, name, blob) { IndexedDBStorage.prototype.putAttachment = function (id, name, blob) {
var db_name = this._database_name; var context = this;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
// Split the blob first // Split the blob first
...@@ -573,7 +609,7 @@ ...@@ -573,7 +609,7 @@
handled_size += UNITE; handled_size += UNITE;
} }
return waitForOpenIndexedDB(db_name, function (db) { return waitForOpenIndexedDB(context, function (db) {
return waitForTransaction(db, ["attachment", "blob"], "readwrite", return waitForTransaction(db, ["attachment", "blob"], "readwrite",
function (tx) { function (tx) {
var blob_store, var blob_store,
...@@ -640,7 +676,7 @@ ...@@ -640,7 +676,7 @@
}; };
IndexedDBStorage.prototype.removeAttachment = function (id, name) { IndexedDBStorage.prototype.removeAttachment = function (id, name) {
return waitForOpenIndexedDB(this._database_name, function (db) { return waitForOpenIndexedDB(this, function (db) {
return waitForTransaction(db, ["attachment", "blob"], "readwrite", return waitForTransaction(db, ["attachment", "blob"], "readwrite",
function (tx) { function (tx) {
var promise_list = [], var promise_list = [],
...@@ -672,4 +708,5 @@ ...@@ -672,4 +708,5 @@
}; };
jIO.addStorage("indexeddb", IndexedDBStorage); jIO.addStorage("indexeddb", IndexedDBStorage);
}(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, DOMError)); }(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, DOMError,
Set));
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment