ydn.db
Version:
Javascript database library for IndexedDB, WebDatabase (WebSQL) and WebStorage (localStorage) storage mechanisms supporting version migration, advanced query and transaction workflow.
752 lines (618 loc) • 22.3 kB
JavaScript
var options = {}; // options = {mechanisms: ['websql']};
if (/log/.test(location.hash)) {
if (/ui/.test(location.hash)) {
if (ydn.debug && ydn.debug.log) {
var div = document.createElement('div');
document.body.appendChild(div);
ydn.debug.log('ydn.db', 'finest', div);
} else {
console.log('no logging facility');
}
} else {
if (ydn.debug && ydn.debug.log) {
ydn.debug.log('ydn.db', 'finest');
} else {
console.log('no logging facility');
}
}
}
if (/websql/.test(location.hash)) {
options['mechanisms'] = ['websql'];
}
QUnit.config.testTimeout = 2000;
var reporter = new ydn.testing.Reporter('ydn-db', ydn.db.version);
var suite_name = 'iterator';
var db_name = "qunit_test_8";
var db_name_put = "qunit_test_8_rw";
var store_inline = "ts"; // in-line key store
var store_outline = "ts2"; // out-of-line key store
var store_inline_auto = "ts3"; // in-line key + auto
var store_outline_auto = "ts4"; // out-of-line key + auto
var store_nested_key = "ts5"; // nested keyPath
var store_inline_index = "ts6"; // in-line key store
var schema_1 = {
stores: [
{
name: store_inline,
keyPath: 'id',
type: 'NUMERIC'},
{
name: store_outline,
type: 'NUMERIC'},
{
name: store_inline_auto,
keyPath: 'id',
autoIncrement: true,
type: 'INTEGER'},
{
name: store_outline_auto,
autoIncrement: true},
{
name: store_nested_key,
keyPath: 'id.$t', // gdata style key.
type: 'TEXT'},
{
name: store_inline_index,
keyPath: 'id',
type: 'NUMERIC',
indexes: [
{name: 'name', type: 'TEXT'},
{name: 'value', type: 'NUMERIC'},
{name: 'tags', type: 'TEXT', multiEntry: true}
]
}
]
};
(function () {
var db_name = 'test_ver_1_iterator_count';
var db_r;
var df = $.Deferred();
// persist store data.
// we don't want to share this database connection and test database connection.
(function() {
var _db = new ydn.db.Storage(db_name, schema_1, options);
_db.clear(store_inline_index);
_db.put(store_inline_index, [
{id: 1, value: 2, name: 'a' + Math.random()},
{id: 2, value: 4, name: 'b' + Math.random()},
{id: 3, value: 6, name: 'b' + Math.random()},
{id: 4, value: 8, name: 'c' + Math.random()}
]);
_db.clear(store_inline);
_db.put(store_inline, [
{id: 1, value: 'v' + Math.random()},
{id: 2, value: 'v' + Math.random()},
{id: 3, value: 'v' + Math.random()},
{id: 4, value: 'v' + Math.random()}
]);
_db.count(store_inline).always(function() {
df.resolve(); // this ensure all transaction are completed
});
_db.close();
})();
var test_count = 0;
var test_env = {
setup: function () {
db_r = new ydn.db.Storage(db_name, schema_1, options);
},
teardown: function () {
db_r.close();
test_count++;
if (test_count >= 2) {
//console.log(db_r.getName() + ' deleted.')
var type = db_r.getType();
db_r.close();
ydn.db.deleteDatabase(db_r.getName(), type);
}
}
};
module("Count", test_env);
reporter.createTestSuite(suite_name, 'Count');
asyncTest("1. primary key", 5, function () {
df.always(function () {
//db_r.count(store_inline).always(function (x) {
//console.log(x);
//});
var iter = ydn.db.KeyIterator.where(store_inline, '>', 1, '<=', 3);
db_r.count(iter).always(function (x) {
equal(x, 2, 'number of records in a bounded range');
});
iter = new ydn.db.KeyIterator(store_inline, ydn.db.KeyRange.lowerBound(2));
db_r.count(iter).always(function (x) {
equal(x, 3, 'number of records in lowerBound');
});
iter = new ydn.db.KeyIterator(store_inline, ydn.db.KeyRange.lowerBound(2, true));
db_r.count(iter).always(function (x) {
equal(x, 2, 'number of records in open lowerBound');
});
iter = new ydn.db.KeyIterator(store_inline, ydn.db.KeyRange.upperBound(2));
db_r.count(iter).always(function (x) {
equal(x, 2, 'number of records in upperBound');
});
iter = new ydn.db.KeyIterator(store_inline, ydn.db.KeyRange.upperBound(2, true));
db_r.count(iter).always(function (x) {
equal(x, 1, 'number of records in open upperBound');
start();
});
});
});
asyncTest("2. by index iterator", 2, function () {
df.always(function () {
var value_iter = ydn.db.IndexIterator.where(store_inline_index, 'value', '>', 1, '<=', 3);
var name_iter = ydn.db.IndexIterator.where(store_inline_index, 'name', '^', 'b');
db_r.countOf(value_iter).always(function (x) {
//console.log('count value')
equal(x, 1, 'number of values in the range');
});
db_r.countOf(name_iter).always(function (x) {
equal(x, 2, 'number of name in the range');
start();
});
});
});
})();
(function () {
var db_name = 'test_ver_1_iterator_get_1';
var schema_1 = {
stores: [
{
name: store_inline_index,
keyPath: 'id',
type: 'NUMERIC',
indexes: [
{name: 'name', type: 'TEXT'},
{name: 'value', type: 'NUMERIC'},
{name: 'tags', type: 'TEXT', multiEntry: true}
]
}
]
};
var test_count = 0;
var db_r = new ydn.db.Storage(db_name, schema_1, options);
var objs = [
{id: 1, value: 2, name: 'a' + Math.random()},
{id: 2, value: 4, name: 'b' + Math.random()},
{id: 3, value: 6, name: 'b' + Math.random()},
{id: 4, value: 8, name: 'c' + Math.random()}
];
module("Get", {
setup: function () {
db_r.clear(store_inline_index);
db_r.put(store_inline_index, objs);
},
teardown: function () {
test_count++;
if (test_count >= 4) {
var type = db_r.getType();
db_r.close();
ydn.db.deleteDatabase(db_r.getName(), type);
}
}
});
reporter.createTestSuite(suite_name, 'Get');
asyncTest("effective key by an iterator", function () {
expect(1);
var iter = ydn.db.KeyIterator.where(store_inline_index, '>', 1, '<=', 3);
db_r.get(iter).then(function (x) {
equal(x, objs[1].id, 'get item 2 key');
start();
}, function (e) {
ok(false, e.message);
start();
});
});
asyncTest("ref value by an iterator", function () {
expect(1);
var iter = ydn.db.ValueIterator.where(store_inline_index, '>', 1, '<=', 3);
db_r.get(iter).then(function (x) {
deepEqual(x, objs[1], 'get item 2 value');
start();
}, function (e) {
ok(false, e.message);
start();
});
});
asyncTest("effective key by an index iterator", 1, function () {
var iter = ydn.db.IndexIterator.where(store_inline_index, 'name', '^', 'c');
db_r.get(iter).then(function (x) {
equal(x, objs[3].id, 'get item 3 key');
start();
}, function (e) {
ok(false, e.message);
start();
});
});
asyncTest("ref value by an iterator", 1, function () {
var iter = ydn.db.IndexValueIterator.where(store_inline_index, 'name', '^', 'c');
db_r.get(iter).then(function (x) {
deepEqual(x, objs[3], 'get item 3 value');
start();
}, function (e) {
ok(false, e.message);
start();
});
});
})();
(function () {
var db_name = 'test_ver_1_iterator_list-2';
var test_count = 0;
var df = $.Deferred();
var objs = [
{test: 't' + Math.random(), value: 0, id: 0, name: 'a', tags: ['a', 'b']},
{test: 't' + Math.random(), value: 2, id: 1, name: 'b', tags: ['x']},
{test: 't' + Math.random(), value: 4, id: 2, name: 'ba', tags: ['z']},
{test: 't' + Math.random(), value: 6, id: 3, name: 'bc', tags: ['a', 'd', 'c']},
{test: 't' + Math.random(), value: 8, id: 4, name: 'bd', tags: ['e', 'c']},
{test: 't' + Math.random(), value: 10, id: 5, name: 'c', tags: ['b']},
{test: 't' + Math.random(), value: 12, id: 6, name: 'c', tags: ['a']}
];
// persist store data.
// we don't want to share this database connection and test database connection.
(function() {
var _db = new ydn.db.Storage(db_name, schema_1, options);
_db.clear(store_inline_index);
_db.put(store_inline_index, objs);
_db.count(store_inline_index).always(function() {
df.resolve(); // this ensure all transactions are completed
});
_db.close();
})();
var db;
var test_env = {
setup: function () {
db = new ydn.db.Storage(db_name, schema_1, options);
},
teardown: function () {
var type = db.getType();
db.close();
test_count++;
if (test_count >= 5) {
ydn.db.deleteDatabase(db_name, type);
}
}
};
module("values", test_env);
reporter.createTestSuite(suite_name, 'values');
asyncTest("1. Ref value by primary key range", 9, function () {
df.always(function () {
var q = new ydn.db.ValueIterator(store_inline_index);
db.values(q).always(function (x) {
//console.log(q)
deepEqual(x, objs, 'all');
});
var key_range = ydn.db.KeyRange.bound(1, 3);
var q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.values(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(1, 4), 'closed bound');
});
key_range = ydn.db.KeyRange.bound(1, 3);
q = new ydn.db.ValueIterator(store_inline_index, key_range, true);
db.values(q).always(function (x) {
var exp = objs.slice(1, 4).reverse();
deepEqual(x, exp, 'closed bound reverse');
});
key_range = ydn.db.KeyRange.bound(1, 3);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.values(q, 1).always(function (x) {
deepEqual(x, objs.slice(1, 2), 'closed bound limit');
});
key_range = ydn.db.KeyRange.bound(1, 3);
q = new ydn.db.ValueIterator(store_inline_index, key_range, true);
db.values(q, 1).always(function (x) {
deepEqual(x, objs.slice(3, 4), 'closed bound reverse limit');
});
key_range = ydn.db.KeyRange.lowerBound(2);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.values(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(2), 'lowerBound');
});
key_range = ydn.db.KeyRange.lowerBound(2, true);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.values(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(3), 'open lowerBound');
});
key_range = ydn.db.KeyRange.upperBound(2);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.values(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(0, 3), 'upperBound');
});
key_range = ydn.db.KeyRange.upperBound(2, true);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.values(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(0, 2), 'open upperBound');
start();
});
})
});
asyncTest("2. Ref value by index key range", 5, function () {
var q = ydn.db.IndexValueIterator.where(store_inline_index, 'value', '>=', 2, '<=', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(1, 3), 'closed bound');
});
q = ydn.db.IndexValueIterator.where(store_inline_index, 'value', '>=', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(2), 'lowerBound');
});
q = ydn.db.IndexValueIterator.where(store_inline_index, 'value', '>', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(3), 'open lowerBound');
});
q = ydn.db.IndexValueIterator.where(store_inline_index, 'value', '<=', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(0, 3), 'upperBound');
});
q = ydn.db.IndexValueIterator.where(store_inline_index, 'value', '<', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, objs.slice(0, 2), 'open upperBound');
start();
});
});
asyncTest("3. Ref value by index key range", 6, function () {
var keys = objs.map(function(x) {return x.id});
var q = ydn.db.IndexIterator.where(store_inline_index, 'value', '>=', 2, '<=', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(1, 3), 'closed bound');
});
q = ydn.db.IndexIterator.where(store_inline_index, 'value', '>=', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(2), 'lowerBound');
});
q = ydn.db.IndexIterator.where(store_inline_index, 'value', '>', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(3), 'open lowerBound');
});
q = ydn.db.IndexIterator.where(store_inline_index, 'value', '<=', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(0, 3), 'upperBound');
});
q = new ydn.db.IndexIterator(store_inline_index, 'value', null, true);
db.valuesOf(q, 3).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice().reverse().slice(0,3), 'reverse key range');
});
q = ydn.db.IndexIterator.where(store_inline_index, 'value', '<', 4);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(0, 2), 'open upperBound');
start();
});
});
asyncTest("4. Ref value by string index key range", 4, function () {
var q = ydn.db.IndexValueIterator.where(store_inline_index, 'name', '^', 'b');
db.valuesOf(q).always(function (x) {
//console.log(q)
equal(x.length, 4, 'LIKE%');
});
q = ydn.db.IndexValueIterator.where(store_inline_index, 'name', '=', 'b');
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x.length, 1, 'equal');
});
q = ydn.db.IndexValueIterator.where(store_inline_index, 'name', '<', 'b');
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x.length, 1, '<');
});
q = ydn.db.IndexValueIterator.where(store_inline_index, 'name', '^', 'd');
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x.length, 0, 'LIKE% no result');
start();
});
});
asyncTest("5. multiEntry IndexIterator", 4, function () {
var range = ydn.db.KeyRange.only('a');
var q = new ydn.db.IndexIterator(store_inline_index, 'tags', range);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, [objs[0].id, objs[3].id, objs[6].id], 'ref value only a');
});
range = ydn.db.KeyRange.only('a');
q = new ydn.db.IndexValueIterator(store_inline_index, 'tags', range);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, [objs[0], objs[3], objs[6]], 'only a');
});
q = new ydn.db.IndexIterator(store_inline_index, 'tags', range, false, true);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, [objs[0].id], 'only a unique');
});
q = new ydn.db.IndexValueIterator(store_inline_index, 'tags', range, false, true);
db.valuesOf(q).always(function (x) {
//console.log(q)
deepEqual(x, [objs[0]], 'only a unique');
start();
});
});
})();
(function () {
var test_count = 0;
var db_name = 'test_tck2_key';
var df = $.Deferred();
var objs = [
{test: 't' + Math.random(), value: 0, id: 0, name: 'a', tags: ['a', 'b']},
{test: 't' + Math.random(), value: 2, id: 1, name: 'b', tags: []},
{test: 't' + Math.random(), value: 4, id: 2, name: 'ba', tags: ['z']},
{test: 't' + Math.random(), value: 6, id: 3, name: 'bc', tags: ['a', 'd', 'c']},
{test: 't' + Math.random(), value: 8, id: 4, name: 'bd', tags: ['e', 'c']},
{test: 't' + Math.random(), value: 10, id: 5, name: 'c', tags: []},
{test: 't' + Math.random(), value: 12, id: 6, name: 'c', tags: ['a']}
];
// persist store data.
// we don't want to share this database connection and test database connection.
(function() {
var _db = new ydn.db.Storage(db_name, schema_1, options);
_db.clear(store_inline_index);
_db.put(store_inline_index, objs);
_db.count(store_inline_index).always(function() {
df.resolve(); // this ensure all transactions are completed
});
_db.close();
})();
var db;
var test_env = {
setup: function () {
db = new ydn.db.Storage(db_name, schema_1, options);
},
teardown: function () {
db.close();
test_count++;
if (test_count >= 3) {
var type = db.getType();
ydn.db.deleteDatabase(db_name, type);
}
}
};
module("keys", test_env);
reporter.createTestSuite(suite_name, 'keys');
asyncTest("1. Effective key by by primary key range", 8, function () {
df.always(function () {
var keys = objs.map(function(x) {return x.id});
var key_range = ydn.db.KeyRange.bound(1, 3);
var q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(1, 4), 'closed bound');
});
key_range = ydn.db.KeyRange.bound(1, 3);
q = new ydn.db.ValueIterator(store_inline_index, key_range, true);
db.keysOf(q).always(function (x) {
var exp = keys.slice(1, 4).reverse();
deepEqual(x, exp, 'closed bound reverse');
});
key_range = ydn.db.KeyRange.bound(1, 3);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.keysOf(q, 1).always(function (x) {
deepEqual(x, keys.slice(1, 2), 'closed bound limit');
});
key_range = ydn.db.KeyRange.bound(1, 3);
q = new ydn.db.ValueIterator(store_inline_index, key_range, true);
db.keysOf(q, 1).always(function (x) {
deepEqual(x, keys.slice(3, 4), 'closed bound reverse limit');
});
key_range = ydn.db.KeyRange.lowerBound(2);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(2), 'lowerBound');
});
key_range = ydn.db.KeyRange.lowerBound(2, true);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(3), 'open lowerBound');
});
key_range = ydn.db.KeyRange.upperBound(2);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(0, 3), 'upperBound');
});
key_range = ydn.db.KeyRange.upperBound(2, true);
q = new ydn.db.ValueIterator(store_inline_index, key_range);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(0, 2), 'open upperBound');
start();
});
})
});
asyncTest("2. Effective key by index key range", function () {
var keys = objs.map(function (x) {
return x.value;
});
expect(5);
var q = ydn.db.IndexIterator.where(store_inline_index, 'value', '>=', 2, '<=', 4);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(1, 3), 'closed bound');
});
q = ydn.db.IndexIterator.where(store_inline_index, 'value', '>=', 4);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(2), 'lowerBound');
});
q = ydn.db.IndexIterator.where(store_inline_index, 'value', '>', 4);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(3), 'open lowerBound');
});
q = ydn.db.IndexIterator.where(store_inline_index, 'value', '<=', 4);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(0, 3), 'upperBound');
});
q = ydn.db.IndexIterator.where(store_inline_index, 'value', '<', 4);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, keys.slice(0, 2), 'open upperBound');
start();
});
});
asyncTest("3. Effective key by multiEntry index key range", 6, function () {
var range = ydn.db.KeyRange.only('a');
var q = new ydn.db.IndexIterator(store_inline_index, 'tags', range);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, ['a', 'a', 'a'], 'only a');
});
range = ydn.db.KeyRange.only('a');
q = new ydn.db.IndexValueIterator(store_inline_index, 'tags', range);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, ['a', 'a', 'a'], 'only a');
});
q = new ydn.db.IndexIterator(store_inline_index, 'tags', range, false, true);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, ['a'], 'only a unique');
});
q = new ydn.db.IndexValueIterator(store_inline_index, 'tags', range, false, true);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, ['a'], 'only a unique');
});
var result = [];
for (var i = 0; i < objs.length; i++) {
result = result.concat(objs[i].tags);
}
result.sort();
var q = new ydn.db.IndexIterator(store_inline_index, 'tags');
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, result, 'all');
});
var q = new ydn.db.IndexIterator(store_inline_index, 'tags', null, false, true);
db.keysOf(q).always(function (x) {
//console.log(q)
deepEqual(x, ['a', 'b', 'c', 'd', 'e', 'z'], 'all unique');
start();
});
});
})();
QUnit.testDone(function(result) {
reporter.addResult(suite_name, result.module,
result.name, result.failed, result.passed, result.duration);
});
QUnit.moduleDone(function(result) {
reporter.endTestSuite(suite_name, result.name,
{passed: result.passed, failed: result.failed});
});
QUnit.done(function(results) {
reporter.report();
});