Commit c39f9581 authored by preetwinder's avatar preetwinder

Migration handling and improved querying support

parent 3e953134
......@@ -29,34 +29,98 @@
if (typeof description.database !== "string" ||
description.database === "") {
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._database_name = "jio:" + description.database;
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) {
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) {
var db = evt.target.result, store, i;
var db = evt.target.result, store, i, current_indices;
store = db.createObjectStore("index-store", {
keyPath: "id",
autoIncrement: false
});
if (!(db.objectStoreNames[0])) {
store = db.createObjectStore("index-store", {
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) {
store.createIndex("Index-" + index_keys[i], "doc." + index_keys[i],
{unique: false});
if (!(current_indices.has('Index-' + index_keys[i]))) {
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) {
// Open DB //
var request = indexedDB.open(db_name);
var request = indexedDB.open(db_name, version);
request.onerror = function (error) {
if (request.result) {
request.result.close();
......@@ -114,6 +178,35 @@
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) {
var tx = db.transaction(stores, flag);
function canceller() {
......@@ -175,29 +268,41 @@
return filtered_doc;
}
IndexStorage2.prototype._runQuery = function (index, value, limit) {
IndexStorage2.prototype._runQuery = function (index, value, limit, sort_on) {
var context = this;
return new RSVP.Queue()
.push(function () {
if ((context._index_keys.indexOf(index) === -1)) {
try {
context._sub_storage.hasCapacity("query");
} catch (error) {
throw new jIO.util.jIOError(
"No index for this key and substorage doesn't support queries"
if (context._use_sub_storage_query_partial) {
try {
context._sub_storage.hasCapacity("query");
} catch (error) {
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(
{"query": index + ":" + value}
);
throw new jIO.util.jIOError("No index for '" + index +
"' key and checking the substorage for partial queries is not set");
}
return waitForOpenIndexedDB(context._database_name, context._index_keys,
function (db) {
return waitForUpdatedOpenIndexedDB(context._database_name,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readonly",
function (tx) {
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store")
.index("Index-" + index).getAll(value, limit))
.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;
});
});
......@@ -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();
return RSVP.Queue()
.push(function () {
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") {
for (i = 0; i < object.query_list.length; i += 1) {
......@@ -249,25 +355,65 @@
};
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) {
return this._processQueryObject(parseStringToObject(options.query),
options.limit)
options.limit, options.sort_on)
.push(function (result) {
if (options.include_docs) {
options.select_list = context._index_keys;
}
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,
function (db) {
return waitForUpdatedOpenIndexedDB(context._database_name,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readonly",
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store")
.getAll(undefined, options.limit))
.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 {"id": value.id, "value": {} };
return {
"id": value.id,
"value": filterDocValues(value.doc,
options.select_list || [])
};
});
});
});
......@@ -282,10 +428,10 @@
var context = this;
return context._sub_storage.put(id, value)
.push(function () {
return waitForOpenIndexedDB(context._database_name, context._index_keys,
function (db) {
return waitForUpdatedOpenIndexedDB(context._database_name,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readwrite",
function (tx) {
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store").put({
"id": id,
"doc": filterDocValues(value, context._index_keys)
......@@ -299,10 +445,10 @@
var context = this;
return context._sub_storage.post(value)
.push(function (id) {
return waitForOpenIndexedDB(context._database_name, context._index_keys,
function (db) {
return waitForUpdatedOpenIndexedDB(context._database_name,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readwrite",
function (tx) {
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store").put({
"id": id,
"doc": filterDocValues(value, context._index_keys)
......@@ -318,16 +464,13 @@
IndexStorage2.prototype.remove = function (id) {
var context = this;
return context._sub_storage.remove(id)
.push(function (result) {
return waitForOpenIndexedDB(context._database_name, context._index_keys,
function (db) {
.push(function () {
return waitForUpdatedOpenIndexedDB(context._database_name,
context._index_keys, function (db) {
return waitForTransaction(db, ["index-store"], "readwrite",
function (tx) {
return waitForIDBRequest(tx.objectStore("index-store")
.delete(id))
.then(function () {
return result;
});
.delete(id));
});
});
});
......
......@@ -37,7 +37,7 @@
var gadget = this;
return gadget._sub_storage.post(value)
.push(function (id) {
return gadget._signature_storage.put(id, {"id": id})
return gadget._signature_storage.put(id, {})
.push(function () {
return id;
});
......@@ -47,7 +47,7 @@
var gadget = this;
return gadget._sub_storage.put(id, value)
.push(function (result) {
return gadget._signature_storage.put(id, {"id": id})
return gadget._signature_storage.put(id, {})
.push(function () {
return result;
});
......@@ -78,11 +78,12 @@
};
ListStorage.prototype.hasCapacity = function (name) {
if (name === "list") {
return true;
return this._signature_storage.hasCapacity('list');
}
return false;
};
ListStorage.prototype.buildQuery = function () {
return this._signature_storage.buildQuery({});
return this._signature_storage.buildQuery();
};
jIO.addStorage('list', ListStorage);
......
......@@ -42,7 +42,7 @@
});
}
function id_compare(value1, value2) {
function idCompare(value1, value2) {
if (value1.id > value2.id) {
return 1;
}
......@@ -83,6 +83,66 @@
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 () {
this.jio = jIO.createJIO({
type: "index2",
......@@ -132,6 +192,9 @@
ok(this.jio.hasCapacity("list"));
ok(this.jio.hasCapacity("query"));
ok(this.jio.hasCapacity("limit"));
ok(this.jio.hasCapacity("select"));
ok(this.jio.hasCapacity("include"));
ok(this.jio.hasCapacity("sort"));
});
/////////////////////////////////////////////////////////////////
......@@ -180,10 +243,13 @@
module("indexStorage2.buildQuery", {
teardown: function () {
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;
context.jio = jIO.createJIO({
type: "index2",
......@@ -196,6 +262,267 @@
stop();
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) {
equal(id, "32");
deepEqual(value, {a: "3", b: "2"});
......@@ -207,7 +534,8 @@
return context.jio.allDocs({query: 'a: "3"'});
})
.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) {
console.log(error);
......@@ -253,12 +581,12 @@
})
.then(function (result) {
equal(result.data.total_rows, 3);
deepEqual(result.data.rows.sort(id_compare),
deepEqual(result.data.rows.sort(idCompare),
[
{"id": "32", "value": {}},
{"id": "21", "value": {}},
{"id": "3", "value": {}}
].sort(id_compare));
].sort(idCompare));
})
.fail(function (error) {
console.log(error);
......@@ -287,20 +615,17 @@
return id;
};
DummyStorage3.prototype.hasCapacity = function (capacity) {
equal(capacity, "query");
return false;
return capacity === 'list';
};
context.jio.put("32", {"a": "3", "b": "2"})
.then(function () {
return context.jio.allDocs({query: 'b:"2"'});
})
.then(function () {
return deleteIndexedDB(context.jio);
})
.fail(function (error) {
equal(error.status_code, 404);
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 () {
start();
......@@ -318,14 +643,13 @@
}
});
stop();
expect(4);
expect(3);
DummyStorage3.prototype.put = function (id) {
return id;
};
DummyStorage3.prototype.hasCapacity = function (capacity) {
equal(capacity, "query");
return (capacity === 'query');
return capacity === 'query';
};
DummyStorage3.prototype.buildQuery = function (options) {
equal(options.query, "a:5");
......@@ -363,14 +687,13 @@
}
});
stop();
expect(4);
expect(3);
DummyStorage3.prototype.put = function (id) {
return id;
};
DummyStorage3.prototype.hasCapacity = function (capacity) {
equal(capacity, "query");
if (capacity === "query") { return true; }
return capacity === 'query';
};
DummyStorage3.prototype.buildQuery = function (options) {
equal(options.query, 'c:linear');
......@@ -387,9 +710,9 @@
})
.then(function (result) {
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": {}}]
.sort(id_compare));
.sort(idCompare));
})
.fail(function (error) {
console.log(error);
......@@ -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 () {
var context = this;
context.jio = jIO.createJIO({
......@@ -571,7 +1020,7 @@
.then(function (result) {
equal(result.data.total_rows, 2);
deepEqual(result.data.rows.sort(), [{"id": "23", "value": {}},
{"id": "14", "value": {}}].sort(id_compare));
{"id": "14", "value": {}}].sort(idCompare));
})
.then(function () {
return context.jio.allDocs({"query": "name:envision AND user:Mann"});
......@@ -585,9 +1034,9 @@
})
.then(function (result) {
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": "45", "value": {}}].sort(id_compare));
{"id": "45", "value": {}}].sort(idCompare));
})
.then(function () {
return context.jio.allDocs(
......@@ -607,7 +1056,7 @@
});
});
/* test("Index keys modified", function () {
test("Index keys are modified", function () {
var context = this;
context.jio = jIO.createJIO({
type: "index2",
......@@ -618,53 +1067,110 @@
}
});
stop();
expect(5);
expect(8);
DummyStorage3.prototype.put = function (id, value) {
equal(id, "32");
deepEqual(value, {a: "3", b: "2"});
DummyStorage3.prototype.put = function (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 () {
return context.jio.allDocs({query: 'a: "3"'});
})
.then(function (result) {
deepEqual(result.data.rows[0], {"id": "32", "value": {}});
deepEqual(result.data.rows, [{"id": "32", "value": {}},
{"id": "62", "value": {}}]);
})
.then(function () {
context.jio = jIO.createJIO({
type: "index2",
database: "index2_test",
index_keys: ["b"],
index_keys: ["a", "c"],
sub_storage: {
type: "dummystorage3"
}
});
})
.then(function () {
console.log(context.jio.__storage._index_keys);
return context.jio.put("32", {"a": "3", "b": "2"});
return context.jio.put("192", {"a": "3", "b": "3", "c": "disolve"});
})
.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 () {
return context.jio.allDocs({query: 'b: "2"'});
return context.jio.allDocs({query: 'c: "disolve"'});
})
.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) {
console.log(error);
equal(error.status_code, 404);
equal(error.message,
"No index for 'b' key and substorage doesn't support queries");
})
.always(function () {
start();
});
});*/
});
/////////////////////////////////////////////////////////////////
// IndexStorage2.getAttachment
/////////////////////////////////////////////////////////////////
module("IndexStorage2.getAttachment");
module("IndexStorage2.getAttachment", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("getAttachment called substorage getAttachment", function () {
stop();
expect(3);
......@@ -700,7 +1206,11 @@
/////////////////////////////////////////////////////////////////
// IndexStorage2.putAttachment
/////////////////////////////////////////////////////////////////
module("IndexStorage2.putAttachment");
module("IndexStorage2.putAttachment", {
teardown: function () {
deleteIndexedDB(this.jio);
}
});
test("putAttachment called substorage putAttachment", function () {
stop();
expect(4);
......@@ -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 () {
stop();
expect(3);
......@@ -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));
\ No newline at end of file
......@@ -162,7 +162,7 @@
};
DummyStorage2.prototype.put = function (id, value) {
equal(id, 'posted');
deepEqual(value, {'id': 'posted'});
deepEqual(value, {});
return id;
};
......@@ -202,7 +202,7 @@
};
DummyStorage2.prototype.put = function (id, param) {
equal(id, "1");
deepEqual(param, {'id': '1'});
deepEqual(param, {});
return id;
};
......@@ -372,6 +372,7 @@
/////////////////////////////////////////////////////////////////
module("ListStorage.hasCapacity");
test("list capacity is implemented", function () {
expect(2);
var jio = jIO.createJIO({
type: "list",
......@@ -386,6 +387,10 @@
DummyStorage1.prototype.hasCapacity = function () {
return false;
};
DummyStorage2.prototype.hasCapacity = function (capacity) {
equal(capacity, 'list');
return true;
};
ok(jio.hasCapacity("list"));
});
......@@ -396,7 +401,7 @@
module("ListStorage.buildQuery");
test("buildQuery calls substorage buildQuery", function () {
stop();
expect(2);
expect(1);
var jio = jIO.createJIO({
type: "list",
......@@ -408,12 +413,11 @@
}
});
DummyStorage2.prototype.buildQuery = function (params) {
deepEqual(params, {});
DummyStorage2.prototype.buildQuery = function () {
return [{"id": "1"}, {"id": "2"}];
};
jio.buildQuery({})
jio.buildQuery()
.then(function (result) {
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