支持了对object的内部投影和查询
This commit is contained in:
parent
ae472cc5b9
commit
7d6456ac8e
|
|
@ -46,6 +46,8 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
private translateExpressionNode;
|
||||
private translateExpression;
|
||||
private translateFulltext;
|
||||
private translatePredicate;
|
||||
private translateObjectPredicate;
|
||||
private translateAttribute;
|
||||
private translateFilter;
|
||||
private translateSorter;
|
||||
|
|
|
|||
531
lib/store.js
531
lib/store.js
|
|
@ -12,8 +12,8 @@ var Expression_1 = require("oak-domain/lib/types/Expression");
|
|||
var CascadeStore_1 = require("oak-domain/lib/store/CascadeStore");
|
||||
;
|
||||
;
|
||||
function obscurePass(row, attr, option) {
|
||||
return !!((option === null || option === void 0 ? void 0 : option.obscure) && row[attr] === undefined);
|
||||
function obscurePass(value, option) {
|
||||
return !!((option === null || option === void 0 ? void 0 : option.obscure) && value === undefined);
|
||||
}
|
||||
var OakExpressionUnresolvedException = /** @class */ (function (_super) {
|
||||
tslib_1.__extends(OakExpressionUnresolvedException, _super);
|
||||
|
|
@ -350,7 +350,7 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
for (var attributes_1 = tslib_1.__values(attributes), attributes_1_1 = attributes_1.next(); !attributes_1_1.done; attributes_1_1 = attributes_1.next()) {
|
||||
var attr = attributes_1_1.value;
|
||||
var name_1 = attr.name;
|
||||
if (row && row[name_1] && (typeof row[name_1] === 'string' && row[name_1].includes($search) || obscurePass(row, name_1, option))) {
|
||||
if (row && row[name_1] && (typeof row[name_1] === 'string' && row[name_1].includes($search) || obscurePass(row[name_1], option))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -365,188 +365,158 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
return false;
|
||||
};
|
||||
};
|
||||
TreeStore.prototype.translateAttribute = function (entity, filter, attr, context, option) {
|
||||
TreeStore.prototype.translatePredicate = function (path, predicate, value, option) {
|
||||
switch (predicate) {
|
||||
case '$gt': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data > value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$lt': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data < value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$gte': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data >= value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$lte': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data <= value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$eq': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data === value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$ne': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data !== value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$between': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data >= value[0] && data <= value[1] || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$startsWith': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data.startsWith(value) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$endsWith': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data.endsWith(value) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$includes': {
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return data.includes(value) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$exists': {
|
||||
(0, assert_1.assert)(typeof value === 'boolean');
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
if (value) {
|
||||
return ![null, undefined].includes(data) || obscurePass(data, option);
|
||||
}
|
||||
else {
|
||||
return [null, undefined].includes(data) || obscurePass(data, option);
|
||||
}
|
||||
};
|
||||
}
|
||||
case '$in': {
|
||||
(0, assert_1.assert)(value instanceof Array);
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return value.includes(data) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$nin': {
|
||||
(0, assert_1.assert)(value instanceof Array);
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return !value.includes(data) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$contains': {
|
||||
// json中的多值查询
|
||||
(0, assert_1.assert)(value instanceof Array);
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return (0, lodash_1.difference)(value, data).length === 0 || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$overlaps': {
|
||||
// json中的多值查询
|
||||
(0, assert_1.assert)(value instanceof Array);
|
||||
return function (row) {
|
||||
var data = (0, lodash_1.get)(row, path);
|
||||
return (0, lodash_1.intersection)(value, data).length > 0 || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
default: {
|
||||
throw new Error("predicate ".concat(predicate, " is not recoganized"));
|
||||
}
|
||||
}
|
||||
};
|
||||
TreeStore.prototype.translateObjectPredicate = function (filter) {
|
||||
var _this = this;
|
||||
var _a;
|
||||
// 如果是模糊查询且该属性为undefined,说明没取到,返回true
|
||||
function obscurePassLocal(row) {
|
||||
return obscurePass(row, attr, option);
|
||||
}
|
||||
if (typeof filter !== 'object') {
|
||||
return function (node) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
return row ? row[attr] === filter || obscurePassLocal(row) : false;
|
||||
};
|
||||
}
|
||||
else if (((_a = this.getSchema()[entity].attributes[attr]) === null || _a === void 0 ? void 0 : _a.type) === 'object') {
|
||||
// 如果查询的目标就是object,则转化成object的比较
|
||||
return function (node) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
return row ? JSON.stringify(row[attr]) === JSON.stringify(filter) || obscurePassLocal(row) : false;
|
||||
};
|
||||
}
|
||||
var fns = [];
|
||||
var _loop_1 = function (op) {
|
||||
switch (op) {
|
||||
case '$gt': {
|
||||
fns.push(function (row) { return row && (row[attr] > filter[op]) || obscurePassLocal(row); });
|
||||
break;
|
||||
}
|
||||
case '$lt': {
|
||||
fns.push(function (row) { return row && (row[attr] < filter[op]) || obscurePassLocal(row); });
|
||||
break;
|
||||
}
|
||||
case '$gte': {
|
||||
fns.push(function (row) { return row && (row[attr] >= filter[op]) || obscurePassLocal(row); });
|
||||
break;
|
||||
}
|
||||
case '$lte': {
|
||||
fns.push(function (row) { return row && (row[attr] <= filter[op]) || obscurePassLocal(row); });
|
||||
break;
|
||||
}
|
||||
case '$eq': {
|
||||
fns.push(function (row) { return row && (row[attr] === filter[op]) || obscurePassLocal(row); });
|
||||
break;
|
||||
}
|
||||
case '$ne': {
|
||||
fns.push(function (row) { return row && (row[attr] !== filter[op]) || obscurePassLocal(row); });
|
||||
break;
|
||||
}
|
||||
case '$between': {
|
||||
fns.push(function (row) {
|
||||
return row && (row[attr] >= filter[op][0] && row[attr] <= filter[op][1] || obscurePassLocal(row));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$startsWith': {
|
||||
fns.push(function (row) {
|
||||
var _a;
|
||||
return row && (((_a = row[attr]) === null || _a === void 0 ? void 0 : _a.startsWith(filter[op])) || obscurePassLocal(row));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$endsWith': {
|
||||
fns.push(function (row) {
|
||||
var _a;
|
||||
return row && (((_a = row[attr]) === null || _a === void 0 ? void 0 : _a.$endsWith(filter[op])) || obscurePassLocal(row));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$includes': {
|
||||
fns.push(function (row) {
|
||||
var _a;
|
||||
return row && (((_a = row[attr]) === null || _a === void 0 ? void 0 : _a.includes(filter[op])) || obscurePassLocal(row));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$exists': {
|
||||
var exists_1 = filter[op];
|
||||
(0, assert_1.assert)(typeof exists_1 === 'boolean');
|
||||
fns.push(function (row) {
|
||||
if (exists_1) {
|
||||
return ![null, undefined].includes(row[attr]) || obscurePassLocal(row);
|
||||
var translatePredicateInner = function (p, path) {
|
||||
var predicate = Object.keys(p)[0];
|
||||
if (predicate.startsWith('$')) {
|
||||
(0, assert_1.assert)(Object.keys(p).length === 1);
|
||||
fns.push(_this.translatePredicate(path, predicate, p[predicate]));
|
||||
}
|
||||
else {
|
||||
if (p instanceof Array) {
|
||||
p.forEach(function (ele, idx) {
|
||||
var path2 = "".concat(path, "[").concat(idx, "]");
|
||||
if (typeof ele !== 'object') {
|
||||
if (![null, undefined].includes(ele)) {
|
||||
fns.push(_this.translatePredicate(path2, '$eq', ele));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return [null, undefined].includes(row[attr]) || obscurePassLocal(row);
|
||||
translatePredicateInner(ele, path2);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$in': {
|
||||
var inData_1 = filter[op];
|
||||
(0, assert_1.assert)(typeof inData_1 === 'object');
|
||||
if (inData_1 instanceof Array) {
|
||||
fns.push(function (row) { return inData_1.includes(row[attr]) || obscurePassLocal(row); });
|
||||
}
|
||||
else {
|
||||
// 如果是obscure,则返回的集合中有没有都不能否决“可能有”,所以可以直接返回true
|
||||
if (option === null || option === void 0 ? void 0 : option.obscure) {
|
||||
fns.push(function () { return true; });
|
||||
else {
|
||||
for (var attr in p) {
|
||||
var path2 = path ? "".concat(path, ".").concat(attr) : attr;
|
||||
if (typeof p[attr] !== 'object') {
|
||||
fns.push(_this.translatePredicate(path2, '$eq', filter[attr]));
|
||||
}
|
||||
else {
|
||||
// 这里只有当子查询中的filter不包含引用外部的子查询时才可以提前计算,否则必须等到执行时再计算
|
||||
try {
|
||||
var legalSets_1 = (this_1.selectAbjointRow(inData_1.entity, inData_1, context, option)).map(function (ele) {
|
||||
var data = inData_1.data;
|
||||
var key = Object.keys(data)[0];
|
||||
return ele[key];
|
||||
});
|
||||
fns.push(function (row) { return legalSets_1.includes(row[attr]); });
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof OakExpressionUnresolvedException) {
|
||||
fns.push(function (row, nodeDict) {
|
||||
var option2 = Object.assign({}, option, { nodeDict: nodeDict });
|
||||
var legalSets = _this.selectAbjointRow(inData_1.entity, inData_1, context, option2).map(function (ele) {
|
||||
var data = inData_1.data;
|
||||
var key = Object.keys(data)[0];
|
||||
return ele[key];
|
||||
});
|
||||
return legalSets.includes(row[attr]);
|
||||
});
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
translatePredicateInner(p[attr], path2);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '$nin': {
|
||||
var inData_2 = filter[op];
|
||||
(0, assert_1.assert)(typeof inData_2 === 'object');
|
||||
if (inData_2 instanceof Array) {
|
||||
fns.push(function (row) { return !inData_2.includes(row[attr]) || obscurePassLocal(row); });
|
||||
}
|
||||
else {
|
||||
// obscure对nin没有影响,如果返回的子查询结果中包含此行就一定是false,否则一定为true(obscure只考虑数据不完整,不考虑不准确),但若相应属性为undefined则任然可以认为true
|
||||
// 这里只有当子查询中的filter不包含引用外部的子查询时才可以提前计算,否则必须等到执行时再计算
|
||||
try {
|
||||
var legalSets_2 = this_1.selectAbjointRow(inData_2.entity, inData_2, context, option).map(function (ele) {
|
||||
var data = inData_2.data;
|
||||
var key = Object.keys(data)[0];
|
||||
return ele[key];
|
||||
});
|
||||
fns.push(function (row) { return !legalSets_2.includes(row[attr]) || obscurePassLocal(row); });
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof OakExpressionUnresolvedException) {
|
||||
fns.push(function (row, nodeDict) {
|
||||
var option2 = Object.assign({}, option, { nodeDict: nodeDict });
|
||||
var legalSets = _this.selectAbjointRow(inData_2.entity, inData_2, context, option2).map(function (ele) {
|
||||
var data = inData_2.data;
|
||||
var key = Object.keys(data)[0];
|
||||
return ele[key];
|
||||
});
|
||||
return !legalSets.includes(row[attr]) || obscurePassLocal(row);
|
||||
});
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
(0, assert_1.assert)(false, "\u76EE\u524D\u4E0D\u652F\u6301\u7684\u7B97\u5B50".concat(op));
|
||||
}
|
||||
};
|
||||
var this_1 = this;
|
||||
for (var op in filter) {
|
||||
_loop_1(op);
|
||||
}
|
||||
return function (node, nodeDict, exprResolveFns) {
|
||||
translatePredicateInner(filter, '');
|
||||
return function (value) {
|
||||
var e_5, _a;
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
for (var fns_5 = tslib_1.__values(fns), fns_5_1 = fns_5.next(); !fns_5_1.done; fns_5_1 = fns_5.next()) {
|
||||
var fn = fns_5_1.value;
|
||||
if (fn(row, nodeDict, exprResolveFns) === false) {
|
||||
if (!fn(value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -561,25 +531,150 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
return true;
|
||||
};
|
||||
};
|
||||
TreeStore.prototype.translateAttribute = function (entity, filter, attr, context, option) {
|
||||
var _this = this;
|
||||
var _a;
|
||||
// 如果是模糊查询且该属性为undefined,说明没取到,返回true
|
||||
function obscurePassLocal(row) {
|
||||
return obscurePass(row[attr], option);
|
||||
}
|
||||
if (typeof filter !== 'object') {
|
||||
return function (node) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
return row ? row[attr] === filter || obscurePassLocal(row) : false;
|
||||
};
|
||||
}
|
||||
else {
|
||||
var predicate = Object.keys(filter)[0];
|
||||
if (predicate.startsWith('$')) {
|
||||
if (['$in', '$nin'].includes(predicate) && !(filter[predicate] instanceof Array)) {
|
||||
var inData_1 = filter[predicate];
|
||||
if (predicate === '$in') {
|
||||
// 如果是obscure,则返回的集合中有没有都不能否决“可能有”,所以可以直接返回true
|
||||
if (option === null || option === void 0 ? void 0 : option.obscure) {
|
||||
return function () { return true; };
|
||||
}
|
||||
else {
|
||||
// 这里只有当子查询中的filter不包含引用外部的子查询时才可以提前计算,否则必须等到执行时再计算
|
||||
try {
|
||||
var legalSets_1 = (this.selectAbjointRow(inData_1.entity, inData_1, context, option)).map(function (ele) {
|
||||
var data = inData_1.data;
|
||||
var key = Object.keys(data)[0];
|
||||
return ele[key];
|
||||
});
|
||||
return function (node) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
return legalSets_1.includes(row[attr]);
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof OakExpressionUnresolvedException) {
|
||||
return function (node, nodeDict) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
var option2 = Object.assign({}, option, { nodeDict: nodeDict });
|
||||
var legalSets = _this.selectAbjointRow(inData_1.entity, inData_1, context, option2).map(function (ele) {
|
||||
var data = inData_1.data;
|
||||
var key = Object.keys(data)[0];
|
||||
return ele[key];
|
||||
});
|
||||
return legalSets.includes(row[attr]);
|
||||
};
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
// obscure对nin没有影响,如果返回的子查询结果中包含此行就一定是false,否则一定为true(obscure只考虑数据不完整,不考虑不准确),但若相应属性为undefined则任然可以认为true
|
||||
// 这里只有当子查询中的filter不包含引用外部的子查询时才可以提前计算,否则必须等到执行时再计算
|
||||
try {
|
||||
var legalSets_2 = this.selectAbjointRow(inData_1.entity, inData_1, context, option).map(function (ele) {
|
||||
var data = inData_1.data;
|
||||
var key = Object.keys(data)[0];
|
||||
return ele[key];
|
||||
});
|
||||
return function (node) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
return !legalSets_2.includes(row[attr]) || obscurePassLocal(row);
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof OakExpressionUnresolvedException) {
|
||||
return function (node, nodeDict) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
var option2 = Object.assign({}, option, { nodeDict: nodeDict });
|
||||
var legalSets = _this.selectAbjointRow(inData_1.entity, inData_1, context, option2).map(function (ele) {
|
||||
var data = inData_1.data;
|
||||
var key = Object.keys(data)[0];
|
||||
return ele[key];
|
||||
});
|
||||
return !legalSets.includes(row[attr]) || obscurePassLocal(row);
|
||||
};
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
var fn_2 = this.translatePredicate(attr, predicate, filter[predicate], option);
|
||||
return function (node) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
return fn_2(row);
|
||||
};
|
||||
}
|
||||
}
|
||||
else {
|
||||
// 对象的内部查询
|
||||
(0, assert_1.assert)(((_a = this.getSchema()[entity].attributes[attr]) === null || _a === void 0 ? void 0 : _a.type) === 'object');
|
||||
var fn_3 = this.translateObjectPredicate(filter);
|
||||
return function (node) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
return fn_3(row[attr]) || obscurePassLocal(row);
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
TreeStore.prototype.translateFilter = function (entity, filter, context, option) {
|
||||
var _this = this;
|
||||
var fns = [];
|
||||
var nodeId;
|
||||
var _loop_2 = function (attr) {
|
||||
var _loop_1 = function (attr) {
|
||||
if (attr === '#id') {
|
||||
nodeId = filter['#id'];
|
||||
}
|
||||
else if (['$and', '$or', '$xor', '$not'].includes(attr)) {
|
||||
fns.push(this_2.translateLogicFilter(entity, filter, attr, context, option));
|
||||
fns.push(this_1.translateLogicFilter(entity, filter, attr, context, option));
|
||||
}
|
||||
else if (attr.toLowerCase().startsWith(Demand_1.EXPRESSION_PREFIX)) {
|
||||
var fn_2 = this_2.translateExpression(entity, filter[attr], context, option);
|
||||
var fn_4 = this_1.translateExpression(entity, filter[attr], context, option);
|
||||
fns.push(function (node, nodeDict, exprResolveFns) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
var result = fn_2(row, nodeDict);
|
||||
var result = fn_4(row, nodeDict);
|
||||
if (typeof result === 'function') {
|
||||
exprResolveFns.push(result);
|
||||
}
|
||||
|
|
@ -587,21 +682,21 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
});
|
||||
}
|
||||
else if (attr.toLowerCase() === '$text') {
|
||||
fns.push(this_2.translateFulltext(entity, filter[attr], context, option));
|
||||
fns.push(this_1.translateFulltext(entity, filter[attr], context, option));
|
||||
}
|
||||
else {
|
||||
// 属性级过滤
|
||||
var relation_2 = (0, relation_1.judgeRelation)(this_2.getSchema(), entity, attr);
|
||||
var relation_2 = (0, relation_1.judgeRelation)(this_1.getSchema(), entity, attr);
|
||||
if (relation_2 === 1) {
|
||||
// 行本身的属性
|
||||
fns.push(this_2.translateAttribute(entity, filter[attr], attr, context, option));
|
||||
fns.push(this_1.translateAttribute(entity, filter[attr], attr, context, option));
|
||||
}
|
||||
else if (relation_2 === 2) {
|
||||
// 基于entity/entityId的指针
|
||||
var fn_3 = this_2.translateFilter(attr, filter[attr], context, option);
|
||||
var fn_5 = this_1.translateFilter(attr, filter[attr], context, option);
|
||||
fns.push(function (node, nodeDict, exprResolveFns) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (obscurePass(row, 'entity', option) || obscurePass(row, 'entityId', option)) {
|
||||
if (obscurePass(row.entity, option) || obscurePass(row.entityId, option)) {
|
||||
return true;
|
||||
}
|
||||
if (row.entity !== attr || !row.entityId) {
|
||||
|
|
@ -614,15 +709,15 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
return fn_3(node2, nodeDict, exprResolveFns);
|
||||
return fn_5(node2, nodeDict, exprResolveFns);
|
||||
});
|
||||
}
|
||||
else if (typeof relation_2 === 'string') {
|
||||
// 只能是基于普通属性的外键
|
||||
var fn_4 = this_2.translateFilter(relation_2, filter[attr], context, option);
|
||||
var fn_6 = this_1.translateFilter(relation_2, filter[attr], context, option);
|
||||
fns.push(function (node, nodeDict, exprResolveFns) {
|
||||
var row = _this.constructRow(node, context, option);
|
||||
if (obscurePass(row, "".concat(attr, "Id"), option)) {
|
||||
if (obscurePass(row["".concat(attr, "Id")], option)) {
|
||||
return true;
|
||||
}
|
||||
if (row["".concat(attr, "Id")]) {
|
||||
|
|
@ -633,7 +728,7 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
return fn_4(node2, nodeDict, exprResolveFns);
|
||||
return fn_6(node2, nodeDict, exprResolveFns);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
|
@ -644,9 +739,9 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
}
|
||||
}
|
||||
};
|
||||
var this_2 = this;
|
||||
var this_1 = this;
|
||||
for (var attr in filter) {
|
||||
_loop_2(attr);
|
||||
_loop_1(attr);
|
||||
}
|
||||
return function (node, nodeDict, exprResolveFns) {
|
||||
var _a, e_6, _b;
|
||||
|
|
@ -988,19 +1083,19 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
_c));
|
||||
}
|
||||
}
|
||||
var _loop_3 = function (attr) {
|
||||
var rel = this_3.judgeRelation(entity, attr);
|
||||
var _loop_2 = function (attr) {
|
||||
var rel = this_2.judgeRelation(entity, attr);
|
||||
if (rel === 1) {
|
||||
}
|
||||
else if (rel === 2) {
|
||||
if (data[attr]) {
|
||||
this_3.formExprInResult(attr, projection[attr], data[attr], nodeDict, context);
|
||||
this_2.formExprInResult(attr, projection[attr], data[attr], nodeDict, context);
|
||||
}
|
||||
}
|
||||
else if (typeof rel === 'string') {
|
||||
if (data[attr]) {
|
||||
var result2 = {};
|
||||
this_3.formExprInResult(rel, projection[attr], data[attr], nodeDict, context);
|
||||
this_2.formExprInResult(rel, projection[attr], data[attr], nodeDict, context);
|
||||
}
|
||||
}
|
||||
else if (rel instanceof Array) {
|
||||
|
|
@ -1011,9 +1106,9 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
}
|
||||
}
|
||||
};
|
||||
var this_3 = this;
|
||||
var this_2 = this;
|
||||
for (var attr in projection) {
|
||||
_loop_3(attr);
|
||||
_loop_2(attr);
|
||||
}
|
||||
for (var attr in laterExprDict) {
|
||||
var exprResult = laterExprDict[attr](nodeDict);
|
||||
|
|
@ -1025,7 +1120,7 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
}
|
||||
};
|
||||
TreeStore.prototype.formResult = function (entity, rows, selection, context, option) {
|
||||
var e_11, _a, _b, _c;
|
||||
var e_11, _a, _b;
|
||||
var _this = this;
|
||||
var data = selection.data, sorter = selection.sorter, indexFrom = selection.indexFrom, count = selection.count;
|
||||
var findAvailableExprName = function (current) {
|
||||
|
|
@ -1081,24 +1176,64 @@ var TreeStore = /** @class */ (function (_super) {
|
|||
for (var rows_1 = tslib_1.__values(rows), rows_1_1 = rows_1.next(); !rows_1_1.done; rows_1_1 = rows_1.next()) {
|
||||
var row = rows_1_1.value;
|
||||
var result = {};
|
||||
for (var attr in projection) {
|
||||
var rel = this.judgeRelation(entity, attr);
|
||||
var _loop_3 = function (attr) {
|
||||
var _c, _d;
|
||||
var rel = this_3.judgeRelation(entity, attr);
|
||||
if (rel === 1) {
|
||||
if (row[attr] === undefined) {
|
||||
incompletedRowIds.push(row.id);
|
||||
break;
|
||||
return "break";
|
||||
}
|
||||
else if (typeof projection[attr] === 'number') {
|
||||
Object.assign(result, (_c = {},
|
||||
_c[attr] = row[attr],
|
||||
_c));
|
||||
}
|
||||
else {
|
||||
Object.assign(result, (_b = {},
|
||||
_b[attr] = row[attr],
|
||||
_b));
|
||||
// object数据的深层次select
|
||||
Object.assign(result, (_d = {},
|
||||
_d[attr] = {},
|
||||
_d));
|
||||
var assignIner_1 = function (dest, proj, source) {
|
||||
if (proj instanceof Array) {
|
||||
(0, assert_1.assert)(dest instanceof Array);
|
||||
(0, assert_1.assert)(source instanceof Array);
|
||||
proj.forEach(function (attr, idx) {
|
||||
if (typeof attr === 'number') {
|
||||
dest[idx] = source[idx];
|
||||
}
|
||||
else if (typeof attr === 'object') {
|
||||
dest[idx] = {};
|
||||
assignIner_1(dest[idx], attr, source[idx]);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
for (var attr_1 in proj) {
|
||||
if (typeof proj[attr_1] === 'number') {
|
||||
dest[attr_1] = source[attr_1];
|
||||
}
|
||||
else if (typeof proj[attr_1] === 'object') {
|
||||
dest[attr_1] = proj[attr_1] instanceof Array ? [] : {};
|
||||
assignIner_1(dest[attr_1], proj[attr_1], source[attr_1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
assignIner_1(result[attr], projection[attr], row[attr]);
|
||||
}
|
||||
}
|
||||
};
|
||||
var this_3 = this;
|
||||
for (var attr in projection) {
|
||||
var state_1 = _loop_3(attr);
|
||||
if (state_1 === "break")
|
||||
break;
|
||||
}
|
||||
if (row.$$deleteAt$$) {
|
||||
Object.assign(result, (_c = {},
|
||||
_c[Entity_1.DeleteAtAttribute] = row.$$deleteAt$$,
|
||||
_c));
|
||||
Object.assign(result, (_b = {},
|
||||
_b[Entity_1.DeleteAtAttribute] = row.$$deleteAt$$,
|
||||
_b));
|
||||
}
|
||||
rows2.push(result);
|
||||
}
|
||||
|
|
|
|||
417
src/store.ts
417
src/store.ts
|
|
@ -1,4 +1,4 @@
|
|||
import { cloneDeep, get, groupBy, set, unset } from 'oak-domain/lib/utils/lodash';
|
||||
import { cloneDeep, get, groupBy, set, unset, difference, intersection } from 'oak-domain/lib/utils/lodash';
|
||||
import { assert } from 'oak-domain/lib/utils/assert';
|
||||
import {
|
||||
EntityShape, OperationResult, OperateOption, OpRecord,
|
||||
|
|
@ -27,8 +27,8 @@ interface ExprNodeTranslator {
|
|||
(row: any, nodeDict: NodeDict): ExpressionConstant | ExprLaterCheckFn;
|
||||
};
|
||||
|
||||
function obscurePass(row: any, attr: string, option?: SelectOption): boolean {
|
||||
return !!(option?.obscure && row[attr] === undefined);
|
||||
function obscurePass(value: any, option?: SelectOption): boolean {
|
||||
return !!(option?.obscure && value === undefined);
|
||||
}
|
||||
|
||||
class OakExpressionUnresolvedException<ED extends EntityDict & BaseEntityDict> extends OakException<ED> {
|
||||
|
|
@ -412,7 +412,7 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
const row = this.constructRow(node, context, option) as any;
|
||||
for (const attr of attributes) {
|
||||
const { name } = attr;
|
||||
if (row && row[name] && (typeof row[name] === 'string' && row[name].includes($search) || obscurePass(row, name as string, option))) {
|
||||
if (row && row[name] && (typeof row[name] === 'string' && row[name].includes($search) || obscurePass(row[name], option))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -420,6 +420,165 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
};
|
||||
}
|
||||
|
||||
private translatePredicate<OP extends TreeStoreSelectOption>(path: string, predicate: string, value: any, option?: OP): (row: Record<string, any>) => boolean {
|
||||
switch (predicate) {
|
||||
case '$gt': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data > value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$lt': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data < value || obscurePass(data, option);
|
||||
};
|
||||
}case '$gte': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data >= value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$lte': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data <= value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$eq': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data === value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$ne': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data !== value || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$between': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data >= value[0] && data <= value[1]|| obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$startsWith': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data.startsWith(value) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$endsWith': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data.endsWith(value) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$includes': {
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return data.includes(value) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$exists': {
|
||||
assert(typeof value === 'boolean');
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
if (value) {
|
||||
return ![null, undefined].includes(data) || obscurePass(data, option);
|
||||
}
|
||||
else {
|
||||
return [null, undefined].includes(data) || obscurePass(data, option);
|
||||
}
|
||||
};
|
||||
}
|
||||
case '$in': {
|
||||
assert(value instanceof Array);
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return value.includes(data) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$nin': {
|
||||
assert(value instanceof Array);
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return !value.includes(data) || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$contains': {
|
||||
// json中的多值查询
|
||||
assert(value instanceof Array);
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return difference(value, data).length === 0 || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
case '$overlaps': {
|
||||
// json中的多值查询
|
||||
assert(value instanceof Array);
|
||||
return (row) => {
|
||||
const data = get(row, path);
|
||||
return intersection(value, data).length > 0 || obscurePass(data, option);
|
||||
};
|
||||
}
|
||||
default: {
|
||||
throw new Error(`predicate ${predicate} is not recoganized`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private translateObjectPredicate(filter: Record<string, any>) {
|
||||
const fns: Array<(value: any) => boolean> = [];
|
||||
const translatePredicateInner = (p: Record<string, any>, path: string) => {
|
||||
const predicate = Object.keys(p)[0];
|
||||
if (predicate.startsWith('$')) {
|
||||
assert(Object.keys(p).length === 1);
|
||||
fns.push(
|
||||
this.translatePredicate(path, predicate, p[predicate])
|
||||
);
|
||||
}
|
||||
else {
|
||||
if (p instanceof Array) {
|
||||
p.forEach(
|
||||
(ele, idx) => {
|
||||
const path2 = `${path}[${idx}]`;
|
||||
if (typeof ele !== 'object') {
|
||||
if (![null, undefined].includes(ele)) {
|
||||
fns.push(this.translatePredicate(path2, '$eq', ele));
|
||||
}
|
||||
}
|
||||
else {
|
||||
translatePredicateInner(ele, path2);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
for (const attr in p) {
|
||||
const path2 = path ? `${path}.${attr}` : attr;
|
||||
if (typeof p[attr] !== 'object') {
|
||||
fns.push(this.translatePredicate(path2, '$eq', filter[attr]));
|
||||
}
|
||||
else {
|
||||
translatePredicateInner(p[attr], path2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
translatePredicateInner(filter, '');
|
||||
return (value: any) => {
|
||||
for (const fn of fns) {
|
||||
if (!fn(value)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private translateAttribute<T extends keyof ED, OP extends TreeStoreSelectOption, Cxt extends Context>(
|
||||
entity: T,
|
||||
filter: Q_NumberValue | Q_StringValue | Q_BooleanValue | Object | ED[T]['Selection'] & {
|
||||
|
|
@ -430,7 +589,7 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
option?: OP): (node: RowNode, nodeDict: NodeDict, exprResolveFns: Array<ExprResolveFn>) => boolean {
|
||||
// 如果是模糊查询且该属性为undefined,说明没取到,返回true
|
||||
function obscurePassLocal(row: any) {
|
||||
return obscurePass(row, attr, option);
|
||||
return obscurePass(row[attr], option);
|
||||
}
|
||||
if (typeof filter !== 'object') {
|
||||
return (node) => {
|
||||
|
|
@ -438,87 +597,15 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
return row ? (row as any)[attr] === filter || obscurePassLocal(row) : false;
|
||||
};
|
||||
}
|
||||
else if (this.getSchema()[entity].attributes[attr]?.type === 'object') {
|
||||
// 如果查询的目标就是object,则转化成object的比较
|
||||
return (node) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
return row ? JSON.stringify((row as any)[attr]) === JSON.stringify(filter) || obscurePassLocal(row) : false;
|
||||
};
|
||||
}
|
||||
const fns: Array<(row: any, nodeDict: NodeDict, exprResolveFns: Array<ExprResolveFn>) => boolean> = [];
|
||||
for (const op in filter) {
|
||||
switch (op) {
|
||||
case '$gt': {
|
||||
fns.push((row) => row && (row[attr] > (filter as any)[op]) || obscurePassLocal(row));
|
||||
break;
|
||||
}
|
||||
case '$lt': {
|
||||
fns.push((row) => row && (row[attr] < (filter as any)[op]) || obscurePassLocal(row));
|
||||
break;
|
||||
}
|
||||
case '$gte': {
|
||||
fns.push((row) => row && (row[attr] >= (filter as any)[op]) || obscurePassLocal(row));
|
||||
break;
|
||||
}
|
||||
case '$lte': {
|
||||
fns.push((row) => row && (row[attr] <= (filter as any)[op]) || obscurePassLocal(row));
|
||||
break;
|
||||
}
|
||||
case '$eq': {
|
||||
fns.push((row) => row && (row[attr] === (filter as any)[op]) || obscurePassLocal(row));
|
||||
break;
|
||||
}
|
||||
case '$ne': {
|
||||
fns.push((row) => row && (row[attr] !== (filter as any)[op]) || obscurePassLocal(row));
|
||||
break;
|
||||
}
|
||||
case '$between': {
|
||||
fns.push((row) => {
|
||||
return row && (row[attr] >= (filter as any)[op][0] && row[attr] <= (filter as any)[op][1] || obscurePassLocal(row));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$startsWith': {
|
||||
fns.push((row) => {
|
||||
return row && (row[attr]?.startsWith((filter as any)[op]) || obscurePassLocal(row));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$endsWith': {
|
||||
fns.push((row) => {
|
||||
return row && (row[attr]?.$endsWith((filter as any)[op]) || obscurePassLocal(row));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$includes': {
|
||||
fns.push((row) => {
|
||||
return row && (row[attr]?.includes((filter as any)[op]) || obscurePassLocal(row));
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$exists': {
|
||||
const exists = (filter as any)[op];
|
||||
assert(typeof exists === 'boolean');
|
||||
fns.push((row) => {
|
||||
if (exists) {
|
||||
return ![null, undefined].includes(row[attr]) || obscurePassLocal(row);
|
||||
}
|
||||
else {
|
||||
return [null, undefined].includes(row[attr]) || obscurePassLocal(row);
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case '$in': {
|
||||
const inData = (filter as any)[op];
|
||||
assert(typeof inData === 'object');
|
||||
if (inData instanceof Array) {
|
||||
fns.push((row) => inData.includes(row[attr]) || obscurePassLocal(row));
|
||||
}
|
||||
else {
|
||||
else {
|
||||
const predicate = Object.keys(filter)[0];
|
||||
if (predicate.startsWith('$')) {
|
||||
if (['$in', '$nin'].includes(predicate) && !((filter as Record<string, any>)[predicate] instanceof Array)) {
|
||||
const inData = (filter as Record<string, any>)[predicate];
|
||||
if (predicate === '$in') {
|
||||
// 如果是obscure,则返回的集合中有没有都不能否决“可能有”,所以可以直接返回true
|
||||
if (option?.obscure) {
|
||||
fns.push(() => true);
|
||||
return () => true;
|
||||
}
|
||||
else {
|
||||
// 这里只有当子查询中的filter不包含引用外部的子查询时才可以提前计算,否则必须等到执行时再计算
|
||||
|
|
@ -531,25 +618,31 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
}
|
||||
);
|
||||
|
||||
fns.push(
|
||||
(row) => legalSets.includes(row[attr])
|
||||
);
|
||||
return (node) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
return legalSets.includes((row as any)[attr]);
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof OakExpressionUnresolvedException) {
|
||||
fns.push(
|
||||
(row, nodeDict) => {
|
||||
const option2 = Object.assign({}, option, { nodeDict });
|
||||
const legalSets = this.selectAbjointRow(inData.entity, inData, context, option2).map(
|
||||
(ele) => {
|
||||
const { data } = inData as ED[keyof ED]['Selection'];
|
||||
const key = Object.keys(data)[0];
|
||||
return (ele as any)[key];
|
||||
}
|
||||
);
|
||||
return legalSets.includes(row[attr]);
|
||||
return (node, nodeDict) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
const option2 = Object.assign({}, option, { nodeDict });
|
||||
const legalSets = this.selectAbjointRow(inData.entity, inData, context, option2).map(
|
||||
(ele) => {
|
||||
const { data } = inData as ED[keyof ED]['Selection'];
|
||||
const key = Object.keys(data)[0];
|
||||
return (ele as any)[key];
|
||||
}
|
||||
);
|
||||
return legalSets.includes((row as any)[attr]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
|
|
@ -557,14 +650,6 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case '$nin': {
|
||||
const inData = (filter as any)[op];
|
||||
assert(typeof inData === 'object');
|
||||
if (inData instanceof Array) {
|
||||
fns.push((row) => !inData.includes(row[attr]) || obscurePassLocal(row));
|
||||
}
|
||||
else {
|
||||
// obscure对nin没有影响,如果返回的子查询结果中包含此行就一定是false,否则一定为true(obscure只考虑数据不完整,不考虑不准确),但若相应属性为undefined则任然可以认为true
|
||||
// 这里只有当子查询中的filter不包含引用外部的子查询时才可以提前计算,否则必须等到执行时再计算
|
||||
|
|
@ -576,49 +661,61 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
return (ele as any)[key];
|
||||
}
|
||||
);
|
||||
|
||||
fns.push(
|
||||
(row) => !legalSets.includes(row[attr]) || obscurePassLocal(row)
|
||||
);
|
||||
return (node) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
return !legalSets.includes((row as any)[attr]) || obscurePassLocal(row);
|
||||
};
|
||||
}
|
||||
catch (err) {
|
||||
if (err instanceof OakExpressionUnresolvedException) {
|
||||
fns.push(
|
||||
(row, nodeDict) => {
|
||||
const option2 = Object.assign({}, option, { nodeDict });
|
||||
const legalSets = this.selectAbjointRow(inData.entity, inData, context, option2).map(
|
||||
(ele) => {
|
||||
const { data } = inData as ED[keyof ED]['Selection'];
|
||||
const key = Object.keys(data)[0];
|
||||
return (ele as any)[key];
|
||||
}
|
||||
);
|
||||
return !legalSets.includes(row[attr]) || obscurePassLocal(row);
|
||||
return (node, nodeDict) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
);
|
||||
const option2 = Object.assign({}, option, { nodeDict });
|
||||
const legalSets = this.selectAbjointRow(inData.entity, inData, context, option2).map(
|
||||
(ele) => {
|
||||
const { data } = inData as ED[keyof ED]['Selection'];
|
||||
const key = Object.keys(data)[0];
|
||||
return (ele as any)[key];
|
||||
}
|
||||
);
|
||||
return !legalSets.includes((row as any)[attr]) || obscurePassLocal(row);
|
||||
};
|
||||
}
|
||||
else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false, `目前不支持的算子${op}`);
|
||||
}
|
||||
}
|
||||
return (node, nodeDict, exprResolveFns) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
for (const fn of fns) {
|
||||
if (fn(row, nodeDict, exprResolveFns) === false) {
|
||||
return false;
|
||||
else {
|
||||
const fn = this.translatePredicate(attr, predicate, (filter as Record<string, any>)[predicate], option);
|
||||
return (node) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
return fn(row);
|
||||
};
|
||||
}
|
||||
}
|
||||
return true;
|
||||
else {
|
||||
// 对象的内部查询
|
||||
assert(this.getSchema()[entity].attributes[attr]?.type === 'object');
|
||||
const fn = this.translateObjectPredicate(filter);
|
||||
return (node) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
if (!row) {
|
||||
return false;
|
||||
}
|
||||
return fn((row as any)[attr]) || obscurePassLocal(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -672,7 +769,7 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
fns.push(
|
||||
(node, nodeDict, exprResolveFns) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
if (obscurePass(row, 'entity', option) || obscurePass(row, 'entityId', option)) {
|
||||
if (obscurePass((row as any).entity, option) || obscurePass((row as any).entityId, option)) {
|
||||
return true;
|
||||
}
|
||||
if ((row as any).entity !== attr || !(row as any).entityId) {
|
||||
|
|
@ -695,7 +792,7 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
fns.push(
|
||||
(node, nodeDict, exprResolveFns) => {
|
||||
const row = this.constructRow(node, context, option);
|
||||
if (obscurePass(row, `${attr}Id`, option)) {
|
||||
if (obscurePass((row as any)[`${attr}Id`], option)) {
|
||||
return true;
|
||||
}
|
||||
if ((row as any)[`${attr}Id`]) {
|
||||
|
|
@ -1142,7 +1239,7 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
const incompletedRowIds: string[] = [];
|
||||
const { data: projection } = selection;
|
||||
for (const row of rows) {
|
||||
const result = {};
|
||||
const result = {} as Partial<ED[T]['Schema']>;
|
||||
for (const attr in projection) {
|
||||
const rel = this.judgeRelation(entity, attr);
|
||||
if (rel === 1) {
|
||||
|
|
@ -1150,11 +1247,47 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
incompletedRowIds.push(row.id!);
|
||||
break;
|
||||
}
|
||||
else {
|
||||
else if (typeof projection[attr] === 'number') {
|
||||
Object.assign(result, {
|
||||
[attr]: row[attr],
|
||||
});
|
||||
}
|
||||
else {
|
||||
// object数据的深层次select
|
||||
Object.assign(result, {
|
||||
[attr]: {},
|
||||
});
|
||||
|
||||
const assignIner = (dest: Record<string, any> | Array<any>, proj: Record<string, any> | Array<any>, source: Record<string, any> | Array<any>) => {
|
||||
if (proj instanceof Array) {
|
||||
assert(dest instanceof Array);
|
||||
assert(source instanceof Array);
|
||||
proj.forEach(
|
||||
(attr, idx) => {
|
||||
if (typeof attr === 'number') {
|
||||
dest[idx] = source[idx];
|
||||
}
|
||||
else if (typeof attr === 'object') {
|
||||
dest[idx] = {};
|
||||
assignIner(dest[idx], attr, source[idx]);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
for (const attr in proj) {
|
||||
if (typeof proj[attr] === 'number') {
|
||||
(<Record<string, any>>dest)[attr] = (<Record<string, any>>source)[attr];
|
||||
}
|
||||
else if (typeof proj[attr] === 'object') {
|
||||
(<Record<string, any>>dest)[attr] = proj[attr] instanceof Array ? [] : {};
|
||||
assignIner((<Record<string, any>>dest)[attr], proj[attr], (<Record<string, any>>source)[attr]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
assignIner(result[attr]!, projection[attr], row[attr]!);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (row.$$deleteAt$$) {
|
||||
|
|
@ -1162,7 +1295,7 @@ export default class TreeStore<ED extends EntityDict & BaseEntityDict> extends C
|
|||
[DeleteAtAttribute]: row.$$deleteAt$$,
|
||||
})
|
||||
}
|
||||
rows2.push(result as Partial<ED[T]['Schema']>);
|
||||
rows2.push(result);
|
||||
}
|
||||
if (incompletedRowIds.length > 0) {
|
||||
// 如果有缺失属性的行,则报OakRowUnexistedException错误
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import TreeStore from '../src/store';
|
|||
import { EntityDict } from 'oak-domain/lib/base-app-domain';
|
||||
import { SyncContext, SyncRowStore } from "oak-domain/lib/store/SyncRowStore";
|
||||
import { OperateOption, OperationResult, SelectOption, AggregationResult, TxnOption, StorageSchema } from "oak-domain/lib/types";
|
||||
import { reinforceSelection } from 'oak-domain/lib/store/selection';
|
||||
|
||||
/**
|
||||
* 实现一个同步的context和store用于测试
|
||||
|
|
@ -27,7 +26,6 @@ export class FrontendStore extends TreeStore<EntityDict> implements SyncRowStore
|
|||
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"]>[] {
|
||||
reinforceSelection(this.storageSchema, entity, selection);
|
||||
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 {
|
||||
|
|
|
|||
269
test/test.ts
269
test/test.ts
|
|
@ -612,6 +612,7 @@ describe('基础测试', function () {
|
|||
assert(modies.length === 2 && modies[0].modiEntity$modi!.length === 1);
|
||||
|
||||
store.operate('modiEntity', {
|
||||
id: generateNewId(),
|
||||
action: 'remove',
|
||||
data: {},
|
||||
filter: {
|
||||
|
|
@ -818,6 +819,272 @@ describe('基础测试', function () {
|
|||
}, 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, {});
|
||||
|
||||
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);
|
||||
// console.log(JSON.stringify(row7));
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue