适配了domain带来的类型变化

This commit is contained in:
Xu Chang 2022-04-24 19:50:07 +08:00
parent 7e6ab43b00
commit 19d0036ab7
7 changed files with 195 additions and 152 deletions

3
lib/index.d.ts vendored
View File

@ -1,3 +1,2 @@
import TreeStore from "./store";
import { Context } from './context';
export { TreeStore, Context, };
export { TreeStore, };

View File

@ -3,8 +3,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Context = exports.TreeStore = void 0;
exports.TreeStore = void 0;
const store_1 = __importDefault(require("./store"));
exports.TreeStore = store_1.default;
const context_1 = require("./context");
Object.defineProperty(exports, "Context", { enumerable: true, get: function () { return context_1.Context; } });

60
lib/store.d.ts vendored
View File

@ -1,23 +1,12 @@
import { SelectionResult2, DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperationResult, OperateParams, OpRecord, EntityDict } from "oak-domain/lib/types/Entity";
import { DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperationResult, OperateParams, OpRecord, EntityDict, SelectRowShape } from "oak-domain/lib/types/Entity";
import { CascadeStore } from 'oak-domain/lib/store/CascadeStore';
import { StorageSchema } from 'oak-domain/lib/types/Storage';
import { Context } from "./context";
import { NodeDict, RowNode } from "./types/type";
export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
store: {
[T in keyof ED]?: {
[ID: string]: RowNode;
};
};
immutable: boolean;
activeTxnDict: {
[T: string]: {
nodeHeader?: RowNode;
create: number;
update: number;
remove: number;
};
};
import { Context } from "oak-domain/lib/types/Context";
import { NodeDict } from "./types/type";
export default class TreeStore<ED extends EntityDict, Cxt extends Context<ED>> extends CascadeStore<ED, Cxt> {
private store;
private activeTxnDict;
private stat;
setInitialData(data: {
[T in keyof ED]?: {
[ID: string]: ED[T]['OpSchema'];
@ -28,10 +17,15 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
[ID: string]: ED[T]['OpSchema'];
};
};
constructor(storageSchema: StorageSchema<ED>, immutable?: boolean, initialData?: {
constructor(storageSchema: StorageSchema<ED>, initialData?: {
[T in keyof ED]?: {
[ID: string]: ED[T]['OpSchema'];
};
}, stat?: {
create: number;
update: number;
remove: number;
commit: number;
});
private constructRow;
private translateLogicFilter;
@ -60,17 +54,25 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
* @param context
*/
private addToResultSelections;
protected selectAbjointRow<T extends keyof ED>(entity: T, selection: Omit<ED[T]['Selection'], 'indexFrom' | 'count' | 'data' | 'sorter'>, context: Context<ED>, params?: Object): Promise<Array<ED[T]['OpSchema']>>;
protected updateAbjointRow<T extends keyof ED>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Context<ED>, params?: OperateParams): Promise<void>;
protected selectAbjointRow<T extends keyof ED>(entity: T, selection: Omit<ED[T]['Selection'], 'indexFrom' | 'count' | 'data' | 'sorter'>, context: Cxt, params?: OperateParams): Promise<Array<ED[T]['OpSchema']>>;
protected updateAbjointRow<T extends keyof ED>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, params?: OperateParams): Promise<void>;
private doOperation;
operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Context<ED>, params?: OperateParams): Promise<OperationResult>;
protected formProjection<T extends keyof ED>(entity: T, row: Partial<ED[T]['OpSchema']>, data: ED[T]['Selection']['data'], result: object, nodeDict: NodeDict, context: Context<ED>): Promise<void>;
operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, params?: OperateParams): Promise<OperationResult>;
protected formProjection<T extends keyof ED>(entity: T, row: Partial<ED[T]['OpSchema']>, data: ED[T]['Selection']['data'], result: object, nodeDict: NodeDict, context: Cxt): Promise<void>;
private formResult;
select<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, context: Context<ED>, params?: Object): Promise<SelectionResult2<ED[T]['Schema'], S['data']>>;
count<T extends keyof ED>(entity: T, selection: Omit<ED[T]['Selection'], "action" | "data" | "sorter">, context: Context<ED>, params?: Object): Promise<number>;
select<T extends keyof ED, S extends ED[T]['Selection']>(entity: T, selection: S, context: Cxt, params?: Object): Promise<{
result: SelectRowShape<ED[T]["Schema"], ED[T]["Selection"]["data"]>[];
}>;
count<T extends keyof ED>(entity: T, selection: Omit<ED[T]['Selection'], "action" | "data" | "sorter">, context: Cxt, params?: Object): Promise<number>;
private addToTxnNode;
begin(uuid: string): void;
commit(uuid: string): void;
rollback(uuid: string): void;
sync(opRecords: Array<OpRecord<ED>>, context: Context<ED>): Promise<void>;
getStat(): {
create: number;
update: number;
remove: number;
commit: number;
};
begin(): Promise<string>;
commit(uuid: string): Promise<void>;
rollback(uuid: string): Promise<void>;
sync(opRecords: Array<OpRecord<ED>>, context: Cxt): Promise<void>;
}

View File

@ -12,19 +12,23 @@ const RowStore_1 = require("oak-domain/lib/types/RowStore");
const Demand_2 = require("oak-domain/lib/types/Demand");
const relation_1 = require("oak-domain/lib/store/relation");
const Expression_1 = require("oak-domain/lib/types/Expression");
const uuid_1 = require("uuid");
;
;
function obscurePass(row, attr, params) {
return !!(params.obscure && row[attr] === undefined);
}
class TreeStore extends CascadeStore_1.CascadeStore {
store;
immutable;
activeTxnDict;
stat;
setInitialData(data) {
for (const entity in data) {
if (!this.store[entity]) {
this.store[entity] = {};
}
for (const rowId in data[entity]) {
(0, lodash_1.set)(this.store, `${entity}.${rowId}.#current`, data[entity][rowId]);
(0, lodash_1.set)(this.store, `${entity}.${rowId}.$current`, data[entity][rowId]);
}
}
}
@ -38,18 +42,23 @@ class TreeStore extends CascadeStore_1.CascadeStore {
}
return result;
}
constructor(storageSchema, immutable = false, initialData) {
constructor(storageSchema, initialData, stat) {
super(storageSchema);
this.immutable = immutable;
this.store = {};
if (initialData) {
this.setInitialData(initialData);
}
this.activeTxnDict = {};
this.stat = stat || {
create: 0,
update: 0,
remove: 0,
commit: 0,
};
}
constructRow(node, context) {
let data = (0, lodash_1.cloneDeep)(node.$current);
if (context.uuid && node.$uuid === context.uuid) {
if (context.getCurrentTxnId() && node.$txnId === context.getCurrentTxnId()) {
if (!node.$next) {
return null;
}
@ -102,12 +111,12 @@ class TreeStore extends CascadeStore_1.CascadeStore {
* @param context
* @returns
*/
translateExpressionNode(entity, expression, context) {
translateExpressionNode(entity, expression, context, params) {
if ((0, Expression_1.isExpression)(expression)) {
const op = Object.keys(expression)[0];
const params = expression[op];
if ((0, Expression_1.opMultipleParams)(op)) {
const paramsTranslated = params.map(ele => this.translateExpressionNode(entity, ele, context));
const paramsTranslated = params.map(ele => this.translateExpressionNode(entity, ele, context, params));
return (row, nodeDict) => {
let later = false;
let results = paramsTranslated.map((ele) => {
@ -121,7 +130,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
return ele;
});
if (!later) {
return (0, Expression_1.execOp)(op, results);
return (0, Expression_1.execOp)(op, results, params.obscure);
}
const laterCheckFn = (nodeDict2) => {
results = results.map((ele) => {
@ -134,13 +143,13 @@ class TreeStore extends CascadeStore_1.CascadeStore {
if (results.find(ele => typeof ele === 'function')) {
return laterCheckFn;
}
return (0, Expression_1.execOp)(op, results);
return (0, Expression_1.execOp)(op, results, params.obscure);
};
return laterCheckFn;
};
}
else {
const paramsTranslated = this.translateExpressionNode(entity, params, context);
const paramsTranslated = this.translateExpressionNode(entity, params, context, params);
if (typeof paramsTranslated === 'function') {
return (row, nodeDict) => {
let result = paramsTranslated(row, nodeDict);
@ -154,12 +163,12 @@ class TreeStore extends CascadeStore_1.CascadeStore {
};
return laterCheckFn;
}
return (0, Expression_1.execOp)(op, result);
return (0, Expression_1.execOp)(op, result, params.obscure);
};
}
else {
return () => {
return (0, Expression_1.execOp)(op, paramsTranslated);
return (0, Expression_1.execOp)(op, paramsTranslated, params.obscure);
};
}
}
@ -193,8 +202,8 @@ class TreeStore extends CascadeStore_1.CascadeStore {
return expression;
}
}
translateExpression(entity, expression, context) {
const expr = this.translateExpressionNode(entity, expression, context);
translateExpression(entity, expression, context, params) {
const expr = this.translateExpressionNode(entity, expression, context, params);
return async (row, nodeDict) => {
if (typeof expr !== 'function') {
return expr;
@ -203,7 +212,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
return result;
};
}
translateFulltext(entity, filter, context) {
translateFulltext(entity, filter, context, params) {
// 全文索引查找
const { [entity]: { indexes } } = this.storageSchema;
const fulltextIndex = indexes.find(ele => ele.config && ele.config.type === 'fulltext');
@ -213,7 +222,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
const row = this.constructRow(node, context);
for (const attr of attributes) {
const { name } = attr;
if (row && row[name] && typeof row[name] === 'string' && row[name].contains($search)) {
if (row && row[name] && (typeof row[name] === 'string' && row[name].contains($search) || obscurePass(row, name, params))) {
return true;
}
}
@ -221,63 +230,64 @@ class TreeStore extends CascadeStore_1.CascadeStore {
};
}
async translateAttribute(filter, attr, context, params) {
// 如果是模糊查询且该属性为undefined说明没取到返回true
function obscurePassLocal(row) {
return obscurePass(row, attr, params);
}
if (typeof filter !== 'object') {
return async (node) => {
const row = this.constructRow(node, context);
return row ? row[attr] === filter : false;
return row ? row[attr] === filter || obscurePassLocal(row) : false;
};
}
const fns = [];
for (const op in filter) {
switch (op) {
case '$gt': {
fns.push(async (row) => row[attr] > filter[op]);
fns.push(async (row) => row && (row[attr] > filter[op]) || obscurePassLocal(row));
break;
}
case '$lt': {
fns.push(async (row) => row[attr] < filter[op]);
fns.push(async (row) => row && (row[attr] < filter[op]) || obscurePassLocal(row));
break;
}
case '$gte': {
fns.push(async (row) => row[attr] >= filter[op]);
fns.push(async (row) => row && (row[attr] >= filter[op]) || obscurePassLocal(row));
break;
}
case '$lte': {
fns.push(async (row) => row[attr] <= filter[op]);
fns.push(async (row) => row && (row[attr] <= filter[op]) || obscurePassLocal(row));
break;
}
case '$eq': {
fns.push(async (row) => row[attr] === filter[op]);
fns.push(async (row) => row && (row[attr] === filter[op]) || obscurePassLocal(row));
break;
}
case '$ne': {
fns.push(async (row) => row[attr] !== filter[op]);
fns.push(async (row) => row && (row[attr] !== filter[op]) || obscurePassLocal(row));
break;
}
case '$between': {
fns.push(async (row) => {
return row[attr] >= filter[op][0] && row[attr] <= filter[op][1];
return row && (row[attr] >= filter[op][0] && row[attr] <= filter[op][1] || obscurePassLocal(row));
});
break;
}
case '$startsWith': {
fns.push(async (row) => {
(0, assert_1.default)(typeof row[attr] === 'string');
return row[attr].startsWith(filter[op]);
return row && (row[attr].startsWith(filter[op]) || obscurePassLocal(row));
});
break;
}
case '$endsWith': {
fns.push(async (row) => {
(0, assert_1.default)(typeof row[attr] === 'string');
return row[attr].$endsWith(filter[op]);
return row && (row[attr].$endsWith(filter[op]) || obscurePassLocal(row));
});
break;
}
case '$includes': {
fns.push(async (row) => {
(0, assert_1.default)(typeof row[attr] === 'string');
return row[attr].includes(filter[op]);
return row && (row[attr].includes(filter[op]) || obscurePassLocal(row));
});
break;
}
@ -286,10 +296,10 @@ class TreeStore extends CascadeStore_1.CascadeStore {
(0, assert_1.default)(typeof exists === 'boolean');
fns.push(async (row) => {
if (exists) {
return [null, undefined].includes(row[attr]);
return [null].includes(row[attr]) || obscurePassLocal(row);
}
else {
return ![null, undefined].includes(row[attr]);
return ![null, undefined].includes(row[attr]) || obscurePassLocal(row);
}
});
break;
@ -298,35 +308,41 @@ class TreeStore extends CascadeStore_1.CascadeStore {
const inData = filter[op];
(0, assert_1.default)(typeof inData === 'object');
if (inData instanceof Array) {
fns.push(async (row) => inData.includes(row[attr]));
fns.push(async (row) => inData.includes(row[attr]) || obscurePassLocal(row));
}
else {
// 这里只有当子查询中的filter不包含引用外部的子查询时才可以提前计算否则必须等到执行时再计算
try {
const legalSets = (await this.selectAbjointRow(inData.entity, inData, context, params)).map((ele) => {
const { data } = inData;
const key = Object.keys(data)[0];
return ele[key];
});
fns.push(async (row) => legalSets.includes(row[attr]));
// 如果是obscure则返回的集合中有没有都不能否决“可能有”所以可以直接返回true
if (params.obscure) {
fns.push(async () => true);
}
catch (err) {
if (err instanceof OakError_1.OakError && err.$$code === RowStore_1.RowStore.$$CODES.expressionUnresolved[0]) {
fns.push(async (row, nodeDict) => {
(0, lodash_1.assign)(params, {
nodeDict,
});
const legalSets = (await this.selectAbjointRow(inData.entity, inData, context, params)).map((ele) => {
const { data } = inData;
const key = Object.keys(data)[0];
return ele[key];
});
(0, lodash_1.unset)(params, 'nodeDict');
return legalSets.includes(row[attr]);
else {
// 这里只有当子查询中的filter不包含引用外部的子查询时才可以提前计算否则必须等到执行时再计算
try {
const legalSets = (await this.selectAbjointRow(inData.entity, inData, context, params)).map((ele) => {
const { data } = inData;
const key = Object.keys(data)[0];
return ele[key];
});
fns.push(async (row) => legalSets.includes(row[attr]));
}
else {
throw err;
catch (err) {
if (err instanceof OakError_1.OakError && err.$$code === RowStore_1.RowStore.$$CODES.expressionUnresolved[0]) {
fns.push(async (row, nodeDict) => {
(0, lodash_1.assign)(params, {
nodeDict,
});
const legalSets = (await this.selectAbjointRow(inData.entity, inData, context, params)).map((ele) => {
const { data } = inData;
const key = Object.keys(data)[0];
return ele[key];
});
(0, lodash_1.unset)(params, 'nodeDict');
return legalSets.includes(row[attr]);
});
}
else {
throw err;
}
}
}
}
@ -336,9 +352,10 @@ class TreeStore extends CascadeStore_1.CascadeStore {
const inData = filter[op];
(0, assert_1.default)(typeof inData === 'object');
if (inData instanceof Array) {
fns.push(async (row) => !inData.includes(row[attr]));
fns.push(async (row) => !inData.includes(row[attr]) || obscurePassLocal(row));
}
else {
// obscure对nin没有影响如果返回的子查询结果中包含此行就一定是false否则一定为trueobscure只考虑数据不完整不考虑不准确但若相应属性为undefined则任然可以认为true
// 这里只有当子查询中的filter不包含引用外部的子查询时才可以提前计算否则必须等到执行时再计算
try {
const legalSets = (await this.selectAbjointRow(inData.entity, inData, context, params)).map((ele) => {
@ -346,7 +363,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
const key = Object.keys(data)[0];
return ele[key];
});
fns.push(async (row) => !legalSets.includes(row[attr]));
fns.push(async (row) => !legalSets.includes(row[attr]) || obscurePassLocal(row));
}
catch (err) {
if (err instanceof OakError_1.OakError && err.$$code === RowStore_1.RowStore.$$CODES.expressionUnresolved[0]) {
@ -360,7 +377,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
return ele[key];
});
(0, lodash_1.unset)(params, 'nodeDict');
return !legalSets.includes(row[attr]);
return !legalSets.includes(row[attr]) || obscurePassLocal(row);
});
}
else {
@ -398,7 +415,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
fns.push(this.translateLogicFilter(entity, filter, attr, context, params));
}
else if (attr.toLowerCase().startsWith(Demand_1.EXPRESSION_PREFIX)) {
const fn = this.translateExpression(entity, filter[attr], context);
const fn = this.translateExpression(entity, filter[attr], context, params);
fns.push(async (node, nodeDict, exprResolveFns) => {
const row = this.constructRow(node, context);
if (!row) {
@ -412,7 +429,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
});
}
else if (attr.toLowerCase() === '$text') {
fns.push(this.translateFulltext(entity, filter[attr], context));
fns.push(this.translateFulltext(entity, filter[attr], context, params));
}
else {
// 属性级过滤
@ -426,11 +443,19 @@ class TreeStore extends CascadeStore_1.CascadeStore {
const fn = await this.translateFilter(attr, filter[attr], context, params);
fns.push(async (node, nodeDict, exprResolveFns) => {
const row = this.constructRow(node, context);
if (obscurePass(row, 'entity', params) || obscurePass(row, 'entityId', params)) {
return true;
}
if (row.entity !== attr || row.entityId) {
return false;
}
const node2 = (0, lodash_1.get)(this.store, `${attr}.${row.entityId}`);
(0, assert_1.default)(node2);
if (!node2) {
if (params.obscure) {
return true;
}
return false;
}
return fn(node2, nodeDict, exprResolveFns);
});
}
@ -440,9 +465,17 @@ class TreeStore extends CascadeStore_1.CascadeStore {
const fn = await this.translateFilter(relation, filter[attr], context, params);
fns.push(async (node, nodeDict, exprResolveFns) => {
const row = this.constructRow(node, context);
if (obscurePass(row, `${attr}Id`, params)) {
return true;
}
if (row[`${attr}Id`]) {
const node2 = (0, lodash_1.get)(this.store, `${relation}.${row[`${attr}Id`]}`);
(0, assert_1.default)(node2);
if (!node2) {
if (params.obscure) {
return true;
}
return false;
}
return fn(node2, nodeDict, exprResolveFns);
}
return false;
@ -638,7 +671,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
$$updateAt$$: data.$$updateAt$$ || now,
});
const node2 = {
$uuid: context.uuid,
$txnId: context.getCurrentTxnId(),
$current: null,
$next: data2,
$path: `${entity}.${id}`,
@ -668,8 +701,8 @@ class TreeStore extends CascadeStore_1.CascadeStore {
const ids = rows.map(ele => ele.id);
ids.forEach((id) => {
const node = (this.store[entity])[id];
(0, assert_1.default)(node && (!node.$uuid || node.$uuid === context.uuid));
node.$uuid = context.uuid;
(0, assert_1.default)(node && (!node.$txnId || node.$txnId === context.getCurrentTxnId()));
node.$txnId = context.getCurrentTxnId();
if (action === 'remove') {
node.$next = null;
node.$path = `${entity}.${id}`;
@ -719,7 +752,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
}
async operate(entity, operation, context, params) {
let autoCommit = false;
if (!context.uuid) {
if (!context.getCurrentTxnId()) {
autoCommit = true;
await context.begin();
}
@ -744,7 +777,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
const laterExprDict = {};
for (const attr in data) {
if (attr.startsWith(Demand_1.EXPRESSION_PREFIX)) {
const ExprNodeTranslator = this.translateExpression(entity, data2[attr], context);
const ExprNodeTranslator = this.translateExpression(entity, data2[attr], context, {});
const exprResult = await ExprNodeTranslator(row, nodeDict);
if (typeof exprResult === 'function') {
(0, lodash_1.assign)(laterExprDict, {
@ -842,7 +875,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
async select(entity, selection, context, params) {
let autoCommit = false;
let result;
if (!context.uuid) {
if (!context.getCurrentTxnId()) {
autoCommit = true;
await context.begin();
}
@ -873,7 +906,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
return rows.length;
}
addToTxnNode(node, context, action) {
const txnNode = this.activeTxnDict[context.uuid];
const txnNode = this.activeTxnDict[context.getCurrentTxnId()];
(0, assert_1.default)(txnNode);
(0, assert_1.default)(!node.$nextNode);
if (txnNode.nodeHeader) {
@ -885,7 +918,11 @@ class TreeStore extends CascadeStore_1.CascadeStore {
}
txnNode[action]++;
}
begin(uuid) {
getStat() {
return this.stat;
}
async begin() {
const uuid = (0, uuid_1.v4)({ random: await getRandomValues(16) });
(0, assert_1.default)(!this.activeTxnDict.hasOwnProperty(uuid));
(0, lodash_1.assign)(this.activeTxnDict, {
[uuid]: {
@ -894,17 +931,18 @@ class TreeStore extends CascadeStore_1.CascadeStore {
remove: 0,
},
});
return uuid;
}
commit(uuid) {
async commit(uuid) {
(0, assert_1.default)(this.activeTxnDict.hasOwnProperty(uuid));
let node = this.activeTxnDict[uuid].nodeHeader;
while (node) {
const node2 = node.$nextNode;
(0, assert_1.default)(node.$uuid === uuid);
(0, assert_1.default)(node.$txnId === uuid);
if (node.$next) {
// create/update
node.$current = (0, lodash_1.assign)(node.$current, node.$next);
(0, lodash_1.unset)(node, '$uuid');
(0, lodash_1.unset)(node, '$txnId');
(0, lodash_1.unset)(node, '$next');
(0, lodash_1.unset)(node, '$path');
(0, lodash_1.unset)(node, '$nextNode');
@ -916,17 +954,23 @@ class TreeStore extends CascadeStore_1.CascadeStore {
}
node = node2;
}
if (this.activeTxnDict[uuid].create || this.activeTxnDict[uuid].update || this.activeTxnDict[uuid].remove) {
this.stat.create += this.activeTxnDict[uuid].create;
this.stat.update += this.activeTxnDict[uuid].update;
this.stat.remove += this.activeTxnDict[uuid].remove;
this.stat.commit++;
}
(0, lodash_1.unset)(this.activeTxnDict, uuid);
}
rollback(uuid) {
async rollback(uuid) {
(0, assert_1.default)(this.activeTxnDict.hasOwnProperty(uuid));
let node = this.activeTxnDict[uuid].nodeHeader;
while (node) {
const node2 = node.$nextNode;
(0, assert_1.default)(node.$uuid === uuid);
(0, assert_1.default)(node.$txnId === uuid);
if (node.$current) {
// update/remove
(0, lodash_1.unset)(node, '$uuid');
(0, lodash_1.unset)(node, '$txnId');
(0, lodash_1.unset)(node, '$next');
(0, lodash_1.unset)(node, '$path');
(0, lodash_1.unset)(node, '$nextNode');
@ -943,7 +987,7 @@ class TreeStore extends CascadeStore_1.CascadeStore {
// 将输入的OpRecord同步到数据中
async sync(opRecords, context) {
let autoCommit = false;
if (!context.uuid) {
if (!context.getCurrentTxnId()) {
await context.begin();
autoCommit = true;
}

2
lib/types/type.d.ts vendored
View File

@ -1,7 +1,7 @@
import { NodeId } from "oak-domain/lib/types/Demand";
import { EntityShape } from "oak-domain/src/types/Entity";
export declare type RowNode = {
$uuid?: string;
$txnId?: string;
$next?: Partial<EntityShape> | null;
$current?: EntityShape | null;
$nextNode?: RowNode;

View File

@ -1,6 +1,6 @@
import { assign, cloneDeep, get, keys, last, set, unset } from 'lodash';
import assert from 'assert';
import { SelectionResult2, DeduceCreateSingleOperation, DeduceFilter, DeduceSelection, EntityShape, DeduceRemoveOperation, DeduceUpdateOperation, DeduceSorter, DeduceSorterAttr, OperationResult, OperateParams, OpRecord, DeduceCreateOperationData, DeduceUpdateOperationData, UpdateOpResult, RemoveOpResult, SelectOpResult, EntityDict, SelectRowShape } from "oak-domain/lib/types/Entity";
import { DeduceCreateSingleOperation, DeduceFilter, DeduceSelection, EntityShape, DeduceRemoveOperation, DeduceUpdateOperation, DeduceSorter, DeduceSorterAttr, OperationResult, OperateParams, OpRecord, DeduceCreateOperationData, DeduceUpdateOperationData, UpdateOpResult, RemoveOpResult, SelectOpResult, EntityDict, SelectRowShape } from "oak-domain/lib/types/Entity";
import { ExpressionKey, EXPRESSION_PREFIX, NodeId, RefAttr } from 'oak-domain/lib/types/Demand';
import { CascadeStore } from 'oak-domain/lib/store/CascadeStore';
import { StorageSchema } from 'oak-domain/lib/types/Storage';
@ -26,7 +26,7 @@ function obscurePass(row: any, attr: string, params: OperateParams): boolean {
return !!(params.obscure && row[attr] === undefined);
}
export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
export default class TreeStore<ED extends EntityDict, Cxt extends Context<ED>> extends CascadeStore<ED, Cxt> {
private store: {
[T in keyof ED]?: {
[ID: string]: RowNode;
@ -105,7 +105,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
};
}
private constructRow(node: RowNode, context: Context<ED>) {
private constructRow(node: RowNode, context: Cxt) {
let data = cloneDeep(node.$current);
if (context.getCurrentTxnId() && node.$txnId === context.getCurrentTxnId()) {
if (!node.$next) {
@ -122,7 +122,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
entity: T,
filter: DeduceFilter<ED[T]['Schema']>,
attr: string,
context: Context<ED>,
context: Cxt,
params: OperateParams): (node: RowNode, nodeDict: NodeDict, exprResolveFns: Array<ExprResolveFn>) => Promise<boolean> {
switch (attr) {
case '$and': {
@ -175,7 +175,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
private translateExpressionNode<T extends keyof ED>(
entity: T,
expression: Expression<keyof ED[T]['Schema']> | RefAttr<keyof ED[T]['Schema']> | ExpressionConstant,
context: Context<ED>,
context: Cxt,
params: OperateParams): ExprNodeTranslator | ExpressionConstant {
if (isExpression(expression)) {
@ -286,7 +286,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
private translateExpression<T extends keyof ED>(
entity: T,
expression: Expression<keyof ED[T]['Schema']>,
context: Context<ED>, params: OperateParams): (row: Partial<ED[T]['OpSchema']>, nodeDict: NodeDict) => Promise<ExpressionConstant | ExprLaterCheckFn> {
context: Cxt, params: OperateParams): (row: Partial<ED[T]['OpSchema']>, nodeDict: NodeDict) => Promise<ExpressionConstant | ExprLaterCheckFn> {
const expr = this.translateExpressionNode(entity, expression, context, params);
return async (row, nodeDict) => {
@ -301,7 +301,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
private translateFulltext<T extends keyof ED>(
entity: T,
filter: Q_FullTextValue,
context: Context<ED>,
context: Cxt,
params: OperateParams): (node: RowNode) => Promise<boolean> {
// 全文索引查找
const { [entity]: { indexes } } = this.storageSchema;
@ -326,7 +326,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
private async translateAttribute<T extends keyof ED>(filter: Q_NumberValue | Q_StringValue | Q_BooleanValue | ED[T]['Selection'] & {
entity: T;
}, attr: string, context: Context<ED>, params: OperateParams): Promise<(node: RowNode, nodeDict: NodeDict, exprResolveFns: Array<ExprResolveFn>) => Promise<boolean>> {
}, attr: string, context: Cxt, params: OperateParams): Promise<(node: RowNode, nodeDict: NodeDict, exprResolveFns: Array<ExprResolveFn>) => Promise<boolean>> {
// 如果是模糊查询且该属性为undefined说明没取到返回true
function obscurePassLocal(row: any) {
return obscurePass(row, attr, params);
@ -523,7 +523,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
private async translateFilter<T extends keyof ED>(
entity: T,
filter: DeduceFilter<ED[T]['Schema']>,
context: Context<ED>,
context: Cxt,
params: OperateParams): Promise<(node: RowNode, nodeDict: NodeDict, exprResolveFns: Array<ExprResolveFn>) => Promise<boolean>> {
const fns: Array<(node: RowNode, nodeDict: NodeDict, exprResolveFns: Array<ExprResolveFn>) => Promise<boolean>> = [];
@ -634,7 +634,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
};
}
private translateSorter<T extends keyof ED>(entity: T, sorter: DeduceSorter<ED[T]['Schema']>, context: Context<ED>):
private translateSorter<T extends keyof ED>(entity: T, sorter: DeduceSorter<ED[T]['Schema']>, context: Cxt):
(row1: object | null | undefined, row2: object | null | undefined) => number {
const compare = <T2 extends keyof ED>(
row1: object | null | undefined,
@ -725,7 +725,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
* @param rows
* @param context
*/
private addToResultSelections<T extends keyof ED>(entity: T, rows: Array<ED[T]['OpSchema']>, context: Context<ED>) {
private addToResultSelections<T extends keyof ED>(entity: T, rows: Array<ED[T]['OpSchema']>, context: Cxt) {
const { opRecords } = context;
let lastOperation = last(opRecords);
@ -770,7 +770,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
protected async selectAbjointRow<T extends keyof ED>(
entity: T,
selection: Omit<ED[T]['Selection'], 'indexFrom' | 'count' | 'data' | 'sorter'>,
context: Context<ED>,
context: Cxt,
params: OperateParams = {}): Promise<Array<ED[T]['OpSchema']>> {
const { filter } = selection;
const { nodeDict } = params as {
@ -817,7 +817,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
protected async updateAbjointRow<T extends keyof ED>(
entity: T,
operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>,
context: Context<ED>,
context: Cxt,
params?: OperateParams): Promise<void> {
const { data, action } = operation;
@ -905,7 +905,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
}
}
private async doOperation<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Context<ED>, params?: OperateParams): Promise<OperationResult> {
private async doOperation<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, params?: OperateParams): Promise<OperationResult> {
const { action } = operation;
if (action === 'select') {
const rows = await this.cascadeSelect(entity, operation as any, context, params);
@ -922,7 +922,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
}
}
async operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Context<ED>, params?: OperateParams): Promise<OperationResult> {
async operate<T extends keyof ED>(entity: T, operation: ED[T]['Operation'], context: Cxt, params?: OperateParams): Promise<OperationResult> {
let autoCommit = false;
if (!context.getCurrentTxnId()) {
autoCommit = true;
@ -949,7 +949,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
data: ED[T]['Selection']['data'],
result: object,
nodeDict: NodeDict,
context: Context<ED>) {
context: Cxt) {
const row2 = row as any;
const data2 = data as any;
@ -1035,7 +1035,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
entity: T,
rows: Array<Partial<ED[T]['Schema']>>,
selection: Omit<S, 'filter'>,
context: Context<ED>,
context: Cxt,
nodeDict?: NodeDict) {
const { data, sorter, indexFrom, count } = selection;
// 先计算projection
@ -1068,8 +1068,8 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
async select<T extends keyof ED, S extends ED[T]['Selection']>(
entity: T,
selection: S,
context: Context<ED>,
params?: Object): Promise<SelectionResult2<ED[T]['Schema'], S['data']>> {
context: Cxt,
params?: Object) {
let autoCommit = false;
let result;
if (!context.getCurrentTxnId()) {
@ -1095,7 +1095,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
};
}
async count<T extends keyof ED>(entity: T, selection: Omit<ED[T]['Selection'], "action" | "data" | "sorter">, context: Context<ED>, params?: Object): Promise<number> {
async count<T extends keyof ED>(entity: T, selection: Omit<ED[T]['Selection'], "action" | "data" | "sorter">, context: Cxt, params?: Object): Promise<number> {
const rows = await this.cascadeSelect(entity, assign({}, selection, {
data: {
id: 1,
@ -1105,7 +1105,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
return rows.length;
}
private addToTxnNode(node: RowNode, context: Context<ED>, action: 'create' | 'update' | 'remove') {
private addToTxnNode(node: RowNode, context: Cxt, action: 'create' | 'update' | 'remove') {
const txnNode = this.activeTxnDict[context.getCurrentTxnId()!];
assert(txnNode);
assert(!node.$nextNode);
@ -1191,7 +1191,7 @@ export default class TreeStore<ED extends EntityDict> extends CascadeStore<ED> {
}
// 将输入的OpRecord同步到数据中
async sync(opRecords: Array<OpRecord<ED>>, context: Context<ED>) {
async sync(opRecords: Array<OpRecord<ED>>, context: Cxt) {
let autoCommit = false;
if (!context.getCurrentTxnId()) {
await context.begin();

View File

@ -2,17 +2,17 @@ import { v4 } from 'uuid';
import { describe, it } from 'mocha';
import TreeStore from '../src/store';
import { EntityDict } from './app-domain/EntityDict';
import { Context } from '../src/context';
import { storageSchema } from './app-domain/Storage';
import assert from 'assert';
import { CreateSingleOperation } from './app-domain/System/Schema';
import { UniversalContext } from 'oak-domain/lib/store/UniversalContext';
describe('基础测试', function () {
this.timeout(1000000);
it('[1.0]简单查询', async () => {
const store = new TreeStore<EntityDict>(storageSchema);
const context = new Context(store);
const store = new TreeStore<EntityDict, UniversalContext<EntityDict>>(storageSchema);
const context = new UniversalContext(store);
const created = await store.operate('application', {
action: 'create',
data: [{
@ -73,8 +73,8 @@ describe('基础测试', function () {
});
it('[1.1]子查询', async () => {
const store = new TreeStore<EntityDict>(storageSchema);
const context = new Context(store);
const store = new TreeStore<EntityDict, UniversalContext<EntityDict>>(storageSchema);
const context = new UniversalContext(store);
await store.operate('user', {
action: 'create',
@ -115,8 +115,8 @@ describe('基础测试', function () {
});
it('[1.2]行内属性上的表达式', async () => {
const store = new TreeStore<EntityDict>(storageSchema);
const context = new Context(store);
const store = new TreeStore<EntityDict, UniversalContext<EntityDict>>(storageSchema);
const context = new UniversalContext(store);
await store.operate('user', {
action: 'create',
@ -149,8 +149,8 @@ describe('基础测试', function () {
});
it('[1.3]跨filter结点的表达式', async () => {
const store = new TreeStore<EntityDict>(storageSchema);
const context = new Context(store);
const store = new TreeStore<EntityDict, UniversalContext<EntityDict>>(storageSchema);
const context = new UniversalContext(store);
await store.operate('application', {
action: 'create',
@ -227,8 +227,8 @@ describe('基础测试', function () {
it('[1.4]跨filter子查询的表达式', async () => {
const store = new TreeStore<EntityDict>(storageSchema);
const context = new Context(store);
const store = new TreeStore<EntityDict, UniversalContext<EntityDict>>(storageSchema);
const context = new UniversalContext(store);
await store.operate('application', {
action: 'create',
@ -345,8 +345,8 @@ describe('基础测试', function () {
});
it('[1.5]projection中的跨结点表达式', async () => {
const store = new TreeStore<EntityDict>(storageSchema);
const context = new Context(store);
const store = new TreeStore<EntityDict, UniversalContext<EntityDict>>(storageSchema);
const context = new UniversalContext(store);
await store.operate('application', {
action: 'create',
@ -445,8 +445,8 @@ describe('基础测试', function () {
});
it('[1.6]projection中的一对多跨结点表达式', async () => {
const store = new TreeStore<EntityDict>(storageSchema);
const context = new Context(store);
const store = new TreeStore<EntityDict, UniversalContext<EntityDict>>(storageSchema);
const context = new UniversalContext(store);
await store.operate('system', {
action: 'create',
@ -518,8 +518,8 @@ describe('基础测试', function () {
});
it('[1.7]事务性测试', async () => {
const store = new TreeStore<EntityDict>(storageSchema);
const context = new Context(store);
const store = new TreeStore<EntityDict, UniversalContext<EntityDict>>(storageSchema);
const context = new UniversalContext(store);
await store.operate('system', {
action: 'create',