oak-db/test/testcase/json.ts

698 lines
20 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { TestContext } from "../Context";
import { EntityDict } from "../test-app-domain";
import { v4 } from 'uuid';
import assert from 'assert';
import { generateNewIdAsync } from 'oak-domain/lib/utils/uuid';
import { describe, it, before, after } from 'mocha';
import { DbStore } from "../../lib/types/dbStore";
export default (storeGetter: () => DbStore<EntityDict, TestContext>) => {
it('[1.10]json insert/select', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id = await generateNewIdAsync();
await context.operate('house', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id,
areaId: 'area1',
district: 'district1',
ownerId: 'owner1',
size: 123.4567,
data: {
rooms: 3,
features: ['garden', 'garage'],
address: {
street: '123 Main St',
city: 'Metropolis',
zip: '12345'
}
}
}
}, {});
const result = await context.select('house', {
data: {
id: 1,
// TODO: 在projection中支持json字段的部分展开但是展开后返回值都为字符串
data: 1,
},
filter: {
id,
},
}, {});
assert(result.length === 1, `Expected 1 result, got ${result.length}`);
assert((result[0] as any).data.rooms === 3, `Expected rooms to be 3, got ${(result[0] as any).data.rooms}`);
assert.deepStrictEqual((result[0] as any).data.features, ['garden', 'garage'], `Expected features to match`);
assert.deepStrictEqual((result[0] as any).data.address, {
street: '123 Main St',
city: 'Metropolis',
zip: '12345',
}, `Expected address to match`);
});
it('[1.11]json as filter', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id = await generateNewIdAsync();
await store.operate('oper', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id,
action: 'test',
data: {
name: 'xc',
books: [{
title: 'mathmatics',
price: 1,
}, {
title: 'english',
price: 2,
}],
},
targetEntity: 'bbb',
bornAt: 111,
iState: 'normal',
}
}, context, {});
const row = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
books: [undefined, {
title: 1,
price: 1,
}],
},
},
filter: {
id,
data: {
name: 'xc',
}
}
}, context, {});
const row2 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
books: [undefined, {
title: 1,
price: 1,
}],
},
},
filter: {
id,
data: {
name: 'xc2',
}
}
}, context, {});
await context.commit();
// console.log(JSON.stringify(row));
assert(row.length === 1, JSON.stringify(row));
assert(row2.length === 0, JSON.stringify(row2));
});
it('[1.11.2]json filter on top level', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id = await generateNewIdAsync();
await store.operate('actionAuth', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id,
pathId: await generateNewIdAsync(),
deActions: ['1.12'],
}
}, context, {});
await context.commit();
const row = await store.select('actionAuth', {
data: {
id: 1,
deActions: 1,
},
filter: {
id,
deActions: {
$overlaps: '1.12',
}
}
}, context, {});
const row2 = await store.select('actionAuth', {
data: {
id: 1,
deActions: 1,
},
filter: {
id,
deActions: {
$contains: ['1.13333'],
}
}
}, context, {});
// console.log(JSON.stringify(row));
assert(row.length === 1, JSON.stringify(row));
console.log(JSON.stringify(row));
assert(row2.length === 0, JSON.stringify(row2));
const row3 = await store.select('actionAuth', {
data: {
id: 1,
deActions: 1,
},
filter: {
id,
deActions: {
$exists: true,
}
}
}, context, {});
assert(row3.length === 1);
const row4 = await store.select('actionAuth', {
data: {
id: 1,
deActions: 1,
},
filter: {
id,
deActions: {
$exists: false,
}
}
}, context, {});
assert(row4.length === 0);
});
it('[1.11.3]json escape', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id = await generateNewIdAsync();
await store.operate('oper', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id,
action: 'test',
data: {
$or: [{
name: 'xc',
}, {
name: {
$includes: 'xc',
}
}],
},
targetEntity: 'bbb',
bornAt: 123,
iState: 'normal',
}
}, context, {});
const row = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
id,
data: {
'.$or': {
$contains: {
name: 'xc',
},
},
},
}
}, context, {});
process.env.NODE_ENV = 'development';
const row2 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
id,
data: {
'.$or': [
{
name: 'xc',
},
{
name: {
'.$includes': 'xc',
}
}
],
},
}
}, context, {});
await context.commit();
assert(row.length === 1);
assert(row2.length === 1);
});
it('[1.12]complicated json filter', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id = await generateNewIdAsync();
await store.operate('oper', {
id: await generateNewIdAsync(),
action: 'create',
data: {
id,
action: 'test',
data: {
name: 'xcc',
price: [100, 400, 1000],
},
targetEntity: 'bbb',
bornAt: 123,
iState: 'normal',
}
}, context, {});
const row = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
price: [undefined, 400],
}
}
}, context, {});
const row2 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
price: [undefined, 200],
}
}
}, context, {});
const row3 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
price: [undefined, {
$gt: 300,
}],
}
}
}, context, {});
const row4 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
price: {
$contains: [200, 500],
},
}
}
}, context, {});
const row5 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
price: {
$contains: [100, 400],
},
}
}
}, context, {});
const row6 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
price: {
$contains: ['xc'],
},
}
}
}, context, {});
const row7 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
name: {
$includes: 'xc',
},
price: {
$overlaps: [200, 400, 800],
},
}
}
}, context, {});
/**
* 带$or的查询
*/
process.env.NODE_ENV = 'development';
const row8 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
$or: [
{
name: {
$includes: 'xc',
},
},
{
name: {
$includes: 'xzw',
}
}
],
price: {
$overlaps: [200, 400, 800],
},
}
}
}, context, {});
const row9 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
price: {
$length: 3,
},
}
}
}, context, {});
const row10 = await store.select('oper', {
data: {
id: 1,
data: {
name: 1,
price: 1,
},
},
filter: {
data: {
price: {
$length: {
$gt: 3,
},
},
}
}
}, context, {});
await context.commit();
assert(row.length === 1);
assert(row2.length === 0);
assert(row3.length === 1);
assert(row4.length === 0);
assert(row5.length === 1);
assert(row6.length === 0);
assert(row7.length === 1);
assert(row8.length === 1);
assert(row9.length === 1);
assert(row10.length === 0);
// console.log(JSON.stringify(row7));
});
// ==================== JSON 操作符补充测试 ====================
it('[9.1]json $exists operator', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id1 = v4();
const id2 = v4();
await store.operate('oper', {
id: v4(),
action: 'create',
data: [
{
id: id1,
action: 'test1',
data: { name: 'xc', age: 25 },
targetEntity: 'test',
bornAt: Date.now(),
iState: 'normal',
},
{
id: id2,
action: 'test2',
data: { name: 'zz' }, // 没有 age 字段
targetEntity: 'test',
bornAt: Date.now(),
iState: 'normal',
}
]
}, context, {});
await context.commit();
// 测试 $exists: true - 检查 age 字段存在
const r1 = await context.select('oper', {
data: { id: 1 },
filter: {
id: { $in: [id1, id2] },
data: { age: { $exists: true } }
}
}, {});
assert(r1.length === 1 && r1[0].id === id1, `$exists true failed`);
// 测试 $exists: false - 检查 age 字段不存在
const r2 = await context.select('oper', {
data: { id: 1 },
filter: {
id: { $in: [id1, id2] },
data: { age: { $exists: false } }
}
}, {});
assert(r2.length === 1 && r2[0].id === id2, `$exists false failed`);
// 暂不支持这种写法
// 测试 $exists: 'keyName' - 检查 JSON 对象是否包含指定键
// const r3 = await context.select('oper', {
// data: { id: 1 },
// filter: {
// id: { $in: [id1, id2] },
// data: { $exists: 'age' }
// }
// }, {});
// assert(r3.length === 1 && r3[0].id === id1, `$exists keyName failed`);
});
it('[9.2]json nested $and $or in filter', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id1 = v4();
const id2 = v4();
const id3 = v4();
await store.operate('oper', {
id: v4(),
action: 'create',
data: [
{
id: id1,
action: 'test1',
data: { name: 'alice', role: 'admin', level: 10 },
targetEntity: 'test',
bornAt: Date.now(),
iState: 'normal',
},
{
id: id2,
action: 'test2',
data: { name: 'bob', role: 'user', level: 5 },
targetEntity: 'test',
bornAt: Date.now(),
iState: 'normal',
},
{
id: id3,
action: 'test3',
data: { name: 'charlie', role: 'admin', level: 3 },
targetEntity: 'test',
bornAt: Date.now(),
iState: 'normal',
}
]
}, context, {});
await context.commit();
// 测试: (role = 'admin' AND level > 5) OR (role = 'user')
const r1 = await context.select('oper', {
data: { id: 1 },
filter: {
id: { $in: [id1, id2, id3] },
data: {
$or: [
{
$and: [
{ role: 'admin' },
{ level: { $gt: 5 } }
]
},
{ role: 'user' }
]
}
}
}, {});
assert(r1.length === 2, `Nested $and $or failed, expected 2, got ${r1.length}`);
const ids = r1.map(r => r.id);
assert(ids.includes(id1) && ids.includes(id2), `Expected id1 and id2`);
});
it('[9.3]json array index access in filter', async () => {
const store = storeGetter();
const context = new TestContext(store);
await context.begin();
const id1 = v4();
const id2 = v4();
await store.operate('oper', {
id: v4(),
action: 'create',
data: [
{
id: id1,
action: 'test1',
data: { scores: [90, 85, 95] },
targetEntity: 'test',
bornAt: Date.now(),
iState: 'normal',
},
{
id: id2,
action: 'test2',
data: { scores: [70, 75, 80] },
targetEntity: 'test',
bornAt: Date.now(),
iState: 'normal',
}
]
}, context, {});
await context.commit();
// 测试数组第一个元素 > 80
const r1 = await context.select('oper', {
data: { id: 1 },
filter: {
id: { $in: [id1, id2] },
data: {
scores: [{ $gt: 80 }]
}
}
}, {});
assert(r1.length === 1 && r1[0].id === id1, `Array index filter failed`);
// 测试数组第三个元素 = 95
const r2 = await context.select('oper', {
data: { id: 1 },
filter: {
id: { $in: [id1, id2] },
data: {
scores: [undefined, undefined, 95]
}
}
}, {});
assert(r2.length === 1 && r2[0].id === id1, `Array specific index filter failed`);
});
}