1579 lines
46 KiB
TypeScript
1579 lines
46 KiB
TypeScript
import { v4 } from 'uuid';
|
||
import { describe, it } from 'mocha';
|
||
import { EntityDict, storageSchema } from 'oak-domain/lib/base-app-domain';
|
||
import { generateNewId } from 'oak-domain/lib/utils/uuid';
|
||
import { randomPrefixedString } from 'oak-domain/lib/utils/string';
|
||
import assert from 'assert';
|
||
import TreeStore, { TreeStoreSelectOption } from '../src/store';
|
||
import { FrontendRuntimeContext, FrontendStore } from './Context';
|
||
|
||
describe('基础测试', function () {
|
||
this.timeout(1000000);
|
||
|
||
it('[1.0]简单查询', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
const id1 = generateNewId();
|
||
const id2 = generateNewId();
|
||
const created = store.operate('modiEntity', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: id1,
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
action: 'create',
|
||
data: {},
|
||
}
|
||
}
|
||
}, {
|
||
id: id2,
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'update',
|
||
data: {},
|
||
}
|
||
}
|
||
}]
|
||
}, context, {});
|
||
|
||
// console.log(created);
|
||
|
||
const modiEntities = store.select('modiEntity', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
modi: {
|
||
id: 1,
|
||
targetEntity: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
action: 1,
|
||
data: 1,
|
||
}
|
||
},
|
||
filter: {
|
||
id: {
|
||
$in: [id1, id2],
|
||
},
|
||
},
|
||
sorter: [
|
||
{
|
||
$attr: {
|
||
modi: {
|
||
id: 1,
|
||
}
|
||
},
|
||
$direction: 'asc',
|
||
}
|
||
]
|
||
}, context, {});
|
||
assert(modiEntities.length === 2);
|
||
|
||
const modeEntities2 = store.select('modiEntity', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
modi: {
|
||
id: 1,
|
||
targetEntity: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
action: 1,
|
||
data: 1,
|
||
}
|
||
},
|
||
filter: {
|
||
id: {
|
||
$in: [id1, id2],
|
||
},
|
||
entityId: 'user-id-2',
|
||
},
|
||
}, context, {});
|
||
assert(modeEntities2.length === 1);
|
||
// console.log(modiEntities);
|
||
const modeEntities3 = store.select('modiEntity', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
modi: {
|
||
id: 1,
|
||
targetEntity: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
action: 1,
|
||
data: 1,
|
||
}
|
||
},
|
||
filter: {
|
||
id: {
|
||
$in: [id1, id2],
|
||
},
|
||
$or: [
|
||
{
|
||
entityId: 'user-id-2',
|
||
},
|
||
{
|
||
modi: {
|
||
entityId: 'user-id-1',
|
||
},
|
||
}
|
||
]
|
||
},
|
||
}, context, {});
|
||
assert(modeEntities3.length === 2);
|
||
|
||
const modeEntities4 = store.select('modiEntity', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
modi: {
|
||
id: 1,
|
||
targetEntity: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
action: 1,
|
||
data: 1,
|
||
}
|
||
},
|
||
filter: {
|
||
id: {
|
||
$in: [id1, id2],
|
||
},
|
||
$or: [
|
||
{
|
||
entityId: 'user-id-2',
|
||
},
|
||
{
|
||
modi: {
|
||
entityId: 'user-id-2',
|
||
},
|
||
}
|
||
]
|
||
},
|
||
}, context, {});
|
||
assert(modeEntities4.length === 1);
|
||
|
||
const modeEntities5 = store.select('modiEntity', {
|
||
data: {
|
||
entity: 1,
|
||
},
|
||
filter: {
|
||
id: {
|
||
$in: [id1, id2],
|
||
},
|
||
},
|
||
distinct: true
|
||
}, context, {});
|
||
// console.log(modeEntities5);
|
||
context.commit();
|
||
});
|
||
|
||
it('[1.1]子查询', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const created = store.operate('modiEntity', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'create',
|
||
data: {},
|
||
}
|
||
}
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'update',
|
||
data: {},
|
||
}
|
||
}
|
||
}]
|
||
}, context, {});
|
||
|
||
/**
|
||
* 这个子查询没有跨结点的表达式,所以应该可以提前计算子查询的值
|
||
* 这个可以跟一下store.ts中translateFilter函数里子查询的分支代码
|
||
* by Xc
|
||
*/
|
||
const rows = store.select('modi', {
|
||
data: {
|
||
id: 1,
|
||
targetEntity: 1,
|
||
entity: 1,
|
||
},
|
||
filter: {
|
||
modiEntity$modi: {
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
}
|
||
},
|
||
}, context, {});
|
||
// console.log(rows);
|
||
assert(rows.length === 2);
|
||
context.commit();
|
||
});
|
||
|
||
it('[1.2]行内属性上的表达式', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const id1 = generateNewId();
|
||
const id2 = generateNewId();
|
||
store.operate('modiEntity', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: id1,
|
||
entity: 'user-id-1',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'create',
|
||
data: {},
|
||
}
|
||
}
|
||
}, {
|
||
id: id2,
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'update',
|
||
data: {},
|
||
}
|
||
}
|
||
}]
|
||
}, context, {});
|
||
|
||
const modiEntities = store.select('modiEntity', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
},
|
||
filter: {
|
||
// '#id': 'node-123',
|
||
id: {
|
||
$in: [id1, id2],
|
||
},
|
||
$expr: {
|
||
$ne: [{
|
||
'#attr': 'entity',
|
||
}, {
|
||
"#attr": 'entityId',
|
||
}]
|
||
}
|
||
},
|
||
}, context, {});
|
||
|
||
// console.log(modiEntities);
|
||
assert(modiEntities.length === 1);
|
||
|
||
const modiEntities2 = store.select('modiEntity', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
},
|
||
filter: {
|
||
id: {
|
||
$in: [id1, id2],
|
||
},
|
||
$expr: {
|
||
$eq: [
|
||
{
|
||
$mod: [{
|
||
'#attr': '$$seq$$',
|
||
}, 2]
|
||
},
|
||
0
|
||
],
|
||
},
|
||
},
|
||
}, context, {});
|
||
// memory-store中的$$seq$$是随机生成的,这里只能debug看下对不对,目前看是对的
|
||
context.commit();
|
||
});
|
||
|
||
it('[1.3]跨filter结点的表达式', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const created = store.operate('modiEntity', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'create',
|
||
data: {},
|
||
}
|
||
}
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user3',
|
||
entityId: 'user3-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user2',
|
||
entityId: 'user2-id-1',
|
||
action: 'update',
|
||
data: {},
|
||
}
|
||
}
|
||
}]
|
||
}, context, {});
|
||
|
||
|
||
const applications = store.select('modiEntity', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
entityId: 1,
|
||
},
|
||
filter: {
|
||
$expr: {
|
||
$startsWith: [
|
||
{
|
||
"#refAttr": 'entityId',
|
||
"#refId": 'node-1',
|
||
},
|
||
{
|
||
"#attr": 'entity',
|
||
}
|
||
]
|
||
},
|
||
modi: {
|
||
"#id": 'node-1',
|
||
}
|
||
},
|
||
sorter: [
|
||
{
|
||
$attr: {
|
||
modi: {
|
||
entity: 1,
|
||
}
|
||
},
|
||
$direction: 'asc',
|
||
}
|
||
]
|
||
}, context, {});
|
||
// console.log(applications);
|
||
// assert(applications.length === 1);
|
||
|
||
context.commit();
|
||
});
|
||
|
||
|
||
it('[1.4]跨filter子查询的表达式', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const created = store.operate('modiEntity', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'create',
|
||
data: {},
|
||
}
|
||
}
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user3',
|
||
entityId: 'user3-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user2',
|
||
entityId: 'user2-id-1',
|
||
action: 'update',
|
||
data: {},
|
||
}
|
||
}
|
||
}]
|
||
}, context, {});
|
||
|
||
let modies = store.select('modi', {
|
||
data: {
|
||
id: 1,
|
||
targetEntity: 1,
|
||
},
|
||
filter: {
|
||
"#id": 'node-1',
|
||
modiEntity$modi: {
|
||
$expr: {
|
||
$eq: [
|
||
{
|
||
"#attr": 'entity',
|
||
},
|
||
{
|
||
'#refId': 'node-1',
|
||
"#refAttr": 'entity',
|
||
}
|
||
]
|
||
},
|
||
'#id': 'node-2',
|
||
'#sqp': 'not in',
|
||
}
|
||
},
|
||
sorter: [
|
||
{
|
||
$attr: {
|
||
entity: 1,
|
||
},
|
||
$direction: 'asc',
|
||
}
|
||
]
|
||
}, context, {});
|
||
assert(modies.length === 1);
|
||
// console.log(modies);
|
||
context.commit();
|
||
});
|
||
|
||
it('[1.5]projection中的跨结点表达式', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const created = store.operate('modiEntity', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'create',
|
||
data: {},
|
||
}
|
||
}
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user3',
|
||
entityId: 'user3-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user2',
|
||
entityId: 'user2-id-1',
|
||
action: 'update',
|
||
data: {},
|
||
}
|
||
}
|
||
}]
|
||
}, context, {});
|
||
|
||
let modiEntities = store.select('modiEntity', {
|
||
data: {
|
||
"#id": 'node-1',
|
||
id: 1,
|
||
entity: 1,
|
||
modi: {
|
||
id: 1,
|
||
$expr: {
|
||
$eq: [
|
||
{
|
||
"#attr": 'entity',
|
||
},
|
||
{
|
||
'#refId': 'node-1',
|
||
"#refAttr": 'entity',
|
||
}
|
||
]
|
||
},
|
||
}
|
||
},
|
||
}, context, {});
|
||
// console.log(modiEntities);
|
||
assert(modiEntities.length === 2);
|
||
modiEntities.forEach(
|
||
(me) => {
|
||
assert(me.entity === 'user' && me?.modi?.$expr === true ||
|
||
me.entity === 'user3' && me?.modi?.$expr === false);
|
||
}
|
||
)
|
||
|
||
const modiEntities2 = store.select('modiEntity', {
|
||
data: {
|
||
$expr: {
|
||
$eq: [
|
||
{
|
||
"#attr": 'entity',
|
||
},
|
||
{
|
||
'#refId': 'node-1',
|
||
"#refAttr": 'entity',
|
||
}
|
||
]
|
||
},
|
||
id: 1,
|
||
entity: 1,
|
||
modi: {
|
||
"#id": 'node-1',
|
||
id: 1,
|
||
targetEntity: 1,
|
||
entity: 1,
|
||
}
|
||
},
|
||
}, context, {});
|
||
// console.log(modiEntities2);
|
||
assert(modiEntities2.length === 2);
|
||
modiEntities2.forEach(
|
||
(me) => assert(me.entity === 'user' && me.$expr === true ||
|
||
me.entity === 'user3' && me.$expr === false)
|
||
);
|
||
context.commit();
|
||
});
|
||
|
||
it('[1.6]projection中的一对多跨结点表达式', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const created = store.operate('modiEntity', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'create',
|
||
data: {},
|
||
}
|
||
}
|
||
}]
|
||
}, context, {});
|
||
|
||
const modies = store.select('modi', {
|
||
data: {
|
||
"#id": 'node-1',
|
||
id: 1,
|
||
targetEntity: 1,
|
||
entity: 1,
|
||
modiEntity$modi: {
|
||
$entity: 'modiEntity',
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
// modiId: 1,
|
||
$expr: {
|
||
$eq: [
|
||
{
|
||
"#attr": 'entity',
|
||
},
|
||
{
|
||
'#refId': 'node-1',
|
||
"#refAttr": 'entity',
|
||
}
|
||
]
|
||
},
|
||
$expr2: {
|
||
'#refId': 'node-1',
|
||
"#refAttr": 'id',
|
||
}
|
||
}
|
||
},
|
||
},
|
||
}, context, {});
|
||
// console.log(JSON.stringify(modies));
|
||
assert(modies.length === 1);
|
||
const [modi] = modies;
|
||
const { modiEntity$modi: modiEntities } = modi;
|
||
assert(modiEntities!.length === 1 && modiEntities![0]?.$expr === true && modiEntities![0]?.$expr2 === modi.id);
|
||
context.commit();
|
||
});
|
||
|
||
it('[1.7]事务性测试', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const created = store.operate('modiEntity', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'create',
|
||
data: {},
|
||
}
|
||
}
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user3',
|
||
entityId: 'user3-id-1',
|
||
modi: {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user2',
|
||
entityId: 'user2-id-1',
|
||
action: 'update',
|
||
data: {},
|
||
}
|
||
}
|
||
}]
|
||
}, context, {});
|
||
context.commit();
|
||
|
||
context.begin();
|
||
const modies = store.select('modi', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
modiEntity$modi: {
|
||
$entity: 'modiEntity',
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
modiId: 1,
|
||
}
|
||
},
|
||
},
|
||
}, context, {});
|
||
assert(modies.length === 2 && modies[0].modiEntity$modi!.length === 1);
|
||
|
||
store.operate('modiEntity', {
|
||
id: generateNewId(),
|
||
action: 'remove',
|
||
data: {},
|
||
filter: {
|
||
modiId: modies[0]!.id,
|
||
}
|
||
}, context, {});
|
||
|
||
const me2 = store.select('modiEntity', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
},
|
||
}, context, {});
|
||
assert(me2.length === 1 && !me2.find(ele => !!ele.$$deleteAt$$));
|
||
context.rollback();
|
||
|
||
context.begin();
|
||
|
||
const me3 = store.select('modiEntity', {
|
||
data: {
|
||
id: 1,
|
||
entity: 1,
|
||
},
|
||
}, context, {});
|
||
assert(me3.length === 2 && !me3.find(ele => !!ele.$$deleteAt$$));
|
||
|
||
context.commit();
|
||
});
|
||
|
||
it('[1.8]aggregate', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
store.operate('modi', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'create',
|
||
data: {},
|
||
modiEntity$modi: {
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
}]
|
||
}
|
||
}, {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd2',
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
action: 'create',
|
||
data: {},
|
||
modiEntity$modi: {
|
||
action: 'create',
|
||
data: [
|
||
{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
},
|
||
{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
},
|
||
{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
}
|
||
],
|
||
},
|
||
}],
|
||
}, context, {});
|
||
context.commit();
|
||
|
||
context.begin();
|
||
const result = store.aggregate('modiEntity', {
|
||
data: {
|
||
'#count-1': {
|
||
id: 1,
|
||
},
|
||
'#avg-1': {
|
||
$$createAt$$: 1,
|
||
},
|
||
'#aggr': {
|
||
modi: {
|
||
targetEntity: 1,
|
||
}
|
||
}
|
||
},
|
||
}, context, {});
|
||
// console.log(result);
|
||
|
||
// distinct
|
||
const result2 = store.aggregate('modiEntity', {
|
||
data: {
|
||
'#count-1': {
|
||
entity: 1,
|
||
},
|
||
distinct: true,
|
||
},
|
||
}, context, {});
|
||
|
||
console.log(result2);
|
||
context.commit();
|
||
});
|
||
|
||
it('[1.9]selection+aggregate', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
store.operate('modi', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd',
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
action: 'create',
|
||
data: {},
|
||
modiEntity$modi: {
|
||
action: 'create',
|
||
data: [{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
}, {
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-1',
|
||
}]
|
||
}
|
||
}, {
|
||
id: generateNewId(),
|
||
targetEntity: 'ddd2',
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
action: 'create',
|
||
data: {},
|
||
modiEntity$modi: {
|
||
action: 'create',
|
||
data: [
|
||
{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
},
|
||
{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
},
|
||
{
|
||
id: generateNewId(),
|
||
entity: 'user',
|
||
entityId: 'user-id-2',
|
||
}
|
||
],
|
||
},
|
||
}],
|
||
}, context, {});
|
||
context.commit();
|
||
|
||
context.begin();
|
||
const result = store.select('modi', {
|
||
data: {
|
||
id: 1,
|
||
modiEntity$modi$$aggr: {
|
||
$entity: 'modiEntity',
|
||
data: {
|
||
'#count-1': {
|
||
id: 1,
|
||
},
|
||
'#avg-1': {
|
||
$$createAt$$: 1,
|
||
},
|
||
'#aggr': {
|
||
modi: {
|
||
targetEntity: 1,
|
||
}
|
||
}
|
||
},
|
||
filter: {
|
||
entity: 'user',
|
||
},
|
||
}
|
||
}
|
||
}, context, {});
|
||
console.log(result);
|
||
context.commit();
|
||
});
|
||
|
||
it('[1.10]select json', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
const id = generateNewId();
|
||
store.operate('oper', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id,
|
||
action: 'test',
|
||
data: {
|
||
name: 'xc',
|
||
books: [{
|
||
title: 'mathmatics',
|
||
price: 1,
|
||
}, {
|
||
title: 'english',
|
||
price: 2,
|
||
}]
|
||
},
|
||
targetEntity: 'bbb',
|
||
}
|
||
}, context, {});
|
||
|
||
const row = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
books: [undefined, {
|
||
title: 1,
|
||
price: 1,
|
||
}],
|
||
},
|
||
},
|
||
}, context, {});
|
||
|
||
context.commit();
|
||
console.log(JSON.stringify(row));
|
||
});
|
||
|
||
|
||
it('[1.11]json as filter', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const id = generateNewId();
|
||
store.operate('oper', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id,
|
||
action: 'test',
|
||
data: {
|
||
name: 'xc',
|
||
books: [{
|
||
title: 'mathmatics',
|
||
price: 1,
|
||
}, {
|
||
title: 'english',
|
||
price: 2,
|
||
}]
|
||
},
|
||
targetEntity: 'bbb',
|
||
}
|
||
}, context, {});
|
||
|
||
const row = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
books: [undefined, {
|
||
title: 1,
|
||
price: 1,
|
||
}],
|
||
},
|
||
},
|
||
filter: {
|
||
data: {
|
||
name: 'xc',
|
||
}
|
||
}
|
||
}, context, {});
|
||
const row2 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
books: [undefined, {
|
||
title: 1,
|
||
price: 1,
|
||
}],
|
||
},
|
||
},
|
||
filter: {
|
||
data: {
|
||
name: 'xc2',
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
context.commit();
|
||
// console.log(JSON.stringify(row));
|
||
assert(row.length === 1);
|
||
assert(row2.length === 0);
|
||
});
|
||
|
||
it('[1.12]complicated json filter', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const id = generateNewId();
|
||
store.operate('oper', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id,
|
||
action: 'test',
|
||
data: {
|
||
name: 'xc',
|
||
price: [100, 400, 1000],
|
||
},
|
||
targetEntity: 'bbb',
|
||
}
|
||
}, context, {});
|
||
|
||
const row = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
price: [undefined, 400],
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
const row2 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
price: [undefined, 200],
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
const row3 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
price: [undefined, {
|
||
$gt: 300,
|
||
}],
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
const row4 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
price: {
|
||
$contains: [200, 500],
|
||
},
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
const row5 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
price: {
|
||
$contains: [100, 400],
|
||
},
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
const row6 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
price: {
|
||
$contains: ['xc'],
|
||
},
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
const row7 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
name: {
|
||
$includes: 'xc',
|
||
},
|
||
price: {
|
||
$overlaps: [200, 400, 800],
|
||
},
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
/**
|
||
* 带logic条件查询
|
||
*/
|
||
const row8 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
$or: [
|
||
{
|
||
name: {
|
||
$includes: 'xc',
|
||
}
|
||
},
|
||
{
|
||
name: {
|
||
$includes: 'xzw',
|
||
}
|
||
}
|
||
],
|
||
price: {
|
||
$overlaps: [200, 400, 800],
|
||
},
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
/**
|
||
* object属性的等值查询
|
||
*/
|
||
const row9 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
action: 'test',
|
||
targetEntity: 'bbb',
|
||
data: JSON.stringify({
|
||
name: 'xc',
|
||
price: [100, 400, 1000],
|
||
})
|
||
}
|
||
}, context, {});
|
||
|
||
/**
|
||
* object 的 $exists查询
|
||
*/
|
||
const row10 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
action: 'test',
|
||
targetEntity: 'bbb',
|
||
data: {
|
||
$exists: true,
|
||
}
|
||
}
|
||
}, context, {});
|
||
const row11 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
action: 'test',
|
||
targetEntity: 'bbb',
|
||
data: {
|
||
$exists: false,
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
const row12 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
price: {
|
||
$length: 3,
|
||
},
|
||
}
|
||
}
|
||
}, context, {});
|
||
|
||
const row13 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
data: {
|
||
name: 1,
|
||
price: 1,
|
||
},
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
price: {
|
||
$length: {
|
||
$gt: 3,
|
||
},
|
||
},
|
||
}
|
||
}
|
||
}, context, {});
|
||
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 === 1);
|
||
assert(row11.length === 0);
|
||
assert(row12.length === 1);
|
||
assert(row13.length === 0);
|
||
// console.log(JSON.stringify(row7));
|
||
});
|
||
|
||
it('[1.13]json escapes', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const id = generateNewId();
|
||
store.operate('oper', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id,
|
||
action: 'test',
|
||
data: {
|
||
$or: [{
|
||
name: 'xc',
|
||
}, {
|
||
name: {
|
||
$includes: 'xc',
|
||
}
|
||
}],
|
||
},
|
||
targetEntity: 'bbb',
|
||
}
|
||
}, context, {});
|
||
|
||
const rows1 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
'.$or': {
|
||
$contains: {
|
||
name: 'xc',
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}, context, {});
|
||
|
||
const rows2 = store.select('oper', {
|
||
data: {
|
||
id: 1,
|
||
},
|
||
filter: {
|
||
id,
|
||
data: {
|
||
'.$or': [
|
||
{
|
||
name: 'xc',
|
||
},
|
||
{
|
||
name: {
|
||
'.$includes': 'xc',
|
||
}
|
||
}
|
||
],
|
||
},
|
||
},
|
||
}, context, {});
|
||
|
||
assert(rows1.length === 1);
|
||
assert(rows2.length === 1);
|
||
context.commit();
|
||
});
|
||
});
|
||
|
||
|
||
describe('性能测试', function () {
|
||
this.timeout(80000);
|
||
it('[2.1]子查询性能测试', () => {
|
||
const store = new FrontendStore(storageSchema);
|
||
const context = new FrontendRuntimeContext(store);
|
||
context.begin();
|
||
|
||
const users: EntityDict['user']['CreateSingle']['data'][] = [];
|
||
let iter = 10;
|
||
while (iter--) {
|
||
const id = generateNewId();
|
||
const user: EntityDict['user']['CreateSingle']['data'] = {
|
||
id,
|
||
name: randomPrefixedString('user'),
|
||
nickname: randomPrefixedString('nick'),
|
||
};
|
||
users.push(user);
|
||
// 每人再介绍10个人
|
||
let iter2 = 10;
|
||
while (iter2--) {
|
||
const idd = v4();
|
||
const user: EntityDict['user']['CreateSingle']['data'] = {
|
||
id: idd,
|
||
name: randomPrefixedString('user'),
|
||
nickname: randomPrefixedString('nick'),
|
||
refId: id,
|
||
};
|
||
users.push(user);
|
||
|
||
|
||
// 每人再介绍10个人
|
||
let iter3 = 10;
|
||
while (iter3--) {
|
||
const user: EntityDict['user']['CreateSingle']['data'] = {
|
||
id: v4(),
|
||
name: randomPrefixedString('user'),
|
||
nickname: randomPrefixedString('nick'),
|
||
refId: idd,
|
||
};
|
||
users.push(user);
|
||
}
|
||
}
|
||
}
|
||
|
||
context.operate('user', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: users,
|
||
}, {});
|
||
|
||
/* const relationId = generateNewId();
|
||
context.operate('relation', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: {
|
||
id: relationId,
|
||
name: 'bbbccc',
|
||
},
|
||
}, {});
|
||
|
||
const userRelations: EntityDict['userRelation']['CreateSingle']['data'][] = [];
|
||
iter = 50;
|
||
while (iter --) {
|
||
userRelations.push({
|
||
id: generateNewId(),
|
||
userId: users[iter].id,
|
||
entity: 'modi',
|
||
entityId: '111',
|
||
relationId,
|
||
});
|
||
}
|
||
context.operate('userRelation', {
|
||
id: generateNewId(),
|
||
action: 'create',
|
||
data: userRelations,
|
||
}, {}); */
|
||
|
||
context.commit();
|
||
|
||
/**
|
||
* 构造一个场景,三层子查询
|
||
* 在原算法下(外层每一行去内层匹配)相当于数据库中的三层nestloopjoin,对user表进行遍历达到了2211次(1 + 1110 + 110 * 10)
|
||
* 耗时3s
|
||
*
|
||
* 新算法使用hashjoin,每次将内表建立成hash桶,再进行匹配
|
||
* 耗时20ms
|
||
*/
|
||
|
||
{
|
||
// 新算法
|
||
const start = Date.now();
|
||
context.begin();
|
||
const users2 = store.select<'user', TreeStoreSelectOption>('user', {
|
||
data: {
|
||
id: 1,
|
||
name: 1,
|
||
},
|
||
filter: {
|
||
user$ref: {
|
||
user$ref: {
|
||
name: {
|
||
$exists: true,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}, context, { });
|
||
context.commit();
|
||
const duration = Date.now() - start;
|
||
console.log(users2.length, duration);
|
||
}
|
||
|
||
{
|
||
// 旧算法
|
||
const start = Date.now();
|
||
context.begin();
|
||
const users2 = store.select<'user', TreeStoreSelectOption>('user', {
|
||
data: {
|
||
id: 1,
|
||
name: 1,
|
||
},
|
||
filter: {
|
||
user$ref: {
|
||
user$ref: {
|
||
name: {
|
||
$exists: true,
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}, context, { disableSubQueryHashjoin: true });
|
||
context.commit();
|
||
const duration = Date.now() - start;
|
||
console.log(users2.length, duration);
|
||
}
|
||
});
|
||
}) |