重新编写了测试代码,增加了aggregate的实现
This commit is contained in:
parent
7a6d66fb61
commit
4ab09a9bde
|
|
@ -1,4 +1,4 @@
|
||||||
import { DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperationResult, OperateOption, OpRecord, EntityDict, SelectOption } from "oak-domain/lib/types/Entity";
|
import { DeduceCreateSingleOperation, DeduceRemoveOperation, DeduceUpdateOperation, OperationResult, OperateOption, OpRecord, EntityDict, SelectOption, AggregationResult } from "oak-domain/lib/types/Entity";
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { StorageSchema } from 'oak-domain/lib/types/Storage';
|
import { StorageSchema } from 'oak-domain/lib/types/Storage';
|
||||||
import { NodeDict } from "./types/type";
|
import { NodeDict } from "./types/type";
|
||||||
|
|
@ -53,8 +53,8 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
||||||
protected updateAbjointRow<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends Context>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, option?: OP): number;
|
protected updateAbjointRow<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends Context>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, option?: OP): number;
|
||||||
protected selectAbjointRowAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends Context>(entity: T, selection: ED[T]['Selection'], context: Cxt, option?: OP): Promise<Partial<ED[T]["Schema"]>[]>;
|
protected selectAbjointRowAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends Context>(entity: T, selection: ED[T]['Selection'], context: Cxt, option?: OP): Promise<Partial<ED[T]["Schema"]>[]>;
|
||||||
protected updateAbjointRowAsync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends Context>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, option?: OP): Promise<number>;
|
protected updateAbjointRowAsync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends Context>(entity: T, operation: DeduceCreateSingleOperation<ED[T]['Schema']> | DeduceUpdateOperation<ED[T]['Schema']> | DeduceRemoveOperation<ED[T]['Schema']>, context: Cxt, option?: OP): Promise<number>;
|
||||||
operateSync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends SyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): OperationResult<ED>;
|
protected operateSync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends SyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): OperationResult<ED>;
|
||||||
operateAsync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends AsyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<OperationResult<ED>>;
|
protected operateAsync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends AsyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): Promise<OperationResult<ED>>;
|
||||||
/**
|
/**
|
||||||
* 计算最终结果集当中的函数,这个函数可能测试不够充分
|
* 计算最终结果集当中的函数,这个函数可能测试不够充分
|
||||||
* @param entity
|
* @param entity
|
||||||
|
|
@ -63,12 +63,23 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
||||||
* @param nodeDict
|
* @param nodeDict
|
||||||
* @param context
|
* @param context
|
||||||
*/
|
*/
|
||||||
protected formExprInResult<T extends keyof ED, Cxt extends Context>(entity: T, projection: ED[T]['Selection']['data'], data: Partial<ED[T]['Schema']>, nodeDict: NodeDict, context: Cxt): void;
|
private formExprInResult;
|
||||||
private formResult;
|
private formResult;
|
||||||
selectSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Partial<ED[T]['Schema']>[];
|
/**
|
||||||
selectAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Promise<Partial<ED[T]["Schema"]>[]>;
|
* 本函数把结果中的相应属性映射成一个字符串,用于GroupBy
|
||||||
countSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): number;
|
* @param entity
|
||||||
countAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): Promise<number>;
|
* @param row
|
||||||
|
* @param projection
|
||||||
|
*/
|
||||||
|
private mappingProjectionOnRow;
|
||||||
|
private calcAggregation;
|
||||||
|
private formAggregation;
|
||||||
|
protected selectSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Partial<ED[T]['Schema']>[];
|
||||||
|
protected selectAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: ED[T]['Selection'], context: Cxt, option: OP): Promise<Partial<ED[T]["Schema"]>[]>;
|
||||||
|
protected aggregateSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): AggregationResult<ED[T]['Schema']>;
|
||||||
|
protected aggregateAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(entity: T, aggregation: ED[T]['Aggregation'], context: Cxt, option: OP): Promise<AggregationResult<ED[T]['Schema']>>;
|
||||||
|
protected countSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): number;
|
||||||
|
protected countAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(entity: T, selection: Pick<ED[T]['Selection'], 'filter' | 'count'>, context: Cxt, option: OP): Promise<number>;
|
||||||
private addToTxnNode;
|
private addToTxnNode;
|
||||||
getStat(): {
|
getStat(): {
|
||||||
create: number;
|
create: number;
|
||||||
|
|
|
||||||
253
lib/store.js
253
lib/store.js
|
|
@ -5,6 +5,7 @@ var lodash_1 = require("oak-domain/lib/utils/lodash");
|
||||||
var assert_1 = require("oak-domain/lib/utils/assert");
|
var assert_1 = require("oak-domain/lib/utils/assert");
|
||||||
var Entity_1 = require("oak-domain/lib/types/Entity");
|
var Entity_1 = require("oak-domain/lib/types/Entity");
|
||||||
var Demand_1 = require("oak-domain/lib/types/Demand");
|
var Demand_1 = require("oak-domain/lib/types/Demand");
|
||||||
|
var selection_1 = require("oak-domain/lib/store/selection");
|
||||||
var Exception_1 = require("oak-domain/lib/types/Exception");
|
var Exception_1 = require("oak-domain/lib/types/Exception");
|
||||||
var Demand_2 = require("oak-domain/lib/types/Demand");
|
var Demand_2 = require("oak-domain/lib/types/Demand");
|
||||||
var relation_1 = require("oak-domain/lib/store/relation");
|
var relation_1 = require("oak-domain/lib/store/relation");
|
||||||
|
|
@ -1118,6 +1119,176 @@ var TreeStore = /** @class */ (function (_super) {
|
||||||
return rows2;
|
return rows2;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
/**
|
||||||
|
* 本函数把结果中的相应属性映射成一个字符串,用于GroupBy
|
||||||
|
* @param entity
|
||||||
|
* @param row
|
||||||
|
* @param projection
|
||||||
|
*/
|
||||||
|
TreeStore.prototype.mappingProjectionOnRow = function (entity, row, projection) {
|
||||||
|
var _this = this;
|
||||||
|
var key = '';
|
||||||
|
var result = {};
|
||||||
|
var values = [];
|
||||||
|
var mappingIter = function (entity2, row2, p2, result2) {
|
||||||
|
var e_12, _a;
|
||||||
|
var keys = Object.keys(p2).sort(function (ele1, ele2) { return ele1 < ele2 ? -1 : 1; });
|
||||||
|
try {
|
||||||
|
for (var keys_1 = tslib_1.__values(keys), keys_1_1 = keys_1.next(); !keys_1_1.done; keys_1_1 = keys_1.next()) {
|
||||||
|
var k = keys_1_1.value;
|
||||||
|
var rel = _this.judgeRelation(entity2, k);
|
||||||
|
if (rel === 2) {
|
||||||
|
result2[k] = {};
|
||||||
|
if (row2[k]) {
|
||||||
|
mappingIter(k, row2[k], p2[k], result2[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof rel === 'string') {
|
||||||
|
result2[k] = {};
|
||||||
|
if (row2[k]) {
|
||||||
|
mappingIter(rel, row2[k], p2[k], result2[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(0, assert_1.assert)([0, 1].includes(rel));
|
||||||
|
result2[k] = row2[k];
|
||||||
|
(0, assert_1.assert)(['string', 'number', 'boolean'].includes(typeof row2[k]));
|
||||||
|
key += "".concat(row2[k]);
|
||||||
|
values.push(row2[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_12_1) { e_12 = { error: e_12_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (keys_1_1 && !keys_1_1.done && (_a = keys_1.return)) _a.call(keys_1);
|
||||||
|
}
|
||||||
|
finally { if (e_12) throw e_12.error; }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mappingIter(entity, row, projection, result);
|
||||||
|
return {
|
||||||
|
result: result,
|
||||||
|
key: key,
|
||||||
|
values: values,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
TreeStore.prototype.calcAggregation = function (entity, rows, aggregationData) {
|
||||||
|
var e_13, _a, e_14, _b, e_15, _c;
|
||||||
|
var ops = Object.keys(aggregationData).filter(function (ele) { return ele !== '$aggr'; });
|
||||||
|
var result = {};
|
||||||
|
try {
|
||||||
|
for (var rows_2 = tslib_1.__values(rows), rows_2_1 = rows_2.next(); !rows_2_1.done; rows_2_1 = rows_2.next()) {
|
||||||
|
var row = rows_2_1.value;
|
||||||
|
try {
|
||||||
|
for (var ops_1 = (e_14 = void 0, tslib_1.__values(ops)), ops_1_1 = ops_1.next(); !ops_1_1.done; ops_1_1 = ops_1.next()) {
|
||||||
|
var op = ops_1_1.value;
|
||||||
|
var values = this.mappingProjectionOnRow(entity, row, aggregationData[op]).values;
|
||||||
|
(0, assert_1.assert)(values.length === 1, "\u805A\u5408\u8FD0\u7B97\u4E2D\uFF0C".concat(op, "\u7684\u76EE\u6807\u5C5E\u6027\u591A\u4E8E1\u4E2A"));
|
||||||
|
if (op.startsWith('$max')) {
|
||||||
|
if (![undefined, null].includes(values[0]) && (!result.hasOwnProperty(op) || result[op] < values[0])) {
|
||||||
|
result[op] = values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op.startsWith('$min')) {
|
||||||
|
if (![undefined, null].includes(values[0]) && (!result.hasOwnProperty(op) || result[op] > values[0])) {
|
||||||
|
result[op] = values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op.startsWith('$sum')) {
|
||||||
|
if (![undefined, null].includes(values[0])) {
|
||||||
|
(0, assert_1.assert)(typeof values[0] === 'number', '只有number类型的属性才可以计算sum');
|
||||||
|
if (!result.hasOwnProperty(op)) {
|
||||||
|
result[op] = values[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result[op] += values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op.startsWith('$count')) {
|
||||||
|
if (![undefined, null].includes(values[0])) {
|
||||||
|
if (!result.hasOwnProperty(op)) {
|
||||||
|
result[op] = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result[op] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
(0, assert_1.assert)(op.startsWith('$avg'));
|
||||||
|
if (![undefined, null].includes(values[0])) {
|
||||||
|
(0, assert_1.assert)(typeof values[0] === 'number', '只有number类型的属性才可以计算avg');
|
||||||
|
if (!result.hasOwnProperty(op)) {
|
||||||
|
result[op] = {
|
||||||
|
total: values[0],
|
||||||
|
count: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result[op].total += values[0];
|
||||||
|
result[op].count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_14_1) { e_14 = { error: e_14_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (ops_1_1 && !ops_1_1.done && (_b = ops_1.return)) _b.call(ops_1);
|
||||||
|
}
|
||||||
|
finally { if (e_14) throw e_14.error; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_13_1) { e_13 = { error: e_13_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (rows_2_1 && !rows_2_1.done && (_a = rows_2.return)) _a.call(rows_2);
|
||||||
|
}
|
||||||
|
finally { if (e_13) throw e_13.error; }
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
for (var ops_2 = tslib_1.__values(ops), ops_2_1 = ops_2.next(); !ops_2_1.done; ops_2_1 = ops_2.next()) {
|
||||||
|
var op = ops_2_1.value;
|
||||||
|
if (!result[op]) {
|
||||||
|
result[op] = null;
|
||||||
|
}
|
||||||
|
else if (op.startsWith('$avg')) {
|
||||||
|
result[op] = result[op].total / result[op].count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e_15_1) { e_15 = { error: e_15_1 }; }
|
||||||
|
finally {
|
||||||
|
try {
|
||||||
|
if (ops_2_1 && !ops_2_1.done && (_c = ops_2.return)) _c.call(ops_2);
|
||||||
|
}
|
||||||
|
finally { if (e_15) throw e_15.error; }
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
TreeStore.prototype.formAggregation = function (entity, rows, aggregationData) {
|
||||||
|
var _this = this;
|
||||||
|
var $aggr = aggregationData.$aggr;
|
||||||
|
if ($aggr) {
|
||||||
|
var groups_1 = (0, lodash_1.groupBy)(rows, function (row) {
|
||||||
|
var key = _this.mappingProjectionOnRow(entity, row, $aggr).key;
|
||||||
|
return key;
|
||||||
|
});
|
||||||
|
var result = Object.keys(groups_1).map(function (ele) {
|
||||||
|
var aggr = _this.calcAggregation(entity, groups_1[ele], aggregationData);
|
||||||
|
var r = _this.mappingProjectionOnRow(entity, groups_1[ele][0], $aggr).result;
|
||||||
|
aggr.data = r;
|
||||||
|
return aggr;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
var aggr = this.calcAggregation(entity, rows, aggregationData);
|
||||||
|
return [aggr];
|
||||||
|
};
|
||||||
TreeStore.prototype.selectSync = function (entity, selection, context, option) {
|
TreeStore.prototype.selectSync = function (entity, selection, context, option) {
|
||||||
var _this = this;
|
var _this = this;
|
||||||
(0, assert_1.assert)(context.getCurrentTxnId());
|
(0, assert_1.assert)(context.getCurrentTxnId());
|
||||||
|
|
@ -1144,6 +1315,64 @@ var TreeStore = /** @class */ (function (_super) {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
TreeStore.prototype.aggregateSync = function (entity, aggregation, context, option) {
|
||||||
|
var _this = this;
|
||||||
|
(0, assert_1.assert)(context.getCurrentTxnId());
|
||||||
|
var data = aggregation.data, filter = aggregation.filter, sorter = aggregation.sorter, indexFrom = aggregation.indexFrom, count = aggregation.count;
|
||||||
|
var p = {};
|
||||||
|
for (var k in data) {
|
||||||
|
Object.assign(p, (0, lodash_1.cloneDeep)(data[k]));
|
||||||
|
}
|
||||||
|
var selection = {
|
||||||
|
data: p,
|
||||||
|
filter: filter,
|
||||||
|
sorter: sorter,
|
||||||
|
indexFrom: indexFrom,
|
||||||
|
count: count,
|
||||||
|
};
|
||||||
|
(0, selection_1.reinforceSelection)(this.storageSchema, entity, selection);
|
||||||
|
var result = this.cascadeSelect(entity, selection, context, Object.assign({}, option, {
|
||||||
|
dontCollect: true,
|
||||||
|
}));
|
||||||
|
// 在这里再计算所有的表达式
|
||||||
|
result.forEach(function (ele) { return _this.formExprInResult(entity, selection.data, ele, {}, context); });
|
||||||
|
// 最后计算Aggregation
|
||||||
|
return this.formAggregation(entity, result, aggregation.data);
|
||||||
|
};
|
||||||
|
TreeStore.prototype.aggregateAsync = function (entity, aggregation, context, option) {
|
||||||
|
return tslib_1.__awaiter(this, void 0, void 0, function () {
|
||||||
|
var data, filter, sorter, indexFrom, count, p, k, selection, result;
|
||||||
|
var _this = this;
|
||||||
|
return tslib_1.__generator(this, function (_a) {
|
||||||
|
switch (_a.label) {
|
||||||
|
case 0:
|
||||||
|
(0, assert_1.assert)(context.getCurrentTxnId());
|
||||||
|
data = aggregation.data, filter = aggregation.filter, sorter = aggregation.sorter, indexFrom = aggregation.indexFrom, count = aggregation.count;
|
||||||
|
p = {};
|
||||||
|
for (k in data) {
|
||||||
|
Object.assign(p, (0, lodash_1.cloneDeep)(data[k]));
|
||||||
|
}
|
||||||
|
selection = {
|
||||||
|
data: p,
|
||||||
|
filter: filter,
|
||||||
|
sorter: sorter,
|
||||||
|
indexFrom: indexFrom,
|
||||||
|
count: count,
|
||||||
|
};
|
||||||
|
(0, selection_1.reinforceSelection)(this.storageSchema, entity, selection);
|
||||||
|
return [4 /*yield*/, this.cascadeSelectAsync(entity, selection, context, Object.assign({}, option, {
|
||||||
|
dontCollect: true,
|
||||||
|
}))];
|
||||||
|
case 1:
|
||||||
|
result = _a.sent();
|
||||||
|
// 在这里再计算所有的表达式
|
||||||
|
result.forEach(function (ele) { return _this.formExprInResult(entity, selection.data, ele, {}, context); });
|
||||||
|
// 最后计算Aggregation
|
||||||
|
return [2 /*return*/, this.formAggregation(entity, result, aggregation.data)];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
TreeStore.prototype.countSync = function (entity, selection, context, option) {
|
TreeStore.prototype.countSync = function (entity, selection, context, option) {
|
||||||
var result = this.selectSync(entity, Object.assign({}, selection, {
|
var result = this.selectSync(entity, Object.assign({}, selection, {
|
||||||
data: {
|
data: {
|
||||||
|
|
@ -1206,7 +1435,7 @@ var TreeStore = /** @class */ (function (_super) {
|
||||||
return uuid;
|
return uuid;
|
||||||
};
|
};
|
||||||
TreeStore.prototype.commitSync = function (uuid) {
|
TreeStore.prototype.commitSync = function (uuid) {
|
||||||
var e_12, _a;
|
var e_16, _a;
|
||||||
(0, assert_1.assert)(this.activeTxnDict.hasOwnProperty(uuid), uuid);
|
(0, assert_1.assert)(this.activeTxnDict.hasOwnProperty(uuid), uuid);
|
||||||
var node = this.activeTxnDict[uuid].nodeHeader;
|
var node = this.activeTxnDict[uuid].nodeHeader;
|
||||||
while (node) {
|
while (node) {
|
||||||
|
|
@ -1246,17 +1475,17 @@ var TreeStore = /** @class */ (function (_super) {
|
||||||
waiter.fn();
|
waiter.fn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e_12_1) { e_12 = { error: e_12_1 }; }
|
catch (e_16_1) { e_16 = { error: e_16_1 }; }
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||||
}
|
}
|
||||||
finally { if (e_12) throw e_12.error; }
|
finally { if (e_16) throw e_16.error; }
|
||||||
}
|
}
|
||||||
(0, lodash_1.unset)(this.activeTxnDict, uuid);
|
(0, lodash_1.unset)(this.activeTxnDict, uuid);
|
||||||
};
|
};
|
||||||
TreeStore.prototype.rollbackSync = function (uuid) {
|
TreeStore.prototype.rollbackSync = function (uuid) {
|
||||||
var e_13, _a;
|
var e_17, _a;
|
||||||
(0, assert_1.assert)(this.activeTxnDict.hasOwnProperty(uuid));
|
(0, assert_1.assert)(this.activeTxnDict.hasOwnProperty(uuid));
|
||||||
var node = this.activeTxnDict[uuid].nodeHeader;
|
var node = this.activeTxnDict[uuid].nodeHeader;
|
||||||
while (node) {
|
while (node) {
|
||||||
|
|
@ -1289,12 +1518,12 @@ var TreeStore = /** @class */ (function (_super) {
|
||||||
waiter.fn();
|
waiter.fn();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e_13_1) { e_13 = { error: e_13_1 }; }
|
catch (e_17_1) { e_17 = { error: e_17_1 }; }
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
if (_c && !_c.done && (_a = _b.return)) _a.call(_b);
|
||||||
}
|
}
|
||||||
finally { if (e_13) throw e_13.error; }
|
finally { if (e_17) throw e_17.error; }
|
||||||
}
|
}
|
||||||
(0, lodash_1.unset)(this.activeTxnDict, uuid);
|
(0, lodash_1.unset)(this.activeTxnDict, uuid);
|
||||||
};
|
};
|
||||||
|
|
@ -1321,7 +1550,7 @@ var TreeStore = /** @class */ (function (_super) {
|
||||||
};
|
};
|
||||||
// 将输入的OpRecord同步到数据中
|
// 将输入的OpRecord同步到数据中
|
||||||
TreeStore.prototype.sync = function (opRecords, context, option) {
|
TreeStore.prototype.sync = function (opRecords, context, option) {
|
||||||
var e_14, _a, e_15, _b;
|
var e_18, _a, e_19, _b;
|
||||||
var option2 = Object.assign({}, option, {
|
var option2 = Object.assign({}, option, {
|
||||||
dontCollect: true,
|
dontCollect: true,
|
||||||
dontCreateOper: true,
|
dontCreateOper: true,
|
||||||
|
|
@ -1334,7 +1563,7 @@ var TreeStore = /** @class */ (function (_super) {
|
||||||
var e = record.e, d = record.d;
|
var e = record.e, d = record.d;
|
||||||
if (d instanceof Array) {
|
if (d instanceof Array) {
|
||||||
try {
|
try {
|
||||||
for (var d_1 = (e_15 = void 0, tslib_1.__values(d)), d_1_1 = d_1.next(); !d_1_1.done; d_1_1 = d_1.next()) {
|
for (var d_1 = (e_19 = void 0, tslib_1.__values(d)), d_1_1 = d_1.next(); !d_1_1.done; d_1_1 = d_1.next()) {
|
||||||
var dd = d_1_1.value;
|
var dd = d_1_1.value;
|
||||||
if (this.store[e] && this.store[e][dd.id]) {
|
if (this.store[e] && this.store[e][dd.id]) {
|
||||||
this.updateAbjointRow(e, {
|
this.updateAbjointRow(e, {
|
||||||
|
|
@ -1355,12 +1584,12 @@ var TreeStore = /** @class */ (function (_super) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e_15_1) { e_15 = { error: e_15_1 }; }
|
catch (e_19_1) { e_19 = { error: e_19_1 }; }
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
if (d_1_1 && !d_1_1.done && (_b = d_1.return)) _b.call(d_1);
|
if (d_1_1 && !d_1_1.done && (_b = d_1.return)) _b.call(d_1);
|
||||||
}
|
}
|
||||||
finally { if (e_15) throw e_15.error; }
|
finally { if (e_19) throw e_19.error; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
@ -1435,12 +1664,12 @@ var TreeStore = /** @class */ (function (_super) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e_14_1) { e_14 = { error: e_14_1 }; }
|
catch (e_18_1) { e_18 = { error: e_18_1 }; }
|
||||||
finally {
|
finally {
|
||||||
try {
|
try {
|
||||||
if (opRecords_1_1 && !opRecords_1_1.done && (_a = opRecords_1.return)) _a.call(opRecords_1);
|
if (opRecords_1_1 && !opRecords_1_1.done && (_a = opRecords_1.return)) _a.call(opRecords_1);
|
||||||
}
|
}
|
||||||
finally { if (e_14) throw e_14.error; }
|
finally { if (e_18) throw e_18.error; }
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return TreeStore;
|
return TreeStore;
|
||||||
|
|
|
||||||
234
src/store.ts
234
src/store.ts
|
|
@ -1,12 +1,13 @@
|
||||||
import { cloneDeep, get, set, unset } from 'oak-domain/lib/utils/lodash';
|
import { cloneDeep, get, groupBy, set, unset } from 'oak-domain/lib/utils/lodash';
|
||||||
import { assert } from 'oak-domain/lib/utils/assert';
|
import { assert } from 'oak-domain/lib/utils/assert';
|
||||||
import {
|
import {
|
||||||
DeduceCreateSingleOperation, DeduceFilter, DeduceSelection, EntityShape, DeduceRemoveOperation,
|
DeduceCreateSingleOperation, DeduceFilter, DeduceSelection, EntityShape, DeduceRemoveOperation,
|
||||||
DeduceUpdateOperation, DeduceSorter, DeduceSorterAttr, OperationResult, OperateOption, OpRecord,
|
DeduceUpdateOperation, DeduceSorter, DeduceSorterAttr, OperationResult, OperateOption, OpRecord,
|
||||||
DeduceCreateOperationData, UpdateOpResult, RemoveOpResult, SelectOpResult,
|
DeduceCreateOperationData, UpdateOpResult, RemoveOpResult, SelectOpResult,
|
||||||
EntityDict, SelectOption, DeleteAtAttribute
|
EntityDict, SelectOption, DeleteAtAttribute, AggregationResult, AggregationOp
|
||||||
} from "oak-domain/lib/types/Entity";
|
} from "oak-domain/lib/types/Entity";
|
||||||
import { ExpressionKey, EXPRESSION_PREFIX, NodeId, RefAttr } from 'oak-domain/lib/types/Demand';
|
import { ExpressionKey, EXPRESSION_PREFIX, NodeId, RefAttr } from 'oak-domain/lib/types/Demand';
|
||||||
|
import { reinforceSelection } from 'oak-domain/lib/store/selection';
|
||||||
import { OakCongruentRowExists, OakException, OakRowUnexistedException } from 'oak-domain/lib/types/Exception';
|
import { OakCongruentRowExists, OakException, OakRowUnexistedException } from 'oak-domain/lib/types/Exception';
|
||||||
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict as BaseEntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { StorageSchema } from 'oak-domain/lib/types/Storage';
|
import { StorageSchema } from 'oak-domain/lib/types/Storage';
|
||||||
|
|
@ -973,12 +974,12 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
operateSync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends SyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): OperationResult<ED> {
|
protected operateSync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends SyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP): OperationResult<ED> {
|
||||||
assert(context.getCurrentTxnId());
|
assert(context.getCurrentTxnId());
|
||||||
return this.cascadeUpdate(entity, operation, context, option);
|
return this.cascadeUpdate(entity, operation, context, option);
|
||||||
}
|
}
|
||||||
|
|
||||||
async operateAsync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends AsyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP) {
|
protected async operateAsync<T extends keyof ED, OP extends TreeStoreOperateOption, Cxt extends AsyncContext<ED>>(entity: T, operation: ED[T]['Operation'], context: Cxt, option: OP) {
|
||||||
assert(context.getCurrentTxnId());
|
assert(context.getCurrentTxnId());
|
||||||
return this.cascadeUpdateAsync(entity, operation, context, option);
|
return this.cascadeUpdateAsync(entity, operation, context, option);
|
||||||
}
|
}
|
||||||
|
|
@ -991,7 +992,7 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
||||||
* @param nodeDict
|
* @param nodeDict
|
||||||
* @param context
|
* @param context
|
||||||
*/
|
*/
|
||||||
protected formExprInResult<T extends keyof ED, Cxt extends Context>(
|
private formExprInResult<T extends keyof ED, Cxt extends Context>(
|
||||||
entity: T,
|
entity: T,
|
||||||
projection: ED[T]['Selection']['data'],
|
projection: ED[T]['Selection']['data'],
|
||||||
data: Partial<ED[T]['Schema']>,
|
data: Partial<ED[T]['Schema']>,
|
||||||
|
|
@ -1172,7 +1173,158 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(
|
/**
|
||||||
|
* 本函数把结果中的相应属性映射成一个字符串,用于GroupBy
|
||||||
|
* @param entity
|
||||||
|
* @param row
|
||||||
|
* @param projection
|
||||||
|
*/
|
||||||
|
private mappingProjectionOnRow<T extends keyof ED>(
|
||||||
|
entity: T,
|
||||||
|
row: Partial<ED[T]['Schema']>,
|
||||||
|
projection: ED[T]['Selection']['data']
|
||||||
|
) {
|
||||||
|
let key = '';
|
||||||
|
let result = {} as Partial<ED[T]['Schema']>;
|
||||||
|
const values = [] as any[];
|
||||||
|
const mappingIter = <T2 extends keyof ED>(
|
||||||
|
entity2: T2,
|
||||||
|
row2: Partial<ED[T2]['Schema']>,
|
||||||
|
p2: ED[T2]['Selection']['data'],
|
||||||
|
result2: Partial<ED[T2]['Schema']>) => {
|
||||||
|
const keys = Object.keys(p2).sort((ele1, ele2) => ele1 < ele2 ? -1 : 1);
|
||||||
|
for (const k of keys) {
|
||||||
|
const rel = this.judgeRelation(entity2, k);
|
||||||
|
if (rel === 2) {
|
||||||
|
(result2 as any)[k] = {};
|
||||||
|
if (row2[k]) {
|
||||||
|
mappingIter(k, row2[k]!, p2[k], result2[k]!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (typeof rel === 'string') {
|
||||||
|
(result2 as any)[k] = {};
|
||||||
|
if(row2[k]) {
|
||||||
|
mappingIter(rel, row2[k]!, p2[k], result2[k]!);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert([0, 1].includes(rel as number));
|
||||||
|
(result2 as any)[k] = row2[k];
|
||||||
|
assert(['string', 'number', 'boolean'].includes(typeof row2[k]));
|
||||||
|
key += `${row2[k]}`;
|
||||||
|
values.push(row2[k]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mappingIter(entity, row, projection, result);
|
||||||
|
return {
|
||||||
|
result,
|
||||||
|
key,
|
||||||
|
values,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private calcAggregation<T extends keyof ED>(
|
||||||
|
entity: T,
|
||||||
|
rows: Partial<ED[T]['Schema']>[],
|
||||||
|
aggregationData: ED[T]['Aggregation']['data']
|
||||||
|
) {
|
||||||
|
const ops = Object.keys(aggregationData).filter(
|
||||||
|
ele => ele !== '$aggr'
|
||||||
|
) as AggregationOp [];
|
||||||
|
const result = {} as Record<string, any>;
|
||||||
|
for (const row of rows) {
|
||||||
|
for (const op of ops) {
|
||||||
|
const { values } = this.mappingProjectionOnRow(entity, row, (aggregationData as any)[op]);
|
||||||
|
assert(values.length === 1, `聚合运算中,${op}的目标属性多于1个`);
|
||||||
|
if (op.startsWith('$max')) {
|
||||||
|
if (![undefined, null].includes(values[0]) && (!result.hasOwnProperty(op) || result[op] < values[0])) {
|
||||||
|
result[op] = values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op.startsWith('$min')) {
|
||||||
|
if (![undefined, null].includes(values[0]) && (!result.hasOwnProperty(op) || result[op] > values[0])) {
|
||||||
|
result[op] = values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op.startsWith('$sum')) {
|
||||||
|
if (![undefined, null].includes(values[0])) {
|
||||||
|
assert(typeof values[0] === 'number', '只有number类型的属性才可以计算sum');
|
||||||
|
if (!result.hasOwnProperty(op)) {
|
||||||
|
result[op] = values[0];
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result[op] += values[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op.startsWith('$count')) {
|
||||||
|
if (![undefined, null].includes(values[0])) {
|
||||||
|
if (!result.hasOwnProperty(op)) {
|
||||||
|
result[op] = 1;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result[op] += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(op.startsWith('$avg'));
|
||||||
|
if (![undefined, null].includes(values[0])) {
|
||||||
|
assert(typeof values[0] === 'number', '只有number类型的属性才可以计算avg');
|
||||||
|
if (!result.hasOwnProperty(op)) {
|
||||||
|
result[op] = {
|
||||||
|
total: values[0],
|
||||||
|
count: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
result[op].total += values[0];
|
||||||
|
result[op].count += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const op of ops) {
|
||||||
|
if (!result[op]) {
|
||||||
|
result[op] = null;
|
||||||
|
}
|
||||||
|
else if (op.startsWith('$avg')) {
|
||||||
|
result[op] = result[op].total/result[op].count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result as AggregationResult<ED[T]['Schema']>[number];
|
||||||
|
}
|
||||||
|
|
||||||
|
private formAggregation<T extends keyof ED, Cxt extends Context>(
|
||||||
|
entity: T,
|
||||||
|
rows: Array<Partial<ED[T]['Schema']>>,
|
||||||
|
aggregationData: ED[T]['Aggregation']['data']) {
|
||||||
|
const { $aggr } = aggregationData;
|
||||||
|
if ($aggr) {
|
||||||
|
const groups = groupBy(rows, (row) => {
|
||||||
|
const { key } = this.mappingProjectionOnRow(entity, row, $aggr);
|
||||||
|
return key;
|
||||||
|
});
|
||||||
|
const result = Object.keys(groups).map(
|
||||||
|
(ele) => {
|
||||||
|
const aggr = this.calcAggregation(entity, groups[ele], aggregationData);
|
||||||
|
const { result: r } = this.mappingProjectionOnRow(entity, groups[ele][0], $aggr);
|
||||||
|
aggr.data = r;
|
||||||
|
return aggr;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
const aggr = this.calcAggregation(entity, rows, aggregationData);
|
||||||
|
return [ aggr ];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected selectSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(
|
||||||
entity: T,
|
entity: T,
|
||||||
selection: ED[T]['Selection'],
|
selection: ED[T]['Selection'],
|
||||||
context: Cxt,
|
context: Cxt,
|
||||||
|
|
@ -1186,7 +1338,7 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async selectAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(
|
protected async selectAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(
|
||||||
entity: T,
|
entity: T,
|
||||||
selection: ED[T]['Selection'],
|
selection: ED[T]['Selection'],
|
||||||
context: Cxt,
|
context: Cxt,
|
||||||
|
|
@ -1200,7 +1352,71 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
countSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(
|
protected aggregateSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(
|
||||||
|
entity: T,
|
||||||
|
aggregation: ED[T]['Aggregation'],
|
||||||
|
context: Cxt,
|
||||||
|
option: OP): AggregationResult<ED[T]['Schema']> {
|
||||||
|
assert(context.getCurrentTxnId());
|
||||||
|
const { data, filter, sorter, indexFrom, count } = aggregation;
|
||||||
|
const p: ED[T]['Selection']['data'] = {};
|
||||||
|
for (const k in data) {
|
||||||
|
Object.assign(p, cloneDeep((data as any)[k]));
|
||||||
|
}
|
||||||
|
const selection: ED[T]['Selection'] = {
|
||||||
|
data: p,
|
||||||
|
filter,
|
||||||
|
sorter,
|
||||||
|
indexFrom,
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
reinforceSelection(this.storageSchema, entity, selection);
|
||||||
|
|
||||||
|
const result = this.cascadeSelect(entity, selection, context, Object.assign({}, option, {
|
||||||
|
dontCollect: true,
|
||||||
|
}));
|
||||||
|
// 在这里再计算所有的表达式
|
||||||
|
result.forEach(
|
||||||
|
(ele) => this.formExprInResult(entity, selection.data, ele, {}, context)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 最后计算Aggregation
|
||||||
|
return this.formAggregation(entity, result, aggregation.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async aggregateAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(
|
||||||
|
entity: T,
|
||||||
|
aggregation: ED[T]['Aggregation'],
|
||||||
|
context: Cxt,
|
||||||
|
option: OP): Promise<AggregationResult<ED[T]['Schema']>> {
|
||||||
|
assert(context.getCurrentTxnId());
|
||||||
|
const { data, filter, sorter, indexFrom, count } = aggregation;
|
||||||
|
const p: ED[T]['Selection']['data'] = {};
|
||||||
|
for (const k in data) {
|
||||||
|
Object.assign(p, cloneDeep((data as any)[k]));
|
||||||
|
}
|
||||||
|
const selection: ED[T]['Selection'] = {
|
||||||
|
data: p,
|
||||||
|
filter,
|
||||||
|
sorter,
|
||||||
|
indexFrom,
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
reinforceSelection(this.storageSchema, entity, selection);
|
||||||
|
|
||||||
|
const result = await this.cascadeSelectAsync(entity, selection, context, Object.assign({}, option, {
|
||||||
|
dontCollect: true,
|
||||||
|
}));
|
||||||
|
// 在这里再计算所有的表达式
|
||||||
|
result.forEach(
|
||||||
|
(ele) => this.formExprInResult(entity, selection.data, ele, {}, context)
|
||||||
|
);
|
||||||
|
|
||||||
|
// 最后计算Aggregation
|
||||||
|
return this.formAggregation(entity, result, aggregation.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected countSync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends SyncContext<ED>>(
|
||||||
entity: T,
|
entity: T,
|
||||||
selection: Pick<ED[T]['Selection'], 'filter' | 'count'>,
|
selection: Pick<ED[T]['Selection'], 'filter' | 'count'>,
|
||||||
context: Cxt, option: OP): number {
|
context: Cxt, option: OP): number {
|
||||||
|
|
@ -1215,7 +1431,7 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
||||||
return typeof selection.count === 'number' ? Math.min(result.length, selection.count) : result.length;
|
return typeof selection.count === 'number' ? Math.min(result.length, selection.count) : result.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
async countAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(
|
protected async countAsync<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends AsyncContext<ED>>(
|
||||||
entity: T,
|
entity: T,
|
||||||
selection: Pick<ED[T]['Selection'], 'filter' | 'count'>,
|
selection: Pick<ED[T]['Selection'], 'filter' | 'count'>,
|
||||||
context: Cxt, option: OP) {
|
context: Cxt, option: OP) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import TreeStore from '../src/store';
|
||||||
import { EntityDict } from 'oak-domain/lib/base-app-domain';
|
import { EntityDict } from 'oak-domain/lib/base-app-domain';
|
||||||
import { SyncContext, SyncRowStore } from "oak-domain/lib/store/SyncRowStore";
|
import { SyncContext, SyncRowStore } from "oak-domain/lib/store/SyncRowStore";
|
||||||
import { OperateOption, OperationResult, SelectOption, AggregationResult, TxnOption, StorageSchema } from "oak-domain/lib/types";
|
import { OperateOption, OperationResult, SelectOption, AggregationResult, TxnOption, StorageSchema } from "oak-domain/lib/types";
|
||||||
|
import { reinforceSelection } from 'oak-domain/lib/store/selection';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 实现一个同步的context和store用于测试
|
* 实现一个同步的context和store用于测试
|
||||||
|
|
@ -26,13 +27,14 @@ export class FrontendStore extends TreeStore<EntityDict> implements SyncRowStore
|
||||||
return this.operateSync(entity, operation, context, option);
|
return this.operateSync(entity, operation, context, option);
|
||||||
}
|
}
|
||||||
select<T extends keyof EntityDict, OP extends SelectOption>(entity: T, selection: EntityDict[T]["Selection"], context: FrontendRuntimeContext, option: OP): Partial<EntityDict[T]["Schema"]>[] {
|
select<T extends keyof EntityDict, OP extends SelectOption>(entity: T, selection: EntityDict[T]["Selection"], context: FrontendRuntimeContext, option: OP): Partial<EntityDict[T]["Schema"]>[] {
|
||||||
|
reinforceSelection(this.storageSchema, entity, selection);
|
||||||
return this.selectSync(entity, selection, context, option);
|
return this.selectSync(entity, selection, context, option);
|
||||||
}
|
}
|
||||||
count<T extends keyof EntityDict, OP extends SelectOption>(entity: T, selection: Pick<EntityDict[T]["Selection"], "count" | "filter">, context: FrontendRuntimeContext, option: OP): number {
|
count<T extends keyof EntityDict, OP extends SelectOption>(entity: T, selection: Pick<EntityDict[T]["Selection"], "count" | "filter">, context: FrontendRuntimeContext, option: OP): number {
|
||||||
return this.countSync(entity, selection, context, option);
|
return this.countSync(entity, selection, context, option);
|
||||||
}
|
}
|
||||||
aggregate<T extends keyof EntityDict, OP extends SelectOption>(entity: T, aggregation: EntityDict[T]["Aggregation"], context: FrontendRuntimeContext, option: OP): AggregationResult<EntityDict[T]["Schema"]> {
|
aggregate<T extends keyof EntityDict, OP extends SelectOption>(entity: T, aggregation: EntityDict[T]["Aggregation"], context: FrontendRuntimeContext, option: OP): AggregationResult<EntityDict[T]["Schema"]> {
|
||||||
throw new Error("Method not implemented.");
|
return this.aggregateSync(entity, aggregation, context, option);
|
||||||
}
|
}
|
||||||
begin(option?: TxnOption | undefined): string {
|
begin(option?: TxnOption | undefined): string {
|
||||||
return this.beginSync();
|
return this.beginSync();
|
||||||
|
|
|
||||||
95
test/test.ts
95
test/test.ts
|
|
@ -81,7 +81,6 @@ describe('基础测试', function () {
|
||||||
context.commit();
|
context.commit();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
it('[1.1]子查询', () => {
|
it('[1.1]子查询', () => {
|
||||||
const store = new FrontendStore(storageSchema);
|
const store = new FrontendStore(storageSchema);
|
||||||
const context = new FrontendRuntimeContext(store);
|
const context = new FrontendRuntimeContext(store);
|
||||||
|
|
@ -154,7 +153,7 @@ describe('基础测试', function () {
|
||||||
context.commit();
|
context.commit();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('[1.2]行内属性上的表达式', async () => {
|
it('[1.2]行内属性上的表达式', () => {
|
||||||
const store = new FrontendStore(storageSchema);
|
const store = new FrontendStore(storageSchema);
|
||||||
const context = new FrontendRuntimeContext(store);
|
const context = new FrontendRuntimeContext(store);
|
||||||
context.begin();
|
context.begin();
|
||||||
|
|
@ -218,7 +217,7 @@ describe('基础测试', function () {
|
||||||
context.commit();
|
context.commit();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('[1.3]跨filter结点的表达式', async () => {
|
it('[1.3]跨filter结点的表达式', () => {
|
||||||
const store = new FrontendStore(storageSchema);
|
const store = new FrontendStore(storageSchema);
|
||||||
const context = new FrontendRuntimeContext(store);
|
const context = new FrontendRuntimeContext(store);
|
||||||
context.begin();
|
context.begin();
|
||||||
|
|
@ -433,7 +432,6 @@ describe('基础测试', function () {
|
||||||
entity: 1,
|
entity: 1,
|
||||||
modi: {
|
modi: {
|
||||||
id: 1,
|
id: 1,
|
||||||
entity: 1,
|
|
||||||
$expr: {
|
$expr: {
|
||||||
$eq: [
|
$eq: [
|
||||||
{
|
{
|
||||||
|
|
@ -526,7 +524,7 @@ describe('基础测试', function () {
|
||||||
data: {
|
data: {
|
||||||
id: 1,
|
id: 1,
|
||||||
entity: 1,
|
entity: 1,
|
||||||
modiId: 1,
|
// modiId: 1,
|
||||||
$expr: {
|
$expr: {
|
||||||
$eq: [
|
$eq: [
|
||||||
{
|
{
|
||||||
|
|
@ -554,7 +552,7 @@ describe('基础测试', function () {
|
||||||
context.commit();
|
context.commit();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('[1.7]事务性测试', async () => {
|
it('[1.7]事务性测试', () => {
|
||||||
const store = new FrontendStore(storageSchema);
|
const store = new FrontendStore(storageSchema);
|
||||||
const context = new FrontendRuntimeContext(store);
|
const context = new FrontendRuntimeContext(store);
|
||||||
context.begin();
|
context.begin();
|
||||||
|
|
@ -643,5 +641,90 @@ describe('基础测试', function () {
|
||||||
|
|
||||||
context.commit();
|
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);
|
||||||
|
context.commit();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue