• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/**************************************************************************************************
2 * EJDB2 Node.js native API binding.
3 *
4 * MIT License
5 *
6 * Copyright (c) 2012-2022 Softmotions Ltd <info@softmotions.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 *  copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in all
16 * copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 *************************************************************************************************/
26
27const test = require('ava');
28const { assert } = require('chai');
29const { EJDB2, JBE } = require('./index');
30
31test('Main', async (t) => {
32  const db = await EJDB2.open('hello.db', { truncate: true });
33
34  let q = db.createQuery('@mycoll/*');
35  t.true(q != null);
36  t.is(q.collection, 'mycoll');
37  t.true(q.db != null);
38
39  let id = await db.put('mycoll', { 'foo': 'bar' });
40  t.is(id, 1);
41  t.throwsAsync(db.put('mycoll', '{"'), {
42    code: '@ejdb IWRC:76005 put',
43    message: 'Unquoted JSON string (JBL_ERROR_PARSE_UNQUOTED_STRING)'
44  });
45
46  let doc = await db.get('mycoll', 1);
47  t.deepEqual(doc, { foo: 'bar' });
48
49  await db.put('mycoll', { 'foo': 'baz' });
50
51  const list = await db.createQuery('@mycoll/*').list({ limit: 1 });
52  t.is(list.length, 1);
53
54  let first = await db.createQuery('@mycoll/*').first();
55  t.true(first != null);
56  t.deepEqual(first.json, { foo: 'baz' });
57  t.is(first._raw, null);
58
59  first = await db.createQuery('@mycoll/[zzz=bbb]').first();
60  t.is(first, undefined);
61
62  let firstN = await db.createQuery('@mycoll/*').firstN(5);
63  t.true(firstN != null && firstN.length == 2);
64  t.deepEqual(firstN[0].json, { foo: 'baz' });
65  t.deepEqual(firstN[1].json, { foo: 'bar' });
66
67  firstN = await db.createQuery('@mycoll/*').firstN(1);
68  t.true(firstN != null && firstN.length == 1);
69  t.deepEqual(firstN[0].json, { foo: 'baz' });
70
71  // Query 1
72  const rbuf = [];
73  for await (const doc of q.stream()) {
74    rbuf.push(doc.id);
75    rbuf.push(doc._raw);
76  }
77  t.is(rbuf.toString(), '2,{"foo":"baz"},1,{"foo":"bar"}');
78
79  // Query 2
80  rbuf.length = 0;
81  for await (const doc of db.createQuery('@mycoll/[foo=zaz]').stream()) {
82    rbuf.push(doc.id);
83    rbuf.push(doc._raw);
84  }
85  t.is(rbuf.length, 0);
86
87  // Query 3
88  for await (const doc of db.createQuery('/[foo=bar]', 'mycoll').stream()) {
89    rbuf.push(doc.id);
90    rbuf.push(doc._raw);
91  }
92  t.is(rbuf.toString(), '1,{"foo":"bar"}');
93
94  let error = null;
95  try {
96    await db.createQuery('@mycoll/[').stream();
97  } catch (e) {
98    error = e;
99    t.true(JBE.isInvalidQuery(e));
100  }
101  t.true(error != null);
102  error = null;
103
104  let count = await db.createQuery('@mycoll/* | count').scalarInt();
105  t.is(count, 2);
106
107  // Del
108  await db.del('mycoll', 1);
109
110  error = null;
111  try {
112    await db.get('mycoll', 1);
113  } catch (e) {
114    error = e;
115    t.true(JBE.isNotFound(e));
116  }
117  t.true(error != null);
118  error = null;
119
120  count = await db.createQuery('@mycoll/* | count').scalarInt();
121  t.is(count, 1);
122
123  // Patch
124  await db.patch('mycoll', '[{"op":"add", "path":"/baz", "value":"qux"}]', 2);
125  doc = await db.get('mycoll', 2);
126  t.deepEqual(doc, { foo: 'baz', baz: 'qux' });
127
128  // DB Info
129  doc = await db.info();
130  assert.deepNestedInclude(doc, {
131    'collections[0]': {
132      name: 'mycoll',
133      rnum: 1,
134      dbid: 3,
135      indexes: []
136    }
137  });
138
139  // Indexes
140  await db.ensureStringIndex('mycoll', '/foo', true);
141  doc = await db.info();
142  assert.deepNestedInclude(doc, {
143    'collections[0].indexes[0]': {
144      ptr: '/foo',
145      mode: 5,
146      idbf: 0,
147      dbid: 4,
148      rnum: 1
149    }
150  });
151
152  // Test JQL set
153  doc = await db.createQuery('@mycoll/[foo=:?]').setString(0, 'baz').first();
154  assert.deepInclude(doc.json, {
155    foo: 'baz',
156    baz: 'qux'
157  });
158
159  let log = null;
160  // Test explain log
161  await db.createQuery('@mycoll/[foo=:?]').setString(0, 'baz').completionPromise({
162    explainCallback: (l) => {
163      log = l;
164    }
165  });
166  assert.isString(log);
167  t.true(log.indexOf('[INDEX] MATCHED  UNIQUE|STR|1 /foo EXPR1: \'foo = :?\' INIT: IWKV_CURSOR_EQ') != -1);
168
169
170  doc = await db.createQuery('@mycoll/[foo=:?] and /[baz=:?]')
171    .setString(0, 'baz')
172    .setString(1, 'qux')
173    .first();
174  assert.deepInclude(doc.json, {
175    foo: 'baz',
176    baz: 'qux'
177  });
178
179  doc = await db.createQuery('@mycoll/[foo=:foo] and /[baz=:baz]')
180    .setString('foo', 'baz')
181    .setString('baz', 'qux')
182    .first();
183  assert.deepInclude(doc.json, {
184    foo: 'baz',
185    baz: 'qux'
186  });
187
188  doc = await db.createQuery('@mycoll/[foo re :?]').setRegexp(0, '.*').first();
189  assert.deepInclude(doc.json, {
190    foo: 'baz',
191    baz: 'qux'
192  });
193
194  doc = await db.createQuery('@mycoll/* | /foo').first();
195  assert.deepEqual(doc.json, { foo: 'baz' });
196
197  await db.removeStringIndex('mycoll', '/foo', true);
198  doc = await db.info();
199  assert.deepNestedInclude(doc, {
200    'collections[0].indexes': []
201  });
202
203  await db.removeCollection('mycoll');
204  doc = await db.info();
205  assert.deepNestedInclude(doc, {
206    'collections': []
207  });
208
209  q = db.createQuery('@c1/* | limit 2');
210  t.is(q.limit, 2);
211
212  q = db.createQuery('@c2/*');
213  t.is(q.limit, 0);
214
215  // Rename collection
216  id = await db.put('cc1', { 'foo': 1 });
217  doc = await db.get('cc1', id);
218  t.deepEqual(doc, { 'foo': 1 });
219
220  await db.renameCollection('cc1', 'cc2');
221  doc = await db.get('cc2', id);
222  t.deepEqual(doc, { 'foo': 1 });
223
224  const ts0 = +new Date();
225  const ts = await db.onlineBackup('hello-bkp.db');
226  t.true(ts0 < ts);
227
228  await db.close();
229
230  // Restore from backup
231  const db2 = await EJDB2.open('hello-bkp.db', { truncate: false });
232  doc = await db2.get('cc2', id);
233  t.deepEqual(doc, { 'foo': 1 });
234  await db2.close();
235
236  //global.gc();
237});
238
239// test('GC', async () => {
240//   global.gc();
241//   await new Promise((resolve) => {
242//     global.gc();
243//     setTimeout(() => {
244//       resolve();
245//     }, 500);
246//   });
247// });
248
249