ejdb2_node
Version:
EJDB2 Node.js native binding
249 lines (207 loc) • 6.76 kB
JavaScript
/**************************************************************************************************
* EJDB2 Node.js native API binding.
*
* MIT License
*
* Copyright (c) 2012-2022 Softmotions Ltd <info@softmotions.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*************************************************************************************************/
const test = require('ava');
const { assert } = require('chai');
const { EJDB2, JBE } = require('./index');
test('Main', async (t) => {
const db = await EJDB2.open('hello.db', { truncate: true });
let q = db.createQuery('@mycoll/*');
t.true(q != null);
t.is(q.collection, 'mycoll');
t.true(q.db != null);
let id = await db.put('mycoll', { 'foo': 'bar' });
t.is(id, 1);
t.throwsAsync(db.put('mycoll', '{"'), {
code: '@ejdb IWRC:76005 put',
message: 'Unquoted JSON string (JBL_ERROR_PARSE_UNQUOTED_STRING)'
});
let doc = await db.get('mycoll', 1);
t.deepEqual(doc, { foo: 'bar' });
await db.put('mycoll', { 'foo': 'baz' });
const list = await db.createQuery('@mycoll/*').list({ limit: 1 });
t.is(list.length, 1);
let first = await db.createQuery('@mycoll/*').first();
t.true(first != null);
t.deepEqual(first.json, { foo: 'baz' });
t.is(first._raw, null);
first = await db.createQuery('@mycoll/[zzz=bbb]').first();
t.is(first, undefined);
let firstN = await db.createQuery('@mycoll/*').firstN(5);
t.true(firstN != null && firstN.length == 2);
t.deepEqual(firstN[0].json, { foo: 'baz' });
t.deepEqual(firstN[1].json, { foo: 'bar' });
firstN = await db.createQuery('@mycoll/*').firstN(1);
t.true(firstN != null && firstN.length == 1);
t.deepEqual(firstN[0].json, { foo: 'baz' });
// Query 1
const rbuf = [];
for await (const doc of q.stream()) {
rbuf.push(doc.id);
rbuf.push(doc._raw);
}
t.is(rbuf.toString(), '2,{"foo":"baz"},1,{"foo":"bar"}');
// Query 2
rbuf.length = 0;
for await (const doc of db.createQuery('@mycoll/[foo=zaz]').stream()) {
rbuf.push(doc.id);
rbuf.push(doc._raw);
}
t.is(rbuf.length, 0);
// Query 3
for await (const doc of db.createQuery('/[foo=bar]', 'mycoll').stream()) {
rbuf.push(doc.id);
rbuf.push(doc._raw);
}
t.is(rbuf.toString(), '1,{"foo":"bar"}');
let error = null;
try {
await db.createQuery('@mycoll/[').stream();
} catch (e) {
error = e;
t.true(JBE.isInvalidQuery(e));
}
t.true(error != null);
error = null;
let count = await db.createQuery('@mycoll/* | count').scalarInt();
t.is(count, 2);
// Del
await db.del('mycoll', 1);
error = null;
try {
await db.get('mycoll', 1);
} catch (e) {
error = e;
t.true(JBE.isNotFound(e));
}
t.true(error != null);
error = null;
count = await db.createQuery('@mycoll/* | count').scalarInt();
t.is(count, 1);
// Patch
await db.patch('mycoll', '[{"op":"add", "path":"/baz", "value":"qux"}]', 2);
doc = await db.get('mycoll', 2);
t.deepEqual(doc, { foo: 'baz', baz: 'qux' });
// DB Info
doc = await db.info();
assert.deepNestedInclude(doc, {
'collections[0]': {
name: 'mycoll',
rnum: 1,
dbid: 3,
indexes: []
}
});
// Indexes
await db.ensureStringIndex('mycoll', '/foo', true);
doc = await db.info();
assert.deepNestedInclude(doc, {
'collections[0].indexes[0]': {
ptr: '/foo',
mode: 5,
idbf: 0,
dbid: 4,
rnum: 1
}
});
// Test JQL set
doc = await db.createQuery('@mycoll/[foo=:?]').setString(0, 'baz').first();
assert.deepInclude(doc.json, {
foo: 'baz',
baz: 'qux'
});
let log = null;
// Test explain log
await db.createQuery('@mycoll/[foo=:?]').setString(0, 'baz').completionPromise({
explainCallback: (l) => {
log = l;
}
});
assert.isString(log);
t.true(log.indexOf('[INDEX] MATCHED UNIQUE|STR|1 /foo EXPR1: \'foo = :?\' INIT: IWKV_CURSOR_EQ') != -1);
doc = await db.createQuery('@mycoll/[foo=:?] and /[baz=:?]')
.setString(0, 'baz')
.setString(1, 'qux')
.first();
assert.deepInclude(doc.json, {
foo: 'baz',
baz: 'qux'
});
doc = await db.createQuery('@mycoll/[foo=:foo] and /[baz=:baz]')
.setString('foo', 'baz')
.setString('baz', 'qux')
.first();
assert.deepInclude(doc.json, {
foo: 'baz',
baz: 'qux'
});
doc = await db.createQuery('@mycoll/[foo re :?]').setRegexp(0, '.*').first();
assert.deepInclude(doc.json, {
foo: 'baz',
baz: 'qux'
});
doc = await db.createQuery('@mycoll/* | /foo').first();
assert.deepEqual(doc.json, { foo: 'baz' });
await db.removeStringIndex('mycoll', '/foo', true);
doc = await db.info();
assert.deepNestedInclude(doc, {
'collections[0].indexes': []
});
await db.removeCollection('mycoll');
doc = await db.info();
assert.deepNestedInclude(doc, {
'collections': []
});
q = db.createQuery('@c1/* | limit 2');
t.is(q.limit, 2);
q = db.createQuery('@c2/*');
t.is(q.limit, 0);
// Rename collection
id = await db.put('cc1', { 'foo': 1 });
doc = await db.get('cc1', id);
t.deepEqual(doc, { 'foo': 1 });
await db.renameCollection('cc1', 'cc2');
doc = await db.get('cc2', id);
t.deepEqual(doc, { 'foo': 1 });
const ts0 = +new Date();
const ts = await db.onlineBackup('hello-bkp.db');
t.true(ts0 < ts);
await db.close();
// Restore from backup
const db2 = await EJDB2.open('hello-bkp.db', { truncate: false });
doc = await db2.get('cc2', id);
t.deepEqual(doc, { 'foo': 1 });
await db2.close();
//global.gc();
});
// test('GC', async () => {
// global.gc();
// await new Promise((resolve) => {
// global.gc();
// setTimeout(() => {
// resolve();
// }, 500);
// });
// });