File size: 8,348 Bytes
4e23b01 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | // Drives the query engine through the real db.query() path to cover the
// operators and path handling that the higher-level tests do not exercise.
import { test } from 'vitest';
import assert from 'node:assert/strict';
import fs from 'node:fs/promises';
import os from 'node:os';
import path from 'node:path';
import { MiniDb } from '../src/index.js';
async function tmpDir() {
return fs.mkdtemp(path.join(os.tmpdir(), 'minidb-qe-'));
}
async function seed() {
const dir = await tmpDir();
const db = await MiniDb.open({ dir, valueCodec: 'json' });
await db.set('a', { name: 'Ann', age: 30, city: 'Paris', tags: ['x', 'y'], score: 9.5, active: true });
await db.set('b', { name: 'Bob', age: 17, city: 'London', tags: ['y'], score: 4.2, active: false });
await db.set('c', { name: 'Eve', age: 25, city: 'Paris', tags: ['z'], score: 7.0 });
await db.set('d', { name: 'Max', age: 40, city: 'Berlin', tags: [], score: 8.1, active: true });
return { dir, db };
}
test('comparison operators $eq $ne $gt $gte $lt $lte', async () => {
const { dir, db } = await seed();
try {
assert.deepEqual(db.query({ filter: { age: { $eq: 25 } } }).map((r) => r.key), ['c']);
assert.deepEqual(db.query({ filter: { age: { $ne: 25 } } }).map((r) => r.key).sort(), ['a', 'b', 'd']);
assert.deepEqual(db.query({ filter: { age: { $gte: 30 } } }).map((r) => r.key).sort(), ['a', 'd']);
assert.deepEqual(db.query({ filter: { age: { $lte: 25 } } }).map((r) => r.key).sort(), ['b', 'c']);
// Combine two operators in one cond object (both must hold).
assert.deepEqual(
db.query({ filter: { age: { $gt: 17, $lt: 30 } } }).map((r) => r.key),
['c'],
);
await db.close();
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
test('$in / $nin membership', async () => {
const { dir, db } = await seed();
try {
assert.deepEqual(db.query({ filter: { city: { $in: ['Paris', 'Berlin'] } } }).map((r) => r.key).sort(), [
'a',
'c',
'd',
]);
assert.deepEqual(db.query({ filter: { city: { $nin: ['Paris'] } } }).map((r) => r.key).sort(), ['b', 'd']);
// Non-array argument never matches.
assert.deepEqual(db.query({ filter: { city: { $in: 'Paris' as unknown as string[] } } }).map((r) => r.key), []);
assert.deepEqual(db.query({ filter: { city: { $nin: 'Paris' as unknown as string[] } } }).map((r) => r.key), []);
await db.close();
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
test('$exists and $type', async () => {
const { dir, db } = await seed();
try {
// 'active' is set on a, b, d but not c.
assert.deepEqual(db.query({ filter: { active: { $exists: true } } }).map((r) => r.key).sort(), ['a', 'b', 'd']);
assert.deepEqual(db.query({ filter: { active: { $exists: false } } }).map((r) => r.key), ['c']);
assert.deepEqual(db.query({ filter: { name: { $type: 'string' } } }).map((r) => r.key).sort(), [
'a',
'b',
'c',
'd',
]);
assert.deepEqual(db.query({ filter: { age: { $type: 'string' } } }).map((r) => r.key), []);
await db.close();
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
test('$regex with string, [pattern, flags], RegExp and stateful global RegExp', async () => {
const { dir, db } = await seed();
try {
assert.deepEqual(db.query({ filter: { name: { $regex: '^A' } } }).map((r) => r.key), ['a']);
// Tuple form supplies flags.
assert.deepEqual(
db.query({ filter: { name: { $regex: ['^a', 'i'] as unknown as string } } }).map((r) => r.key),
['a'],
);
// RegExp instance form.
assert.deepEqual(db.query({ filter: { name: { $regex: /^B/ } } }).map((r) => r.key), ['b']);
// A global RegExp is stateful; the engine must reset lastIndex so every
// document is tested from the start.
const g = /^E/g;
assert.deepEqual(db.query({ filter: { name: { $regex: g } } }).map((r) => r.key), ['c']);
// Non-string field never matches a regex.
assert.deepEqual(db.query({ filter: { age: { $regex: '3' } } }).map((r) => r.key), []);
await db.close();
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
test('scalar RegExp value resets lastIndex between documents', async () => {
const { dir, db } = await seed();
try {
const g = /^A/g;
assert.deepEqual(db.query({ filter: { name: g } }).map((r) => r.key), ['a']);
// Non-string value against a scalar RegExp never matches.
assert.deepEqual(db.query({ filter: { age: /^3/ } }).map((r) => r.key), []);
await db.close();
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
test('logical operators $and $or $nor $not', async () => {
const { dir, db } = await seed();
try {
assert.deepEqual(
db.query({ filter: { $and: [{ city: 'Paris' }, { age: { $gt: 20 } }] } }).map((r) => r.key).sort(),
['a', 'c'],
);
assert.deepEqual(
db.query({ filter: { $or: [{ name: 'Ann' }, { name: 'Bob' }] } }).map((r) => r.key).sort(),
['a', 'b'],
);
// $nor: matches documents that satisfy NONE of the branches.
assert.deepEqual(
db.query({ filter: { $nor: [{ city: 'Paris' }, { city: 'London' }] } }).map((r) => r.key),
['d'],
);
assert.deepEqual(db.query({ filter: { $not: { city: 'Paris' } } }).map((r) => r.key).sort(), ['b', 'd']);
await db.close();
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
test('unknown operator and non-array logical args never match', async () => {
const { dir, db } = await seed();
try {
// Unknown operator falls through to the default case (no match).
assert.deepEqual(db.query({ filter: { age: { $bogus: 1 } as never } }).map((r) => r.key), []);
// $and/$or/$nor with a non-array argument are treated as non-matching.
assert.deepEqual(db.query({ filter: { $and: { city: 'Paris' } as never } }).map((r) => r.key), []);
assert.deepEqual(db.query({ filter: { $or: { city: 'Paris' } as never } }).map((r) => r.key), []);
assert.deepEqual(db.query({ filter: { $nor: { city: 'Paris' } as never } }).map((r) => r.key), []);
await db.close();
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
test('nested dot + bracket paths in filter and projection', async () => {
const dir = await tmpDir();
try {
const db = await MiniDb.open({ dir, valueCodec: 'json' });
await db.set('p1', { user: { name: 'Ann', addr: { zip: '75001' } }, items: [{ sku: 'A' }, { sku: 'B' }] });
await db.set('p2', { user: { name: 'Bob', addr: { zip: '12345' } }, items: [{ sku: 'A' }] });
await db.set('p3', { user: { name: 'Eve' }, items: [] });
assert.deepEqual(db.query({ filter: { 'user.name': 'Bob' } }).map((r) => r.key), ['p2']);
assert.deepEqual(db.query({ filter: { 'user.addr.zip': '75001' } }).map((r) => r.key), ['p1']);
// Bracket index into an array element.
assert.deepEqual(db.query({ filter: { 'items[1].sku': 'B' } }).map((r) => r.key), ['p1']);
// Missing intermediate path does not match.
assert.deepEqual(db.query({ filter: { 'user.addr.zip': 'x' } }).map((r) => r.key).length, 0);
// Nested projection rebuilds the nested shape in the output.
const projected = db.query({ filter: { 'user.name': 'Ann' }, project: ['user.addr.zip', 'items[0].sku'] });
assert.deepEqual(projected[0]!.value, { user: { addr: { zip: '75001' } }, items: [{ sku: 'A' }] });
// Projecting a missing path drops it; empty project list returns the doc.
assert.deepEqual(db.query({ project: [] })[0]!.value, (await db.get('p1')));
await db.close();
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
test('sort ascending/descending, skip and limit', async () => {
const { dir, db } = await seed();
try {
assert.deepEqual(db.query({ sort: { age: 1 } }).map((r) => r.key), ['b', 'c', 'a', 'd']);
assert.deepEqual(db.query({ sort: { age: -1 } }).map((r) => r.key), ['d', 'a', 'c', 'b']);
assert.deepEqual(db.query({ sort: { age: 1 }, skip: 1, limit: 2 }).map((r) => r.key), ['c', 'a']);
// No filter / sort returns every document.
assert.equal(db.query().length, 4);
await db.close();
} finally {
await fs.rm(dir, { recursive: true, force: true });
}
});
|