Commit 64b99388 authored by preetwinder's avatar preetwinder

add index keys

parent b08e246c
......@@ -43,10 +43,10 @@
/*jslint nomen: true */
/*global indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest,
DOMError, Event*/
DOMError, Event, Set, parseStringToObject*/
(function (indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest,
DOMError) {
DOMError, parseStringToObject) {
"use strict";
// Read only as changing it can lead to data corruption
......@@ -59,9 +59,14 @@
"must be a non-empty string");
}
this._database_name = "jio:" + description.database;
this._version = description.version;
this._index_keys = description.index_keys || [];
}
IndexedDBStorage.prototype.hasCapacity = function (name) {
if (name === "subquery") {
return this._index_keys;
}
return ((name === "list") || (name === "include"));
};
......@@ -69,10 +74,14 @@
return key_list.join("_");
}
function handleUpgradeNeeded(evt) {
function handleUpgradeNeeded(evt, index_keys) {
var db = evt.target.result,
store;
store,
current_stores = Array.from(db.objectStoreNames),
current_indices,
i;
if (current_stores.indexOf("metadata") === -1) {
store = db.createObjectStore("metadata", {
keyPath: "_id",
autoIncrement: false
......@@ -80,13 +89,34 @@
// 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");
}
current_indices = new Set(store.indexNames);
current_indices.delete("_id");
for (i = 0; i < index_keys.length; i += 1) {
if (current_indices.has(index_keys[i])) {
current_indices.delete(index_keys[i]);
} else {
store.createIndex(index_keys[i], "doc." + index_keys[i],
{unique: false});
}
}
current_indices = Array.from(current_indices);
for (i = 0; i < current_indices.length; i += 1) {
store.deleteIndex(current_indices[i]);
}
if (current_stores.indexOf("attachment") === -1) {
store = db.createObjectStore("attachment", {
keyPath: "_key_path",
autoIncrement: false
});
store.createIndex("_id", "_id", {unique: false});
}
if (current_stores.indexOf("blob") === -1) {
store = db.createObjectStore("blob", {
keyPath: "_key_path",
autoIncrement: false
......@@ -95,8 +125,9 @@
["_id", "_attachment"], {unique: false});
store.createIndex("_id", "_id", {unique: false});
}
}
function waitForOpenIndexedDB(db_name, callback) {
function waitForOpenIndexedDB(db_name, version, index_keys, callback) {
var request;
function canceller() {
......@@ -107,7 +138,7 @@
function resolver(resolve, reject) {
// Open DB //
request = indexedDB.open(db_name);
request = indexedDB.open(db_name, version);
request.onerror = function (error) {
canceller();
if ((error !== undefined) &&
......@@ -135,7 +166,9 @@
};
// Create DB if necessary //
request.onupgradeneeded = handleUpgradeNeeded;
request.onupgradeneeded = function (evt) {
handleUpgradeNeeded(evt, index_keys);
};
request.onversionchange = function () {
canceller();
......@@ -235,11 +268,14 @@
IndexedDBStorage.prototype.buildQuery = function (options) {
var result_list = [],
context = this;
context = this,
query,
key,
value;
function pushIncludedMetadata(cursor) {
result_list.push({
"id": cursor.key,
"id": cursor.primaryKey,
"value": {},
"doc": cursor.value.doc
});
......@@ -247,24 +283,31 @@
function pushMetadata(cursor) {
result_list.push({
"id": cursor.key,
"id": cursor.primaryKey,
"value": {}
});
}
return new RSVP.Queue()
.push(function () {
return waitForOpenIndexedDB(context._database_name, function (db) {
return waitForOpenIndexedDB(context._database_name, context._version,
context._index_keys, function (db) {
return waitForTransaction(db, ["metadata"], "readonly",
function (tx) {
key = "_id";
if (options.subquery) {
query = parseStringToObject(options.subquery);
key = query.key;
value = query.value;
}
if (options.include_docs === true) {
return waitForAllSynchronousCursor(
tx.objectStore("metadata").index("_id").openCursor(),
tx.objectStore("metadata").index(key).openCursor(value),
pushIncludedMetadata
);
}
return waitForAllSynchronousCursor(
tx.objectStore("metadata").index("_id").openKeyCursor(),
tx.objectStore("metadata").index(key).openKeyCursor(value),
pushMetadata
);
});
......@@ -279,7 +322,8 @@
var context = this;
return new RSVP.Queue()
.push(function () {
return waitForOpenIndexedDB(context._database_name, function (db) {
return waitForOpenIndexedDB(context._database_name, context._version,
context._index_keys, function (db) {
return waitForTransaction(db, ["metadata"], "readonly",
function (tx) {
return waitForIDBRequest(tx.objectStore("metadata").get(id));
......@@ -307,9 +351,10 @@
return new RSVP.Queue()
.push(function () {
return waitForOpenIndexedDB(context._database_name, function (db) {
return waitForTransaction(db, ["metadata", "attachment"], "readonly",
function (tx) {
return waitForOpenIndexedDB(context._database_name, context._version,
context._index_keys, function (db) {
return waitForTransaction(db, ["metadata", "attachment"],
"readonly", function (tx) {
return RSVP.all([
waitForIDBRequest(tx.objectStore("metadata").get(id)),
waitForAllSynchronousCursor(
......@@ -336,7 +381,8 @@
};
IndexedDBStorage.prototype.put = function (id, metadata) {
return waitForOpenIndexedDB(this._database_name, function (db) {
return waitForOpenIndexedDB(this._database_name, this._version,
this._index_keys, function (db) {
return waitForTransaction(db, ["metadata"], "readwrite",
function (tx) {
return waitForIDBRequest(tx.objectStore("metadata").put({
......@@ -348,7 +394,8 @@
};
IndexedDBStorage.prototype.remove = function (id) {
return waitForOpenIndexedDB(this._database_name, function (db) {
return waitForOpenIndexedDB(this._database_name, this._version,
this._index_keys, function (db) {
return waitForTransaction(db, ["metadata", "attachment", "blob"],
"readwrite", function (tx) {
......@@ -395,7 +442,8 @@
var db_name = this._database_name,
start,
end,
array_buffer_list = [];
array_buffer_list = [],
context = this;
start = options.start || 0;
end = options.end;
......@@ -416,7 +464,8 @@
return new RSVP.Queue()
.push(function () {
return waitForOpenIndexedDB(db_name, function (db) {
return waitForOpenIndexedDB(db_name, context._version,
context._index_keys, function (db) {
return waitForTransaction(db, ["blob"], "readonly",
function (tx) {
var key_path = buildKeyPath([id, name]),
......@@ -501,7 +550,8 @@
// Request the full blob
return new RSVP.Queue()
.push(function () {
return waitForOpenIndexedDB(db_name, function (db) {
return waitForOpenIndexedDB(db_name, context._version,
context._index_keys, function (db) {
return waitForTransaction(db, ["attachment", "blob"], "readonly",
function (tx) {
var key_path = buildKeyPath([id, name]),
......@@ -573,7 +623,7 @@
};
IndexedDBStorage.prototype.putAttachment = function (id, name, blob) {
var db_name = this._database_name;
var db_name = this._database_name, context = this;
return new RSVP.Queue()
.push(function () {
// Split the blob first
......@@ -591,7 +641,8 @@
handled_size += UNITE;
}
return waitForOpenIndexedDB(db_name, function (db) {
return waitForOpenIndexedDB(db_name, context._version,
context._index_keys, function (db) {
return waitForTransaction(db, ["attachment", "blob"], "readwrite",
function (tx) {
var blob_store,
......@@ -658,7 +709,8 @@
};
IndexedDBStorage.prototype.removeAttachment = function (id, name) {
return waitForOpenIndexedDB(this._database_name, function (db) {
return waitForOpenIndexedDB(this._database_name, this._version,
this._index_keys, function (db) {
return waitForTransaction(db, ["attachment", "blob"], "readwrite",
function (tx) {
var promise_list = [],
......@@ -690,4 +742,5 @@
};
jIO.addStorage("indexeddb", IndexedDBStorage);
}(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, DOMError));
}(indexedDB, jIO, RSVP, Blob, Math, IDBKeyRange, IDBOpenDBRequest, DOMError,
parseStringToObject));
......@@ -49,6 +49,182 @@
});
}
function idCompare(value1, value2) {
if (value1.id > value2.id) {
return 1;
}
if (value1.id < value2.id) {
return -1;
}
return 0;
}
/////////////////////////////////////////////////////////////////
// indexedDBStorage.buildQuery
/////////////////////////////////////////////////////////////////
module("indexedDBStorage.buildQuery", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("Simple query matching single object", function () {
var context = this;
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
index_keys: ["a", "b"],
});
stop();
expect(2);
context.jio.put("32", {"a": "3", "b": "2"})
.then(function () {
return context.jio.allDocs({subquery: 'a: "3"'});
})
.then(function (result) {
equal(result.data.total_rows, 1);
deepEqual(result.data.rows, [{"id": "32", "value": {}}]);
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Simple query matching multiple objects", function () {
var context = this;
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
index_keys: ["a", "b"],
});
stop();
expect(2);
RSVP.all([
context.jio.put("32", {a: "3", b: "1"}),
context.jio.put("21", {a: "8", b: "1"}),
context.jio.put("3", {a: "5", b: "1"})
])
.then(function () {
return context.jio.allDocs({subquery: 'b: "1"'});
})
.then(function (result) {
equal(result.data.total_rows, 3);
deepEqual(result.data.rows.sort(idCompare),
[
{"id": "32", "value": {}},
{"id": "21", "value": {}},
{"id": "3", "value": {}}
].sort(idCompare));
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Index keys are modified", function () {
var context = this;
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
version: 1,
index_keys: ["a"]
});
stop();
expect(7);
RSVP.all([
context.jio.put("32", {"a": "3", "b": "2", "c": "inverse"}),
context.jio.put("5", {"a": "6", "b": "2", "c": "strong"}),
context.jio.put("14", {"a": "67", "b": "3", "c": "disolve"})
])
.then(function () {
return context.jio.allDocs({subquery: 'a: "67"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "14", "value": {}}]);
})
.then(function () {
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
version: 2,
index_keys: ["a", "b", "c"],
});
})
.then(function () {
return RSVP.all([
context.jio.put("18", {"a": "2", "b": "3", "c": "align"}),
context.jio.put("62", {"a": "3", "b": "2", "c": "disolve"})
]);
})
.then(function () {
return context.jio.allDocs({subquery: 'b: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "14", "value": {}},
{"id": "18", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({subquery: 'c: "disolve"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "14", "value": {}},
{"id": "62", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({subquery: 'a: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "32", "value": {}},
{"id": "62", "value": {}}]);
})
.then(function () {
context.jio = jIO.createJIO({
type: "indexeddb",
database: "index2_test",
index_keys: ["a", "c"],
version: 3
});
})
.then(function () {
return context.jio.put("192", {"a": "3", "b": "3", "c": "disolve"});
})
.then(function () {
return context.jio.allDocs({subquery: 'a: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows.sort(idCompare), [{"id": "192", "value": {}},
{"id": "32", "value": {}}, {"id": "62", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({query: 'c: "disolve"'});
})
.then(function (result) {
deepEqual(result.data.rows.sort(idCompare), [{"id": "14", "value": {}},
{"id": "192", "value": {}}, {"id": "62", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({subquery: 'b: "3"'});
})
.fail(function (error) {
equal(error.status_code, 501);
equal(error.message,
"Capacity 'query' is not implemented on 'indexeddb'");
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// indexeddbStorage.constructor
/////////////////////////////////////////////////////////////////
......
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