diff --git a/lib/sqlTranslator.js b/lib/sqlTranslator.js index d0b49af..b02a565 100644 --- a/lib/sqlTranslator.js +++ b/lib/sqlTranslator.js @@ -6,6 +6,7 @@ var assert_1 = tslib_1.__importDefault(require("assert")); var lodash_1 = require("lodash"); var types_1 = require("oak-domain/lib/types"); var relation_1 = require("oak-domain/lib/store/relation"); +var filter_1 = require("oak-domain/lib/store/filter"); ; ; var SqlTranslator = /** @class */ (function () { @@ -207,7 +208,6 @@ var SqlTranslator = /** @class */ (function () { var number = initialNumber || 1; var projectionRefAlias = {}; var filterRefAlias = {}; - var extraWhere = ''; var alias = "".concat(entity, "_").concat(number++); var from = " `".concat(this.getStorageName(entity), "` `").concat(alias, "` "); var aliasDict = { @@ -280,7 +280,7 @@ var SqlTranslator = /** @class */ (function () { } else { // 不支持一对多 - (0, assert_1.default)(rel === 0 || rel === 1); + // assert(rel === 0 || rel === 1); } } }); @@ -431,7 +431,6 @@ var SqlTranslator = /** @class */ (function () { from: from, projectionRefAlias: projectionRefAlias, filterRefAlias: filterRefAlias, - extraWhere: extraWhere, currentNumber: number, }; }; @@ -482,12 +481,13 @@ var SqlTranslator = /** @class */ (function () { }; } else { + (0, assert_1.default)(false, '子查询已经改写为一对多的形式'); // sub query - var _a = this.translateSelectInner(value.entity, value, initialNumber, refAlias, undefined), subQueryStmt = _a.stmt, currentNumber = _a.currentNumber; + /* const { stmt: subQueryStmt, currentNumber } = this.translateSelectInner(value.entity, value, initialNumber, refAlias, undefined); return { - stmt: " ".concat(IN_OP[attr], "(").concat(subQueryStmt, ")"), - currentNumber: currentNumber, - }; + stmt: ` ${IN_OP[attr]}(${subQueryStmt})`, + currentNumber, + }; */ } } default: { @@ -541,10 +541,9 @@ var SqlTranslator = /** @class */ (function () { return ' is null'; } }; - SqlTranslator.prototype.translateFilter = function (entity, selection, aliasDict, filterRefAlias, initialNumber, extraWhere, option) { + SqlTranslator.prototype.translateFilter = function (entity, filter, aliasDict, filterRefAlias, initialNumber, option) { var _this = this; var schema = this.schema; - var filter = selection.filter; var currentNumber = initialNumber; var translateInner = function (entity2, path, filter2, type) { var alias = aliasDict[path]; @@ -553,6 +552,7 @@ var SqlTranslator = /** @class */ (function () { if (filter2) { var attrs = Object.keys(filter2).filter(function (ele) { return !ele.startsWith('#'); }); attrs.forEach(function (attr) { + var _a; if (whereText) { whereText += ' and '; } @@ -601,27 +601,90 @@ var SqlTranslator = /** @class */ (function () { else if (typeof rel === 'string') { whereText += " (".concat(translateInner(rel, "".concat(path).concat(attr, "/"), filter2[attr]), ")"); } + else if (rel instanceof Array) { + var subEntity = rel[0], foreignKey = rel[1]; + var predicate = (filter2[attr]['#sqp'] || 'in'); + /** + * + * in代表外键连接后至少有一行数据 + * not in代表外键连接后一行也不能有 + * all代表反外键连接条件的一行也不能有(符合的是否至少要有一行?直觉上没这个限制) + * not all 代表反外键连接条件的至少有一行 + * + * 目前将这种子查询翻译成了exists查询,当外表很大而子查询结果集很小时可能有性能问题,取决于MySQL执行器的能力 + * by Xc 20230726 + */ + var refAlia = Object.keys(filterRefAlias).find(function (ele) { return ele[0] === alias; }); + var refAlia2 = refAlia || alias; // alias一定是唯一的,可以用来作为node id + if (!refAlia) { + (0, assert_1.default)(!filterRefAlias[refAlia2]); + Object.assign(filterRefAlias, (_a = {}, + _a[refAlia2] = [alias, entity2], + _a)); + } + var fk = foreignKey || 'entityId'; + var joinFilter = ['not in', 'in'].includes(predicate) ? { + $expr12: { + $eq: [ + { + '#attr': fk, + }, + { + '#refId': refAlia2, + '#refAttr': 'id', + } + ], + } + } : { + $expr12: { + $ne: [ + { + '#attr': fk, + }, + { + '#refId': refAlia2, + '#refAttr': 'id', + } + ], + } + }; + if (!foreignKey) { + Object.assign(joinFilter, { + entity: entity2, + }); + } + var _b = _this.translateSelectInner(subEntity, { + data: { + id: 1, + }, + filter: (0, filter_1.combineFilters)([joinFilter, filter2[attr]]), + indexFrom: 0, + count: 1, + }, currentNumber, filterRefAlias, option), stmt = _b.stmt, ct2 = _b.currentNumber; + currentNumber = ct2; + var PREDICATE_DICT = { + 'in': 'exists', + 'not in': 'not exists', + 'all': 'not exists', + 'not all': 'exists', + }; + whereText += " ".concat(PREDICATE_DICT[predicate], " (").concat(stmt, ")"); + } else { (0, assert_1.default)(attributes.hasOwnProperty(attr), "\u975E\u6CD5\u7684\u5C5E\u6027".concat(attr)); var type2 = attributes[attr].type; // assert (type2 !== 'ref'); if (typeof filter2[attr] === 'object') { - var predicate = Object.keys(filter2[attr])[0]; - if (predicate.startsWith('$')) { - // 对属性上的谓词处理 - if (['$in', '$nin'].includes(predicate)) { - var _a = _this.translateEvaluation(predicate, filter2[attr][predicate], entity2, alias, type, initialNumber, filterRefAlias), stmt = _a.stmt, cn2 = _a.currentNumber; - whereText += " (`".concat(alias, "`.`").concat(attr, "` ").concat(stmt, ")"); - currentNumber = cn2; - } - else { - whereText += " (`".concat(alias, "`.`").concat(attr, "` ").concat(_this.translatePredicate(predicate, filter2[attr][predicate], type), ")"); - } + if (['object', 'array'].includes(type2)) { + // 对object数据的深层次查询,这里调用数据库所支持的属性对象级查询,如mysql中的json查询 + whereText += "(".concat(_this.translateObjectPredicate(filter2[attr], alias, attr), ")"); } else { - // 对object数据的深层次查询,这里必须要假设该数据是json - (0, assert_1.default)(['object', 'array'].includes(type2)); - whereText += "(".concat(_this.translateObjectPredicate(filter2[attr], alias, attr), ")"); + (0, assert_1.default)(Object.keys(filter2[attr]).length === 1); + var predicate = Object.keys(filter2[attr])[0]; + (0, assert_1.default)(predicate.startsWith('$')); + // 对属性上的谓词处理 + whereText += " (`".concat(alias, "`.`").concat(attr, "` ").concat(_this.translatePredicate(predicate, filter2[attr][predicate], type2), ")"); } } else { @@ -637,14 +700,8 @@ var SqlTranslator = /** @class */ (function () { return whereText; }; var where = translateInner(entity, './', filter); - if (extraWhere && where) { - return { - stmt: "".concat(extraWhere, " and ").concat(where), - currentNumber: currentNumber, - }; - } return { - stmt: extraWhere || where, + stmt: where, currentNumber: currentNumber, }; }; @@ -781,11 +838,11 @@ var SqlTranslator = /** @class */ (function () { projection: data, filter: filter, sorter: sorter, - }, initialNumber), fromText = _a.from, aliasDict = _a.aliasDict, projectionRefAlias = _a.projectionRefAlias, extraWhere = _a.extraWhere, filterRefAlias = _a.filterRefAlias, currentNumber = _a.currentNumber; + }, initialNumber), fromText = _a.from, aliasDict = _a.aliasDict, projectionRefAlias = _a.projectionRefAlias, filterRefAlias = _a.filterRefAlias, currentNumber = _a.currentNumber; (0, assert_1.default)((0, lodash_1.intersection)((0, lodash_1.keys)(refAlias), (0, lodash_1.keys)(filterRefAlias)).length === 0, 'filter中的#node结点定义有重复'); (0, lodash_1.assign)(refAlias, filterRefAlias); var projText = this.translateProjection(entity, data, aliasDict, projectionRefAlias).projText; - var _b = this.translateFilter(entity, selection, aliasDict, refAlias, currentNumber, extraWhere, option), filterText = _b.stmt, currentNumber2 = _b.currentNumber; + var _b = this.translateFilter(entity, filter, aliasDict, refAlias, currentNumber, option), filterText = _b.stmt, currentNumber2 = _b.currentNumber; var sorterText = sorter && this.translateSorter(entity, sorter, aliasDict); return { stmt: this.populateSelectStmt(projText, fromText, aliasDict, filterText, sorterText, undefined, indexFrom, count, option, selection), @@ -807,7 +864,7 @@ var SqlTranslator = /** @class */ (function () { aggregation: data, filter: filter, sorter: sorter, - }, 1), fromText = _a.from, aliasDict = _a.aliasDict, projectionRefAlias = _a.projectionRefAlias, extraWhere = _a.extraWhere, filterRefAlias = _a.filterRefAlias, currentNumber = _a.currentNumber; + }, 1), fromText = _a.from, aliasDict = _a.aliasDict, projectionRefAlias = _a.projectionRefAlias, filterRefAlias = _a.filterRefAlias, currentNumber = _a.currentNumber; var projText = ''; var groupByText = ''; for (var k in data) { @@ -848,7 +905,7 @@ var SqlTranslator = /** @class */ (function () { } } } - var filterText = this.translateFilter(entity, aggregation, aliasDict, {}, currentNumber, extraWhere, option).stmt; + var filterText = this.translateFilter(entity, filter, aliasDict, {}, currentNumber, option).stmt; var sorterText = sorter && this.translateSorter(entity, sorter, aliasDict); return this.populateSelectStmt(projText, fromText, aliasDict, filterText, sorterText, groupByText, indexFrom, count, option, undefined, aggregation); }; @@ -856,9 +913,9 @@ var SqlTranslator = /** @class */ (function () { var filter = selection.filter, count = selection.count; var _a = this.analyzeJoin(entity, { filter: filter, - }), fromText = _a.from, aliasDict = _a.aliasDict, extraWhere = _a.extraWhere, filterRefAlias = _a.filterRefAlias, currentNumber = _a.currentNumber; + }), fromText = _a.from, aliasDict = _a.aliasDict, filterRefAlias = _a.filterRefAlias, currentNumber = _a.currentNumber; var projText = 'count(1) cnt'; - var filterText = this.translateFilter(entity, selection, aliasDict, filterRefAlias, currentNumber, extraWhere, option).stmt; + var filterText = this.translateFilter(entity, filter, aliasDict, filterRefAlias, currentNumber, option).stmt; if (count) { var subQuerySql = this.populateSelectStmt('1', fromText, aliasDict, filterText, undefined, undefined, undefined, undefined, option, Object.assign({}, selection, { indexFrom: 0, count: count })); return "select count(1) cnt from (".concat(subQuerySql, ") __tmp"); @@ -868,9 +925,9 @@ var SqlTranslator = /** @class */ (function () { SqlTranslator.prototype.translateRemove = function (entity, operation, option) { var filter = operation.filter, sorter = operation.sorter, indexFrom = operation.indexFrom, count = operation.count; (0, assert_1.default)(!sorter, '当前remove不支持sorter行为'); - var _a = this.analyzeJoin(entity, { filter: filter, sorter: sorter }), aliasDict = _a.aliasDict, filterRefAlias = _a.filterRefAlias, extraWhere = _a.extraWhere, fromText = _a.from, currentNumber = _a.currentNumber; + var _a = this.analyzeJoin(entity, { filter: filter, sorter: sorter }), aliasDict = _a.aliasDict, filterRefAlias = _a.filterRefAlias, fromText = _a.from, currentNumber = _a.currentNumber; var alias = aliasDict['./']; - var filterText = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere, { includedDeleted: true }).stmt; + var filterText = this.translateFilter(entity, filter, aliasDict, filterRefAlias, currentNumber, { includedDeleted: true }).stmt; // const sorterText = sorter && sorter.length > 0 ? this.translateSorter(entity, sorter, aliasDict) : undefined; return this.populateRemoveStmt(alias, fromText, aliasDict, filterText, /* sorterText */ undefined, indexFrom, count, option); }; @@ -878,7 +935,7 @@ var SqlTranslator = /** @class */ (function () { var attributes = this.schema[entity].attributes; var filter = operation.filter, sorter = operation.sorter, indexFrom = operation.indexFrom, count = operation.count, data = operation.data; (0, assert_1.default)(!sorter, '当前update不支持sorter行为'); - var _a = this.analyzeJoin(entity, { filter: filter, sorter: sorter }), aliasDict = _a.aliasDict, filterRefAlias = _a.filterRefAlias, extraWhere = _a.extraWhere, fromText = _a.from, currentNumber = _a.currentNumber; + var _a = this.analyzeJoin(entity, { filter: filter, sorter: sorter }), aliasDict = _a.aliasDict, filterRefAlias = _a.filterRefAlias, fromText = _a.from, currentNumber = _a.currentNumber; var alias = aliasDict['./']; var updateText = ''; for (var attr in data) { @@ -889,7 +946,7 @@ var SqlTranslator = /** @class */ (function () { var value = this.translateAttrValue(attributes[attr].type, data[attr]); updateText += "`".concat(alias, "`.`").concat(attr, "` = ").concat(value); } - var filterText = this.translateFilter(entity, operation, aliasDict, filterRefAlias, currentNumber, extraWhere).stmt; + var filterText = this.translateFilter(entity, filter, aliasDict, filterRefAlias, currentNumber).stmt; // const sorterText = sorter && this.translateSorter(entity, sorter, aliasDict); return this.populateUpdateStmt(updateText, fromText, aliasDict, filterText, /* sorterText */ undefined, indexFrom, count, option); }; diff --git a/src/sqlTranslator.ts b/src/sqlTranslator.ts index 5e017c5..2a252a3 100644 --- a/src/sqlTranslator.ts +++ b/src/sqlTranslator.ts @@ -838,7 +838,7 @@ export abstract class SqlTranslator { const predicate = Object.keys(filter2[attr])[0]; assert(predicate.startsWith('$')); // 对属性上的谓词处理 - whereText += ` (\`${alias}\`.\`${attr}\` ${this.translatePredicate(predicate, filter2[attr][predicate], type)})`; + whereText += ` (\`${alias}\`.\`${attr}\` ${this.translatePredicate(predicate, filter2[attr][predicate], type2)})`; } } else {