Commit c39f9581 authored by preetwinder's avatar preetwinder

Migration handling and improved querying support

parent 3e953134
...@@ -29,34 +29,98 @@ ...@@ -29,34 +29,98 @@
if (typeof description.database !== "string" || if (typeof description.database !== "string" ||
description.database === "") { description.database === "") {
throw new TypeError("IndexStorage2 'database' description property " + throw new TypeError("IndexStorage2 'database' description property " +
"must be a non-empty string"); "must be a non-empty string");
}
if (description.index_keys && !(description.index_keys instanceof Array)) {
throw new TypeError("IndexStorage2 'index_keys' description property " +
"must be an Array");
} }
this._sub_storage = jIO.createJIO(description.sub_storage); this._sub_storage = jIO.createJIO(description.sub_storage);
this._database_name = "jio:" + description.database; this._database_name = "jio:" + description.database;
this._index_keys = description.index_keys || []; this._index_keys = description.index_keys || [];
this._use_sub_storage_query = description.use_sub_storage_query || false;
this._use_sub_storage_query_partial =
description.use_sub_storage_query_partial || true;
} }
IndexStorage2.prototype.hasCapacity = function (name) { IndexStorage2.prototype.hasCapacity = function (name) {
return ((name === "list") || (name === "query")) || (name === "limit"); var this_storage_capacity_list = ["limit",
"select",
"list",
"query",
"sort",
"include"];
if (this_storage_capacity_list.indexOf(name) !== -1) {
return true;
}
}; };
function checkArrayEquality(array1, array2) {
if (array1.length !== array2.length) {
return false;
}
var i;
array1 = array1.sort();
array2 = array2.sort();
for (i = 0; i < array1.length; i += 1) {
if (array1[i] !== array2[i]) {
return false;
}
}
return true;
}
function compareSortOn(value1, value2, sort_on) {
var current_compare = sort_on.slice(0, 1)[0],
remaining_compare = sort_on.slice(1);
if (value1[current_compare[0]] === value2[current_compare[0]]) {
if (remaining_compare.length === 0) {
return 0;
}
return compareSortOn(value1, value2, remaining_compare);
}
if (value1[current_compare[0]] > value2[current_compare[0]]) {
if (current_compare[1] === 'ascending') {
return 1;
}
return -1;
}
if (current_compare[1] === 'descending') {
return 1;
}
return -1;
}
function handleUpgradeNeeded(evt, index_keys) { function handleUpgradeNeeded(evt, index_keys) {
var db = evt.target.result, store, i; var db = evt.target.result, store, i, current_indices;
store = db.createObjectStore("index-store", { if (!(db.objectStoreNames[0])) {
keyPath: "id", store = db.createObjectStore("index-store", {
autoIncrement: false keyPath: "id",
}); autoIncrement: false
});
} else {
store = evt.target.transaction.objectStore('index-store');
}
current_indices = new Set(store.indexNames);
for (i = 0; i < index_keys.length; i += 1) { for (i = 0; i < index_keys.length; i += 1) {
store.createIndex("Index-" + index_keys[i], "doc." + index_keys[i], if (!(current_indices.has('Index-' + index_keys[i]))) {
{unique: false}); store.createIndex('Index-' + index_keys[i],
'doc.' + index_keys[i], { unique: false });
}
current_indices.delete('Index-' + index_keys[i]);
}
current_indices = Array.from(current_indices);
for (i = 0; i < current_indices.length; i += 1) {
store.deleteIndex(current_indices[i]);
} }
} }
function waitForOpenIndexedDB(db_name, index_keys, callback) { function waitForOpenIndexedDB(db_name, version, index_keys, callback) {
function resolver(resolve, reject) { function resolver(resolve, reject) {
// Open DB // // Open DB //
var request = indexedDB.open(db_name); var request = indexedDB.open(db_name, version);
request.onerror = function (error) { request.onerror = function (error) {
if (request.result) { if (request.result) {
request.result.close(); request.result.close();
...@@ -114,6 +178,35 @@ ...@@ -114,6 +178,35 @@
return new RSVP.Promise(resolver); return new RSVP.Promise(resolver);
} }
function waitForUpdatedOpenIndexedDB(db_name, index_keys, callback) {
function resolver(resolve, reject) {
var db_version, store, current_indices, required_indices;
required_indices = index_keys.map(
function (value) {return 'Index-' + value; }
);
waitForOpenIndexedDB(db_name, undefined, index_keys, function (db) {
db_version = db.version;
store = db.transaction('index-store').objectStore('index-store');
current_indices = store.indexNames;
if (checkArrayEquality(required_indices,
Array.from(current_indices))) {
resolve(callback(db));
} else {
store.transaction.oncomplete = function () {
waitForOpenIndexedDB(db_name, db_version + 1, index_keys,
function (db) {
resolve(callback(db));
});
};
}
})
.fail(function (error) {
reject(error);
});
}
return new RSVP.Promise(resolver);
}
function waitForTransaction(db, stores, flag, callback) { function waitForTransaction(db, stores, flag, callback) {
var tx = db.transaction(stores, flag); var tx = db.transaction(stores, flag);
function canceller() { function canceller() {
...@@ -175,29 +268,41 @@ ...@@ -175,29 +268,41 @@
return filtered_doc; return filtered_doc;
} }
IndexStorage2.prototype._runQuery = function (index, value, limit) { IndexStorage2.prototype._runQuery = function (index, value, limit, sort_on) {
var context = this; var context = this;
return new RSVP.Queue() return new RSVP.Queue()
.push(function () { .push(function () {
if ((context._index_keys.indexOf(index) === -1)) { if ((context._index_keys.indexOf(index) === -1)) {
try { if (context._use_sub_storage_query_partial) {
context._sub_storage.hasCapacity("query"); try {
} catch (error) { context._sub_storage.hasCapacity("query");
throw new jIO.util.jIOError( } catch (error) {
"No index for this key and substorage doesn't support queries" if (error instanceof jIO.util.jIOError &&
error.status_code === 501) {
throw new jIO.util.jIOError("No index for '" + index +
"' key and substorage doesn't support queries", 404);
}
throw error;
}
return context._sub_storage.buildQuery(
{ "query": index + ":" + value }
); );
} }
return context._sub_storage.buildQuery( throw new jIO.util.jIOError("No index for '" + index +
{"query": index + ":" + value} "' key and checking the substorage for partial queries is not set");
);
} }
return waitForOpenIndexedDB(context._database_name, context._index_keys, return waitForUpdatedOpenIndexedDB(context._database_name,
function (db) { context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readonly", return waitForTransaction(db, ["index-store"], "readonly",
function (tx) { function (tx) {
return waitForIDBRequest(tx.objectStore("index-store") return waitForIDBRequest(tx.objectStore("index-store")
.index("Index-" + index).getAll(value, limit)) .index("Index-" + index).getAll(value, limit))
.then(function (evt) { .then(function (evt) {
if (sort_on) {
evt.target.result.sort(function (v1, v2) {
return compareSortOn(v1.doc, v2.doc, sort_on);
});
}
return evt.target.result; return evt.target.result;
}); });
}); });
...@@ -205,12 +310,13 @@ ...@@ -205,12 +310,13 @@
}); });
}; };
IndexStorage2.prototype._processQueryObject = function (object, limit) { IndexStorage2.prototype._processQueryObject = function (object, limit,
sort_on) {
var promise_list = [], context = this, i, j, query_result = new Set(); var promise_list = [], context = this, i, j, query_result = new Set();
return RSVP.Queue() return RSVP.Queue()
.push(function () { .push(function () {
if (object.type === "simple") { if (object.type === "simple") {
return context._runQuery(object.key, object.value, limit); return context._runQuery(object.key, object.value, limit, sort_on);
} }
if (object.type === "complex") { if (object.type === "complex") {
for (i = 0; i < object.query_list.length; i += 1) { for (i = 0; i < object.query_list.length; i += 1) {
...@@ -249,25 +355,65 @@ ...@@ -249,25 +355,65 @@
}; };
IndexStorage2.prototype.buildQuery = function (options) { IndexStorage2.prototype.buildQuery = function (options) {
var context = this; var context = this, substorage = this._sub_storage;
try {
if (substorage.hasCapacity("list") &&
((options.query === undefined) ||
(context._use_sub_storage_query &&
substorage.hasCapacity("query"))) &&
((options.sort_on === undefined) ||
(substorage.hasCapacity("sort"))) &&
((options.select_list === undefined) ||
(substorage.hasCapacity("select"))) &&
((options.limit === undefined) ||
(substorage.hasCapacity("limit"))) &&
((options.include_docs === undefined ||
(substorage.hasCapacity("include"))))) {
return substorage.buildQuery(options);
}
} catch (error) {
if (!((error instanceof jIO.util.jIOError) &&
(error.status_code === 501))) {
throw error;
}
}
if (options.query) { if (options.query) {
return this._processQueryObject(parseStringToObject(options.query), return this._processQueryObject(parseStringToObject(options.query),
options.limit) options.limit, options.sort_on)
.push(function (result) { .push(function (result) {
if (options.include_docs) {
options.select_list = context._index_keys;
}
return result.map(function (value) { return result.map(function (value) {
return {"id": value.id, "value": {} }; return {
"id": value.id,
"value": filterDocValues(value.doc,
options.select_list || [])
};
}); });
}); });
} }
return waitForOpenIndexedDB(context._database_name, context._index_keys, return waitForUpdatedOpenIndexedDB(context._database_name,
function (db) { context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readonly", return waitForTransaction(db, ["index-store"], "readonly",
function (tx) { function (tx) {
return waitForIDBRequest(tx.objectStore("index-store") return waitForIDBRequest(tx.objectStore("index-store")
.getAll(undefined, options.limit)) .getAll(undefined, options.limit))
.then(function (evt) { .then(function (evt) {
if (options.sort_on) {
evt.target.result.sort(function (v1, v2) {
return compareSortOn(v1.doc, v2.doc, options.sort_on);
});
}
if (options.include_docs) {
options.select_list = context._index_keys;
}
return evt.target.result.map(function (value) { return evt.target.result.map(function (value) {
return {"id": value.id, "value": {} }; return {
"id": value.id,
"value": filterDocValues(value.doc,
options.select_list || [])
};
}); });
}); });
}); });
...@@ -282,10 +428,10 @@ ...@@ -282,10 +428,10 @@
var context = this; var context = this;
return context._sub_storage.put(id, value) return context._sub_storage.put(id, value)
.push(function () { .push(function () {
return waitForOpenIndexedDB(context._database_name, context._index_keys, return waitForUpdatedOpenIndexedDB(context._database_name,
function (db) { context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readwrite", return waitForTransaction(db, ["index-store"], "readwrite",
function (tx) { function (tx) {
return waitForIDBRequest(tx.objectStore("index-store").put({ return waitForIDBRequest(tx.objectStore("index-store").put({
"id": id, "id": id,
"doc": filterDocValues(value, context._index_keys) "doc": filterDocValues(value, context._index_keys)
...@@ -299,10 +445,10 @@ ...@@ -299,10 +445,10 @@
var context = this; var context = this;
return context._sub_storage.post(value) return context._sub_storage.post(value)
.push(function (id) { .push(function (id) {
return waitForOpenIndexedDB(context._database_name, context._index_keys, return waitForUpdatedOpenIndexedDB(context._database_name,
function (db) { context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readwrite", return waitForTransaction(db, ["index-store"], "readwrite",
function (tx) { function (tx) {
return waitForIDBRequest(tx.objectStore("index-store").put({ return waitForIDBRequest(tx.objectStore("index-store").put({
"id": id, "id": id,
"doc": filterDocValues(value, context._index_keys) "doc": filterDocValues(value, context._index_keys)
...@@ -318,16 +464,13 @@ ...@@ -318,16 +464,13 @@
IndexStorage2.prototype.remove = function (id) { IndexStorage2.prototype.remove = function (id) {
var context = this; var context = this;
return context._sub_storage.remove(id) return context._sub_storage.remove(id)
.push(function (result) { .push(function () {
return waitForOpenIndexedDB(context._database_name, context._index_keys, return waitForUpdatedOpenIndexedDB(context._database_name,
function (db) { context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readwrite", return waitForTransaction(db, ["index-store"], "readwrite",
function (tx) { function (tx) {
return waitForIDBRequest(tx.objectStore("index-store") return waitForIDBRequest(tx.objectStore("index-store")
.delete(id)) .delete(id));
.then(function () {
return result;
});
}); });
}); });
}); });
......
...@@ -37,7 +37,7 @@ ...@@ -37,7 +37,7 @@
var gadget = this; var gadget = this;
return gadget._sub_storage.post(value) return gadget._sub_storage.post(value)
.push(function (id) { .push(function (id) {
return gadget._signature_storage.put(id, {"id": id}) return gadget._signature_storage.put(id, {})
.push(function () { .push(function () {
return id; return id;
}); });
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
var gadget = this; var gadget = this;
return gadget._sub_storage.put(id, value) return gadget._sub_storage.put(id, value)
.push(function (result) { .push(function (result) {
return gadget._signature_storage.put(id, {"id": id}) return gadget._signature_storage.put(id, {})
.push(function () { .push(function () {
return result; return result;
}); });
...@@ -78,11 +78,12 @@ ...@@ -78,11 +78,12 @@
}; };
ListStorage.prototype.hasCapacity = function (name) { ListStorage.prototype.hasCapacity = function (name) {
if (name === "list") { if (name === "list") {
return true; return this._signature_storage.hasCapacity('list');
} }
return false;
}; };
ListStorage.prototype.buildQuery = function () { ListStorage.prototype.buildQuery = function () {
return this._signature_storage.buildQuery({}); return this._signature_storage.buildQuery();
}; };
jIO.addStorage('list', ListStorage); jIO.addStorage('list', ListStorage);
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
}); });
} }
function id_compare(value1, value2) { function idCompare(value1, value2) {
if (value1.id > value2.id) { if (value1.id > value2.id) {
return 1; return 1;
} }
...@@ -83,6 +83,66 @@ ...@@ -83,6 +83,66 @@
deepEqual(this.jio.__storage._index_keys, []); deepEqual(this.jio.__storage._index_keys, []);
}); });
test("Constructor incorrect description values", function () {
throws(
function () {
this.jio = jIO.createJIO({
type: "index2",
database: 44,
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
},
function (error) {
ok(error instanceof TypeError);
equal(error.message, "IndexStorage2 'database' description property " +
"must be a non-empty string");
return true;
}
);
throws(
function () {
this.jio = jIO.createJIO({
type: "index2",
database: "",
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
},
function (error) {
ok(error instanceof TypeError);
equal(error.message, "IndexStorage2 'database' description property " +
"must be a non-empty string");
return true;
}
);
throws(
function () {
this.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: "index_key",
sub_storage: {
type: "dummystorage3"
}
});
},
function (error) {
ok(error instanceof TypeError);
equal(error.message, "IndexStorage2 'index_keys' description property" +
" must be an Array");
return true;
}
);
});
test("Constructor with index_keys", function () { test("Constructor with index_keys", function () {
this.jio = jIO.createJIO({ this.jio = jIO.createJIO({
type: "index2", type: "index2",
...@@ -132,6 +192,9 @@ ...@@ -132,6 +192,9 @@
ok(this.jio.hasCapacity("list")); ok(this.jio.hasCapacity("list"));
ok(this.jio.hasCapacity("query")); ok(this.jio.hasCapacity("query"));
ok(this.jio.hasCapacity("limit")); ok(this.jio.hasCapacity("limit"));
ok(this.jio.hasCapacity("select"));
ok(this.jio.hasCapacity("include"));
ok(this.jio.hasCapacity("sort"));
}); });
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
...@@ -180,10 +243,13 @@ ...@@ -180,10 +243,13 @@
module("indexStorage2.buildQuery", { module("indexStorage2.buildQuery", {
teardown: function () { teardown: function () {
deleteIndexedDB(this.jio); deleteIndexedDB(this.jio);
DummyStorage3.prototype.hasCapacity = undefined;
DummyStorage3.prototype.put = undefined;
DummyStorage3.prototype.buildQuery = undefined;
} }
}); });
test("Simple query matching single object", function () { test("Sub-storage handles empty options", function () {
var context = this; var context = this;
context.jio = jIO.createJIO({ context.jio = jIO.createJIO({
type: "index2", type: "index2",
...@@ -196,6 +262,267 @@ ...@@ -196,6 +262,267 @@
stop(); stop();
expect(3); expect(3);
DummyStorage3.prototype.put = function (id) {
return id;
};
DummyStorage3.prototype.hasCapacity = function (name) {
return name === "list";
};
DummyStorage3.prototype.buildQuery = function (options) {
deepEqual(options, {});
return [
{id: "2", value: {}},
{id: "32", value: {}},
{id: "16", value: {}},
{id: "21", value: {}}
];
};
RSVP.all([
context.jio.put("2", {"a": "close", "b": 45}),
context.jio.put("16", {"a": "value", "b": 5}),
context.jio.put("21", {"a": "advice", "b": 12}),
context.jio.put("32", {"a": "recieve", "b": 76})
])
.then(function () {
return context.jio.allDocs();
})
.then(function (result) {
equal(result.data.total_rows, 4);
deepEqual(result.data.rows.sort(idCompare), [{id: "16", value: {}},
{id: "2", value: {}}, {id: "21", value: {}}, {id: "32", value: {}}]);
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("use_sub_storage_query is true", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
use_sub_storage_query: true,
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(3);
DummyStorage3.prototype.put = function (id) {
return id;
};
DummyStorage3.prototype.hasCapacity = function (name) {
return (name === "list") || (name === "query");
};
DummyStorage3.prototype.buildQuery = function (options) {
deepEqual(options, {query: 'a:"advice"'});
return [
{id: "21", value: {}}
];
};
RSVP.all([
context.jio.put("2", {"a": "close", "b": 45}),
context.jio.put("16", {"a": "value", "b": 5}),
context.jio.put("21", {"a": "advice", "b": 12})
])
.then(function () {
return context.jio.allDocs({query: 'a:"advice"'});
})
.then(function (result) {
equal(result.data.total_rows, 1);
deepEqual(result.data.rows, [{id: "21", value: {}}]);
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Sub storage capacities are used by default", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b", "c"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(3);
DummyStorage3.prototype.put = function (id) {
return id;
};
DummyStorage3.prototype.hasCapacity = function (name) {
return (name === "list") || (name === "include") || (name === "select")
|| (name === "sort") || (name === "limit");
};
DummyStorage3.prototype.buildQuery = function (options) {
deepEqual(options, {include_docs: false, select_list: ["a", "c"],
limit: 3, sort_on: [["a", "descending"], ["b", "ascending"]]});
return [
{"id": "21", "value": {"a": "value", "c": "4"}},
{"id": "16", "value": {"a": "value", "c": "54"}},
{"id": "1", "value": {"a": "exhalt", "c": "28"}}
];
};
RSVP.all([
context.jio.put("2", {"a": "close", "b": 45, "c": "78"}),
context.jio.put("16", {"a": "value", "b": 5, "c": "54"}),
context.jio.put("21", {"a": "value", "b": 12, "c": "4"}),
context.jio.put("7", {"a": "device", "b": 83, "c": "26"}),
context.jio.put("1", {"a": "exhalt", "b": 68, "c": "28"})
])
.then(function () {
return context.jio.allDocs({select_list: ["a", "c"], limit: 3,
sort_on: [["a", "descending"], ["b", "ascending"]],
include_docs: false, });
})
.then(function (result) {
equal(result.data.total_rows, 3);
deepEqual(result.data.rows, [
{"id": "21", "value": {"a": "value", "c": "4"}},
{"id": "16", "value": {"a": "value", "c": "54"}},
{"id": "1", "value": {"a": "exhalt", "c": "28"}}
]);
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Sort_on option is given", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b", "c", "d"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(2);
DummyStorage3.prototype.put = function (id) {
return id;
};
DummyStorage3.prototype.hasCapacity = function (name) {
return (name === "list");
};
RSVP.all([
context.jio.put("2", {"a": "close", "b": 45, "c": "78", "d": "gc4"}),
context.jio.put("16", {"a": "value", "b": 5, "c": "54", "d": "xf7"}),
context.jio.put("21", {"a": "value", "b": 83, "c": "4", "d": "gc1"}),
context.jio.put("7", {"a": "value", "b": 12, "c": "26", "d": "x54"}),
context.jio.put("1", {"a": "exhalt", "b": 68, "c": "28", "d": "o32"})
])
.then(function () {
return context.jio.allDocs({sort_on:
[["a", "ascending"], ["b", "descending"]]});
})
.then(function (result) {
equal(result.data.total_rows, 5);
deepEqual(result.data.rows, [
{"id": "2", "value": {}},
{"id": "1", "value": {}},
{"id": "21", "value": {}},
{"id": "7", "value": {}},
{"id": "16", "value": {}}
]);
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Include docs", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b", "c"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(2);
DummyStorage3.prototype.put = function (id) {
return id;
};
DummyStorage3.prototype.hasCapacity = function (name) {
return (name === "list");
};
RSVP.all([
context.jio.put("2", {"a": "close", "b": 45, "c": "78", "d": "gc4"}),
context.jio.put("16", {"a": "value", "b": 5, "c": "54", "d": "xf7"}),
context.jio.put("21", {"a": "value", "b": 83, "c": "4", "d": "gc1"}),
context.jio.put("7", {"a": "value", "b": 12, "c": "26", "d": "x54"}),
context.jio.put("1", {"a": "exhalt", "b": 68, "c": "28", "d": "o32"})
])
.then(function () {
return context.jio.allDocs({include_docs: true});
})
.then(function (result) {
equal(result.data.total_rows, 5);
deepEqual(result.data.rows.sort(idCompare), [
{"id": "2", "value": {"a": "close", "b": 45, "c": "78"}},
{"id": "16", "value": {"a": "value", "b": 5, "c": "54"}},
{"id": "21", "value": {"a": "value", "b": 83, "c": "4"}},
{"id": "7", "value": {"a": "value", "b": 12, "c": "26"}},
{"id": "1", "value": {"a": "exhalt", "b": 68, "c": "28"}}
].sort(idCompare));
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Simple query matching single object", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(4);
DummyStorage3.prototype.put = function (id, value) { DummyStorage3.prototype.put = function (id, value) {
equal(id, "32"); equal(id, "32");
deepEqual(value, {a: "3", b: "2"}); deepEqual(value, {a: "3", b: "2"});
...@@ -207,7 +534,8 @@ ...@@ -207,7 +534,8 @@
return context.jio.allDocs({query: 'a: "3"'}); return context.jio.allDocs({query: 'a: "3"'});
}) })
.then(function (result) { .then(function (result) {
deepEqual(result.data.rows[0], {"id": "32", "value": {}}); equal(result.data.total_rows, 1);
deepEqual(result.data.rows, [{"id": "32", "value": {}}]);
}) })
.fail(function (error) { .fail(function (error) {
console.log(error); console.log(error);
...@@ -253,12 +581,12 @@ ...@@ -253,12 +581,12 @@
}) })
.then(function (result) { .then(function (result) {
equal(result.data.total_rows, 3); equal(result.data.total_rows, 3);
deepEqual(result.data.rows.sort(id_compare), deepEqual(result.data.rows.sort(idCompare),
[ [
{"id": "32", "value": {}}, {"id": "32", "value": {}},
{"id": "21", "value": {}}, {"id": "21", "value": {}},
{"id": "3", "value": {}} {"id": "3", "value": {}}
].sort(id_compare)); ].sort(idCompare));
}) })
.fail(function (error) { .fail(function (error) {
console.log(error); console.log(error);
...@@ -287,20 +615,17 @@ ...@@ -287,20 +615,17 @@
return id; return id;
}; };
DummyStorage3.prototype.hasCapacity = function (capacity) { DummyStorage3.prototype.hasCapacity = function (capacity) {
equal(capacity, "query"); return capacity === 'list';
return false;
}; };
context.jio.put("32", {"a": "3", "b": "2"}) context.jio.put("32", {"a": "3", "b": "2"})
.then(function () { .then(function () {
return context.jio.allDocs({query: 'b:"2"'}); return context.jio.allDocs({query: 'b:"2"'});
}) })
.then(function () {
return deleteIndexedDB(context.jio);
})
.fail(function (error) { .fail(function (error) {
equal(error.status_code, 404);
equal(error.message, equal(error.message,
"No index for this key and substorage doesn't support queries"); "No index for 'b' key and substorage doesn't support queries");
}) })
.always(function () { .always(function () {
start(); start();
...@@ -318,14 +643,13 @@ ...@@ -318,14 +643,13 @@
} }
}); });
stop(); stop();
expect(4); expect(3);
DummyStorage3.prototype.put = function (id) { DummyStorage3.prototype.put = function (id) {
return id; return id;
}; };
DummyStorage3.prototype.hasCapacity = function (capacity) { DummyStorage3.prototype.hasCapacity = function (capacity) {
equal(capacity, "query"); return capacity === 'query';
return (capacity === 'query');
}; };
DummyStorage3.prototype.buildQuery = function (options) { DummyStorage3.prototype.buildQuery = function (options) {
equal(options.query, "a:5"); equal(options.query, "a:5");
...@@ -363,14 +687,13 @@ ...@@ -363,14 +687,13 @@
} }
}); });
stop(); stop();
expect(4); expect(3);
DummyStorage3.prototype.put = function (id) { DummyStorage3.prototype.put = function (id) {
return id; return id;
}; };
DummyStorage3.prototype.hasCapacity = function (capacity) { DummyStorage3.prototype.hasCapacity = function (capacity) {
equal(capacity, "query"); return capacity === 'query';
if (capacity === "query") { return true; }
}; };
DummyStorage3.prototype.buildQuery = function (options) { DummyStorage3.prototype.buildQuery = function (options) {
equal(options.query, 'c:linear'); equal(options.query, 'c:linear');
...@@ -387,9 +710,9 @@ ...@@ -387,9 +710,9 @@
}) })
.then(function (result) { .then(function (result) {
equal(result.data.total_rows, 2); equal(result.data.total_rows, 2);
deepEqual(result.data.rows.sort(id_compare), deepEqual(result.data.rows.sort(idCompare),
[{"id": "32", "value": {}}, {"id": "3", "value": {}}] [{"id": "32", "value": {}}, {"id": "3", "value": {}}]
.sort(id_compare)); .sort(idCompare));
}) })
.fail(function (error) { .fail(function (error) {
console.log(error); console.log(error);
...@@ -513,6 +836,132 @@ ...@@ -513,6 +836,132 @@
}); });
}); });
test("Select without query", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b", "c"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(2);
DummyStorage3.prototype.put = function (id) {
return id;
};
RSVP.all([
context.jio.put("1", {"a": "55", "b": "2", "c": "try"}),
context.jio.put("2", {"a": "98", "b": "2", "c": "adverse"}),
context.jio.put("3", {"a": "75", "b": "2", "c": "invite"}),
context.jio.put("8", {"a": "43", "b": "2", "c": "absolve"}),
context.jio.put("6", {"a": "21", "b": "2", "c": "defy"})
])
.then(function () {
return context.jio.allDocs({select_list: ["a", "c"]});
})
.then(function (result) {
equal(result.data.total_rows, 5);
deepEqual(result.data.rows.sort(idCompare), [
{id: "1", value: {"a": "55", "c": "try"}},
{id: "2", value: {"a": "98", "c": "adverse"}},
{id: "3", value: {"a": "75", "c": "invite"}},
{id: "8", value: {"a": "43", "c": "absolve"}},
{id: "6", value: {"a": "21", "c": "defy"}}
].sort(idCompare));
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Select with query and limit", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b", "c"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(2);
DummyStorage3.prototype.put = function (id) {
return id;
};
RSVP.all([
context.jio.put("1", {"a": "55", "b": "2", "c": "try"}),
context.jio.put("2", {"a": "98", "b": "2", "c": "adverse"}),
context.jio.put("3", {"a": "75", "b": "2", "c": "invite"}),
context.jio.put("8", {"a": "43", "b": "3", "c": "absolve"}),
context.jio.put("6", {"a": "21", "b": "2", "c": "defy"}),
context.jio.put("4", {"a": "65", "b": "3", "c": "odd"})
])
.then(function () {
return context.jio.allDocs({select_list: ["a", "c"],
query: "b:2"});
})
.then(function (result) {
equal(result.data.total_rows, 4);
deepEqual(result.data.rows.sort(idCompare), [
{id: "1", value: {"a": "55", "c": "try"}},
{id: "2", value: {"a": "98", "c": "adverse"}},
{id: "3", value: {"a": "75", "c": "invite"}},
{id: "6", value: {"a": "21", "c": "defy"}}
].sort(idCompare));
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
test("Select_list key is not present in the index", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "c"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(2);
DummyStorage3.prototype.put = function (id) {
return id;
};
RSVP.all([
context.jio.put("1", {"a": "55", "b": "2", "c": "try"}),
context.jio.put("2", {"a": "55", "b": "5", "c": "adverse"}),
context.jio.put("3", {"a": "55", "b": "1", "c": "invite"})
])
.then(function () {
return context.jio.allDocs({select_list: ["a", "b"],
query: "a:55"});
})
.fail(function (error) {
equal(error.status_code, 404);
equal(error.message, "Index key 'b' not found in document");
})
.always(function () {
start();
});
});
test("Complex queries", function () { test("Complex queries", function () {
var context = this; var context = this;
context.jio = jIO.createJIO({ context.jio = jIO.createJIO({
...@@ -571,7 +1020,7 @@ ...@@ -571,7 +1020,7 @@
.then(function (result) { .then(function (result) {
equal(result.data.total_rows, 2); equal(result.data.total_rows, 2);
deepEqual(result.data.rows.sort(), [{"id": "23", "value": {}}, deepEqual(result.data.rows.sort(), [{"id": "23", "value": {}},
{"id": "14", "value": {}}].sort(id_compare)); {"id": "14", "value": {}}].sort(idCompare));
}) })
.then(function () { .then(function () {
return context.jio.allDocs({"query": "name:envision AND user:Mann"}); return context.jio.allDocs({"query": "name:envision AND user:Mann"});
...@@ -585,9 +1034,9 @@ ...@@ -585,9 +1034,9 @@
}) })
.then(function (result) { .then(function (result) {
equal(result.data.total_rows, 3); equal(result.data.total_rows, 3);
deepEqual(result.data.rows.sort(id_compare), deepEqual(result.data.rows.sort(idCompare),
[{"id": "23", "value": {}}, {"id": "38", "value": {}}, [{"id": "23", "value": {}}, {"id": "38", "value": {}},
{"id": "45", "value": {}}].sort(id_compare)); {"id": "45", "value": {}}].sort(idCompare));
}) })
.then(function () { .then(function () {
return context.jio.allDocs( return context.jio.allDocs(
...@@ -607,7 +1056,7 @@ ...@@ -607,7 +1056,7 @@
}); });
}); });
/* test("Index keys modified", function () { test("Index keys are modified", function () {
var context = this; var context = this;
context.jio = jIO.createJIO({ context.jio = jIO.createJIO({
type: "index2", type: "index2",
...@@ -618,53 +1067,110 @@ ...@@ -618,53 +1067,110 @@
} }
}); });
stop(); stop();
expect(5); expect(8);
DummyStorage3.prototype.put = function (id, value) { DummyStorage3.prototype.put = function (id) {
equal(id, "32");
deepEqual(value, {a: "3", b: "2"});
return id; return id;
}; };
context.jio.put("32", {"a": "3", "b": "2"}) DummyStorage3.prototype.hasCapacity = function () {
return false;
};
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({query: 'a: "67"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "14", "value": {}}]);
})
.then(function () {
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b", "c"],
sub_storage: {
type: "dummystorage3"
}
});
})
.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({query: 'b: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "18", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({query: 'c: "disolve"'});
})
.then(function (result) {
deepEqual(result.data.rows, [{"id": "62", "value": {}}]);
})
.then(function () { .then(function () {
return context.jio.allDocs({query: 'a: "3"'}); return context.jio.allDocs({query: 'a: "3"'});
}) })
.then(function (result) { .then(function (result) {
deepEqual(result.data.rows[0], {"id": "32", "value": {}}); deepEqual(result.data.rows, [{"id": "32", "value": {}},
{"id": "62", "value": {}}]);
}) })
.then(function () { .then(function () {
context.jio = jIO.createJIO({ context.jio = jIO.createJIO({
type: "index2", type: "index2",
database: "index2_test", database: "index2_test",
index_keys: ["b"], index_keys: ["a", "c"],
sub_storage: { sub_storage: {
type: "dummystorage3" type: "dummystorage3"
} }
}); });
}) })
.then(function () { .then(function () {
console.log(context.jio.__storage._index_keys); return context.jio.put("192", {"a": "3", "b": "3", "c": "disolve"});
return context.jio.put("32", {"a": "3", "b": "2"}); })
.then(function () {
return context.jio.allDocs({query: 'a: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows.sort(idCompare), [{"id": "192", "value": {}},
{"id": "32", "value": {}}, {"id": "62", "value": {}}]);
}) })
.then(function () { .then(function () {
return context.jio.allDocs({query: 'b: "2"'}); return context.jio.allDocs({query: 'c: "disolve"'});
}) })
.then(function (result) { .then(function (result) {
deepEqual(result.data.rows[0], {"id": "32", "value": {}}); deepEqual(result.data.rows.sort(idCompare), [{"id": "192", "value": {}},
{"id": "62", "value": {}}]);
})
.then(function () {
return context.jio.allDocs({query: 'b: "3"'});
}) })
.fail(function (error) { .fail(function (error) {
console.log(error); equal(error.status_code, 404);
equal(error.message,
"No index for 'b' key and substorage doesn't support queries");
}) })
.always(function () { .always(function () {
start(); start();
}); });
});*/ });
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// IndexStorage2.getAttachment // IndexStorage2.getAttachment
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("IndexStorage2.getAttachment"); module("IndexStorage2.getAttachment", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("getAttachment called substorage getAttachment", function () { test("getAttachment called substorage getAttachment", function () {
stop(); stop();
expect(3); expect(3);
...@@ -700,7 +1206,11 @@ ...@@ -700,7 +1206,11 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// IndexStorage2.putAttachment // IndexStorage2.putAttachment
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("IndexStorage2.putAttachment"); module("IndexStorage2.putAttachment", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("putAttachment called substorage putAttachment", function () { test("putAttachment called substorage putAttachment", function () {
stop(); stop();
expect(4); expect(4);
...@@ -735,9 +1245,13 @@ ...@@ -735,9 +1245,13 @@
}); });
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
// IndexStorage3.removeAttachment // IndexStorage2.removeAttachment
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("IndexStorage3.removeAttachment"); module("IndexStorage2.removeAttachment", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("removeAttachment called substorage removeAttachment", function () { test("removeAttachment called substorage removeAttachment", function () {
stop(); stop();
expect(3); expect(3);
...@@ -769,4 +1283,252 @@ ...@@ -769,4 +1283,252 @@
}); });
}); });
/////////////////////////////////////////////////////////////////
// indexStorage2.put
/////////////////////////////////////////////////////////////////
module("indexStorage2.put", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("Put creates index", function () {
var context = this, request, store, records;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(12);
DummyStorage3.prototype.put = function (id) {
return id;
};
RSVP.all([
context.jio.put("32", {"a": "894", "b": "inversion", "c": 2}),
context.jio.put("33", {"a": "65", "b": "division", "c": 4}),
context.jio.put("34", {"a": "65", "b": "prolong", "c": 8})
])
.then(function () {
return new RSVP.Promise(function (resolve) {
request = indexedDB.open('jio:index2_test');
request.onsuccess = function () {
resolve(request.result);
};
});
})
.then(function (result) {
equal(result.version, 1);
equal(result.name, 'jio:index2_test');
equal(result.objectStoreNames.length, 1);
equal(result.objectStoreNames[0], 'index-store');
store = result.transaction('index-store').objectStore('index-store');
equal(store.indexNames.length, 2);
equal(store.keyPath, "id");
deepEqual(Array.from(store.indexNames).sort(), ['Index-a', 'Index-b']);
equal(store.index('Index-a').keyPath, 'doc.a');
equal(store.index('Index-b').keyPath, 'doc.b');
equal(store.index('Index-a').unique, false);
equal(store.index('Index-b').unique, false);
return new RSVP.Promise(function (resolve) {
records = store.getAll();
records.onsuccess = function () {
resolve(records);
};
});
})
.then(function (values) {
deepEqual(values.result.sort(idCompare), [
{"id": "32", "doc": {"a": "894", "b": "inversion"}},
{"id": "33", "doc": {"a": "65", "b": "division"}},
{"id": "34", "doc": {"a": "65", "b": "prolong"}}
]);
})
.then(function () {
request.result.close();
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// indexStorage2.post
/////////////////////////////////////////////////////////////////
module("indexStorage2.post", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("Post creates index", function () {
var context = this, request, store, records;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(12);
DummyStorage3.prototype.post = function (value) {
if (value.a === "5") {
return "1";
}
if (value.a === "62") {
return "2";
}
if (value.a === "37") {
return "3";
}
};
RSVP.all([
context.jio.post({"a": "5", "b": "inversion", "c": 2}),
context.jio.post({"a": "62", "b": "division", "c": 4}),
context.jio.post({"a": "37", "b": "prolong", "c": 8})
])
.then(function () {
return new RSVP.Promise(function (resolve) {
request = indexedDB.open('jio:index2_test');
request.onsuccess = function () {
resolve(request.result);
};
});
})
.then(function (result) {
equal(result.version, 1);
equal(result.name, 'jio:index2_test');
equal(result.objectStoreNames.length, 1);
equal(result.objectStoreNames[0], 'index-store');
store = result.transaction('index-store').objectStore('index-store');
equal(store.indexNames.length, 2);
equal(store.keyPath, "id");
deepEqual(Array.from(store.indexNames).sort(), ['Index-a', 'Index-b']);
equal(store.index('Index-a').keyPath, 'doc.a');
equal(store.index('Index-b').keyPath, 'doc.b');
equal(store.index('Index-a').unique, false);
equal(store.index('Index-b').unique, false);
return new RSVP.Promise(function (resolve) {
records = store.getAll();
records.onsuccess = function () {
resolve(records);
};
});
})
.then(function (values) {
deepEqual(values.result.sort(idCompare), [
{"id": "1", "doc": {"a": "5", "b": "inversion"}},
{"id": "2", "doc": {"a": "62", "b": "division"}},
{"id": "3", "doc": {"a": "37", "b": "prolong"}}
]);
})
.then(function () {
request.result.close();
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// indexStorage2.remove
/////////////////////////////////////////////////////////////////
module("indexStorage2.remove", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("Remove values", function () {
var context = this, request, store, records;
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["a", "b"],
sub_storage: {
type: "dummystorage3"
}
});
stop();
expect(3);
DummyStorage3.prototype.put = function (id) {
return id;
};
DummyStorage3.prototype.remove = function (id) {
equal(id, "33");
};
RSVP.all([
context.jio.put("32", {"a": "894", "b": "inversion", "c": 2}),
context.jio.put("33", {"a": "65", "b": "division", "c": 4}),
context.jio.put("34", {"a": "65", "b": "prolong", "c": 8})
])
.then(function () {
return new RSVP.Promise(function (resolve) {
request = indexedDB.open('jio:index2_test');
request.onsuccess = function () {
resolve(request.result);
};
});
})
.then(function (result) {
store = result.transaction('index-store').objectStore('index-store');
return new RSVP.Promise(function (resolve) {
records = store.getAll();
records.onsuccess = function () {
resolve(records);
};
});
})
.then(function (values) {
deepEqual(values.result.sort(idCompare), [
{"id": "32", "doc": {"a": "894", "b": "inversion"}},
{"id": "33", "doc": {"a": "65", "b": "division"}},
{"id": "34", "doc": {"a": "65", "b": "prolong"}}
]);
})
.then(function () {
return context.jio.remove("33");
})
.then(function () {
store = request.result.transaction('index-store')
.objectStore('index-store');
return new RSVP.Promise(function (resolve) {
records = store.getAll();
records.onsuccess = function () {
resolve(records);
};
});
})
.then(function (values) {
deepEqual(values.result.sort(idCompare), [
{"id": "32", "doc": {"a": "894", "b": "inversion"}},
{"id": "34", "doc": {"a": "65", "b": "prolong"}}
]);
})
.then(function () {
request.result.close();
})
.fail(function (error) {
console.log(error);
})
.always(function () {
start();
});
});
}(jIO, QUnit, indexedDB, Blob)); }(jIO, QUnit, indexedDB, Blob));
\ No newline at end of file
...@@ -162,7 +162,7 @@ ...@@ -162,7 +162,7 @@
}; };
DummyStorage2.prototype.put = function (id, value) { DummyStorage2.prototype.put = function (id, value) {
equal(id, 'posted'); equal(id, 'posted');
deepEqual(value, {'id': 'posted'}); deepEqual(value, {});
return id; return id;
}; };
...@@ -202,7 +202,7 @@ ...@@ -202,7 +202,7 @@
}; };
DummyStorage2.prototype.put = function (id, param) { DummyStorage2.prototype.put = function (id, param) {
equal(id, "1"); equal(id, "1");
deepEqual(param, {'id': '1'}); deepEqual(param, {});
return id; return id;
}; };
...@@ -372,6 +372,7 @@ ...@@ -372,6 +372,7 @@
///////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////
module("ListStorage.hasCapacity"); module("ListStorage.hasCapacity");
test("list capacity is implemented", function () { test("list capacity is implemented", function () {
expect(2);
var jio = jIO.createJIO({ var jio = jIO.createJIO({
type: "list", type: "list",
...@@ -386,6 +387,10 @@ ...@@ -386,6 +387,10 @@
DummyStorage1.prototype.hasCapacity = function () { DummyStorage1.prototype.hasCapacity = function () {
return false; return false;
}; };
DummyStorage2.prototype.hasCapacity = function (capacity) {
equal(capacity, 'list');
return true;
};
ok(jio.hasCapacity("list")); ok(jio.hasCapacity("list"));
}); });
...@@ -396,7 +401,7 @@ ...@@ -396,7 +401,7 @@
module("ListStorage.buildQuery"); module("ListStorage.buildQuery");
test("buildQuery calls substorage buildQuery", function () { test("buildQuery calls substorage buildQuery", function () {
stop(); stop();
expect(2); expect(1);
var jio = jIO.createJIO({ var jio = jIO.createJIO({
type: "list", type: "list",
...@@ -408,12 +413,11 @@ ...@@ -408,12 +413,11 @@
} }
}); });
DummyStorage2.prototype.buildQuery = function (params) { DummyStorage2.prototype.buildQuery = function () {
deepEqual(params, {});
return [{"id": "1"}, {"id": "2"}]; return [{"id": "1"}, {"id": "2"}];
}; };
jio.buildQuery({}) jio.buildQuery()
.then(function (result) { .then(function (result) {
deepEqual(result, [{"id": "1"}, {"id": "2"}]); deepEqual(result, [{"id": "1"}, {"id": "2"}]);
}) })
......
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