ydn.db
Version:
Javascript database library for IndexedDB, WebDatabase (WebSQL) and WebStorage (localStorage) storage mechanisms supporting version migration, advanced query and transaction workflow.
622 lines (530 loc) • 16.8 kB
JavaScript
(function() {
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_inline_index = 'ts6'; // in-line key store
var db_type;
var schema_1 = {
stores: [
{
name: store_inline,
keyPath: 'id'},
{
name: store_outline},
{
name: store_inline_auto,
keyPath: 'id',
autoIncrement: true},
{
name: store_outline_auto,
autoIncrement: true
},
{
name: store_inline_index,
keyPath: 'id',
type: 'NUMERIC',
indexes: [
{name: 'name', type: 'TEXT'},
{name: 'value', type: 'NUMERIC'},
{name: 'name, value', keyPath: ['name', 'value']},
{name: 'tags', type: 'TEXT', multiEntry: true}
]
}
]
};
var db_name = 'test_query_count';
var db_r;
var df = $.Deferred();
// persist store data.
(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: 2, name: 'b' + Math.random()},
{id: 3, value: 2, name: 'b' + Math.random()},
{id: 4, value: 3, 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_index).always(function() {
db_type = _db.getType();
_db.close();
setTimeout(function() {
df.resolve();
}, 100);
});
})();
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) {
ydn.db.deleteDatabase(db_r.getName(), db_r.getType());
db_r.close();
}
}
};
module('query,count', test_env);
reporter.createTestSuite('query', 'count');
asyncTest('primary key', 2, function() {
df.always(function() {
var q = db_r.from(store_inline);
q.count().always(function(x) {
equal(x, 4, 'all records');
});
q = db_r.from(store_inline, '>', 1, '<=', 3);
q.count().always(function(x) {
equal(x, 2, 'number of records in a bounded range');
start();
});
});
});
asyncTest('by index iterator', 3, function() {
df.always(function() {
var value_iter = db_r.from(store_inline_index)
.where('value', '>', 2, '<=', 3);
value_iter.count().always(function(x) {
//console.log('count value')
equal(x, 1, 'number of values in the range');
});
var name_iter = db_r.from(store_inline_index).where('name', '^', 'b');
name_iter.count().always(function(x) {
equal(x, 2, 'number of name in the range');
});
var iter = db_r.from(store_inline_index).select('value').unique(true);
iter.count().always(function(x) {
equal(x, 2, 'unique count');
start();
});
});
});
})();
(function() {
var db_name = 'test_query_to_list';
var test_count = 0;
var df = $.Deferred();
var store_inline = 'st0';
var store_outline = 'st1';
var store_inline_auto = 'st2';
var store_outline_auto = 'st3';
var store_inline_index = 'st4';
var schema_1 = {
stores: [
{
name: store_inline,
keyPath: 'id'},
{
name: store_outline},
{
name: store_inline_auto,
keyPath: 'id',
autoIncrement: true},
{
name: store_outline_auto,
autoIncrement: true
},
{
name: store_inline_index,
keyPath: 'id',
type: 'NUMERIC',
indexes: [
{name: 'name', type: 'TEXT'},
{name: 'value', type: 'NUMERIC'},
{name: 'name, value', keyPath: ['name', 'value']},
{name: 'tags', type: 'TEXT', multiEntry: true}
]
}
]
};
var objs = [
{test: 't' + Math.random(), value: 4, id: 0, name: 'a', tags: ['a', 'b']},
{test: 't' + Math.random(), value: 10, id: 1, name: 'a', tags: ['x']},
{test: 't' + Math.random(), value: 0, id: 2, name: 'a', tags: ['z']},
{test: 't' + Math.random(), value: 2, id: 3, name: 'bc',
tags: ['a', 'd', 'c']},
{test: 't' + Math.random(), value: 2, id: 4, name: 'bc', tags: ['e', 'c']},
{test: 't' + Math.random(), value: 8, id: 5, name: 'c', tags: ['b']},
{test: 't' + Math.random(), value: 2, id: 6, name: 'c', tags: ['a']}
];
var cmp_value = function(a, b) {
return a.value > b.value ? 1 : a.value < b.value ? -1 :
a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
};
// persist store data.
(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() {
_db.close();
setTimeout(function() {
df.resolve();
}, 100);
});
})();
var db;
var test_env = {
setup: function() {
db = new ydn.db.Storage(db_name, schema_1, options);
},
teardown: function() {
var type = db.getType();
test_count++;
db.close();
if (test_count == 4) {
ydn.db.deleteDatabase(db.getName(), type);
}
}
};
module('query,list', test_env);
reporter.createTestSuite('query', 'list');
var sorted_objs = objs.slice().sort(function(a, b) {
return a.value > b.value ? 1 : -1;
});
var keys = objs.map(function(x) {return x.id;});
asyncTest('primary key range', 3, function() {
df.always(function() {
var q = db.from(store_inline_index, '>=', 1, '<=', 3);
q.list().always(function(x) {
//console.log(q)
deepEqual(x, objs.slice(1, 4), 'closed bound');
});
q.copy().reverse().list().always(function(x) {
var exp = objs.slice(1, 4).reverse();
deepEqual(x, exp, 'closed bound reverse');
});
q.copy().list(1).always(function(x) {
deepEqual(x, objs.slice(1, 2), 'closed bound limit');
start();
});
});
});
asyncTest('by index key range', 4, function() {
df.always(function() {
var q;
q = db.from(store_inline_index).where('value', '>=', 2, '<=', 4);
q.list().always(function(x) {
//console.log(q)
var result = objs.filter(function(x) {
return x.value >= 2 && x.value <= 4;
}).sort(cmp_value);
deepEqual(x, result, 'closed bound');
});
q = db.from(store_inline_index).where('value', '<', 4);
q.list().always(function(x) {
//console.log(q)
var result = objs.filter(function(x) {
return x.value < 4;
}).sort(cmp_value);
deepEqual(x, result, 'open upperBound');
});
q = db.from(store_inline_index).where('value', '>=', 2, '<=', 4);
var result_limit = objs.filter(function(x) {
return x.value >= 2 && x.value <= 4;
}).sort(cmp_value);
q.list(2).always(function(x) {
//console.log(q)
deepEqual(x, result_limit.slice(0, 2), 'first limit');
q.list().always(function(x) {
//console.log(q)
deepEqual(x, result_limit.slice(2), 'last limit');
start();
});
});
});
});
asyncTest('ordering', 5, function() {
df.always(function() {
var q = db.from(store_inline_index);
q.list().always(function(x) {
//console.log(q)
deepEqual(x, objs, 'natural order');
});
q = db.from(store_inline_index).order('value');
q.list().always(function(x) {
//console.log(q)
deepEqual(x, sorted_objs, 'simple index order');
});
q = db.from(store_inline_index).where('value', '>=', 2, '<=', 4);
q = q.order('value');
q.list().always(function(x) {
//console.log(q)
var result = objs.filter(function(x) {
return x.value >= 2 && x.value <= 4;
}).sort(function(a, b) {
return a.value > b.value ? 1 : a.value == b.value ? 0 : -1;
});
deepEqual(x, result, 'closed bound');
});
q = db.from(store_inline_index).where('name', '>', 'a');
throws(function() {
q = q.order('value');
}, Error, 'impossible ordering'
);
q = db.from(store_inline_index).where('name', '=', 'a');
q = q.order('value');
q.list().always(function(x) {
//console.log(q)
deepEqual(x, [objs[2], objs[0], objs[1]], 'compound order');
start();
});
});
});
asyncTest('primary keys by key range', 3, function() {
df.always(function() {
var q = db.from(store_inline_index, '>=', 1, '<=', 3);
q.select('id').list().always(function(x) {
// console.log(x, keys.slice(1, 4))
deepEqual(x, keys.slice(1, 4), 'closed bound');
});
q.copy().reverse().select('id').list().always(function(x) {
var exp = keys.slice(1, 4).reverse();
deepEqual(x, exp, 'closed bound reverse');
});
q.copy().select('id').list(1).always(function(x) {
deepEqual(x, keys.slice(1, 2), 'closed bound limit');
start();
});
});
});
})();
(function() {
var db_name = 'test_query_to_list-2';
var test_count = 0;
var df = $.Deferred();
var store_inline = 'st0';
var store_outline = 'st1';
var store_inline_auto = 'st2';
var store_outline_auto = 'st3';
var store_inline_index = 'st4';
var schema_1 = {
stores: [
{
name: store_inline,
keyPath: 'id'},
{
name: store_outline},
{
name: store_inline_auto,
keyPath: 'id',
autoIncrement: true},
{
name: store_outline_auto,
autoIncrement: true
},
{
name: store_inline_index,
keyPath: 'id',
type: 'NUMERIC',
indexes: [
{name: 'name', type: 'TEXT'},
{name: 'value', type: 'NUMERIC'},
{name: 'name, value', keyPath: ['name', 'value']},
{name: 'tags', type: 'TEXT', multiEntry: true}
]
}
]
};
var objs = [
{test: 't' + Math.random(), value: 4, id: 0, name: 'a', tags: ['a', 'b']},
{test: 't' + Math.random(), value: 10, id: 1, name: 'a', tags: ['b']},
{test: 't' + Math.random(), value: 0, id: 2, name: 'a', tags: ['a']},
{test: 't' + Math.random(), value: 2, id: 3, name: 'bc',
tags: ['a', 'b', 'c']},
{test: 't' + Math.random(), value: 2, id: 4, name: 'bc', tags: ['a', 'c']},
{test: 't' + Math.random(), value: 8, id: 5, name: 'c', tags: ['b']},
{test: 't' + Math.random(), value: 2, id: 6, name: 'c', tags: ['a']}
];
var cmp_value = function(a, b) {
return a.value > b.value ? 1 : a.value < b.value ? -1 :
a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
};
// persist store data.
(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() {
_db.close();
setTimeout(function() {
df.resolve();
}, 100);
});
})();
var db;
module('query,query', {
setup: function() {
db = new ydn.db.Storage(db_name, schema_1, options);
},
teardown: function() {
var type = db.getType();
test_count++;
db.close();
ydn.db.deleteDatabase(db.getName(), type);
}
});
reporter.createTestSuite('query', 'query');
var sorted_objs = objs.slice().sort(function(a, b) {
return a.value > b.value ? 1 : -1;
});
var keys = objs.map(function(x) {return x.id;});
asyncTest('unique index', 1, function() {
df.always(function() {
var q = db.from(store_inline_index).select('tags').unique(true);
q.list().always(function(x) {
//console.log(q)
deepEqual(x, ['a', 'b', 'c'], 'unique index keys');
start();
});
});
});
})();
(function() {
var db_name = 'test_query_patch';
var test_count = 0;
var df = $.Deferred();
var store_inline = 'st0';
var store_outline = 'st1';
var store_inline_auto = 'st2';
var store_outline_auto = 'st3';
var store_inline_index = 'st4';
var schema_1 = {
stores: [
{
name: store_inline,
keyPath: 'id'},
{
name: store_outline},
{
name: store_inline_auto,
keyPath: 'id',
autoIncrement: true},
{
name: store_outline_auto,
autoIncrement: true
},
{
name: store_inline_index,
keyPath: 'id',
type: 'NUMERIC',
indexes: [
{name: 'name', type: 'TEXT'},
{name: 'value', type: 'NUMERIC'},
{name: 'name, value', keyPath: ['name', 'value']},
{name: 'tags', type: 'TEXT', multiEntry: true}
]
}
]
};
var objs = [
{test: 't' + Math.random(), value: 4, id: 0, name: 'a', tags: ['a', 'f']},
{test: 't' + Math.random(), value: 10, id: 1, name: 'a', tags: ['f']},
{test: 't' + Math.random(), value: 0, id: 2, name: 'a', tags: ['a']},
{test: 't' + Math.random(), value: 2, id: 3, name: 'bc',
tags: ['a', 'b', 'c']},
{test: 't' + Math.random(), value: 2, id: 4, name: 'bc', tags: ['a', 'c']},
{test: 't' + Math.random(), value: 8, id: 5, name: 'c', tags: ['b']},
{test: 't' + Math.random(), value: 2, id: 6, name: 'c', tags: ['a']}
];
var cmp_value = function(a, b) {
return a.value > b.value ? 1 : a.value < b.value ? -1 :
a.id > b.id ? 1 : a.id < b.id ? -1 : 0;
};
// persist store data.
(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() {
_db.close();
setTimeout(function() {
df.resolve();
}, 100);
});
})();
var db;
module('query,patch', {
setup: function() {
db = new ydn.db.Storage(db_name, schema_1, options);
},
teardown: function() {
var type = db.getType();
test_count++;
db.close();
if (test_count == 2) {
ydn.db.deleteDatabase(db.getName(), type);
}
}
});
reporter.createTestSuite('query', 'patch');
var sorted_objs = objs.slice().sort(function(a, b) {
return a.value > b.value ? 1 : -1;
});
var keys = objs.map(function(x) {return x.id;});
asyncTest('1. single', 3, function() {
var new_val = 'new-' + Math.random();
var createPatch = function(id) {
var new_obj = JSON.parse(JSON.stringify(objs[id]));
new_obj.test = new_val;
return new_obj;
};
df.always(function() {
var q = db.from(store_inline_index, '=', 0);
q.patch({test: new_val}).always(function() {
db.get(store_inline_index, 0).always(function(x) {
deepEqual(x, createPatch(0), 'by object');
});
});
q = db.from(store_inline_index, '=', 1);
q.patch('test', new_val).always(function() {
db.get(store_inline_index, 1).always(function(x) {
deepEqual(x, createPatch(1), 'by single field');
});
});
q = db.from(store_inline_index, '=', 2);
q.patch(['test', 'name'], [new_val, 'name']).always(function() {
db.get(store_inline_index, 2).always(function(x) {
var obj = createPatch(2); obj.name = 'name';
deepEqual(x, obj, 'by fields');
start();
});
});
});
});
asyncTest('multiple', 2, function() {
var new_val = 'new-' + Math.random();
var createPatch = function(id) {
var new_obj = JSON.parse(JSON.stringify(objs[id]));
new_obj.test = new_val;
return new_obj;
};
var exp = [createPatch(3), createPatch(4), createPatch(6)];
var exp2 = [createPatch(0), createPatch(1)];
df.always(function() {
var q = db.from(store_inline_index).where('value', '=', 2);
q.patch({test: new_val}).always(function() {
db.values(store_inline_index, 'value', ydn.db.KeyRange.only(2)).always(function(x) {
deepEqual(x, exp, 'by object');
});
});
q = db.from(store_inline_index).where('tags', '=', 'f');
q.patch('test', new_val).always(function() {
db.values(store_inline_index, 'tags', ydn.db.KeyRange.only('f')).always(function(x) {
deepEqual(x, exp2, 'by field');
start();
});
});
});
});
})();