UNPKG

@wener/miniquery

Version:

SQL Where like **safe** filter expression for ORM.

334 lines (333 loc) 11.3 kB
/* eslint-disable @typescript-eslint/no-non-null-assertion */ function _array_like_to_array(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i]; return arr2; } function _array_without_holes(arr) { if (Array.isArray(arr)) return _array_like_to_array(arr); } function _define_property(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _iterable_to_array(iter) { if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter); } function _non_iterable_spread() { throw new TypeError("Invalid attempt to spread non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } function _object_spread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === "function") { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _define_property(target, key, source[key]); }); } return target; } function _to_consumable_array(arr) { return _array_without_holes(arr) || _iterable_to_array(arr) || _unsupported_iterable_to_array(arr) || _non_iterable_spread(); } function _unsupported_iterable_to_array(o, minLen) { if (!o) return; if (typeof o === "string") return _array_like_to_array(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(n); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen); } import { col, DataTypes, fn, Op, where } from "@sequelize/core"; import { toMiniQueryAST } from "../ast.js"; export function toSequelizeWhere(s, o) { var _ctx_include; var ctx = _object_spread({ functions: DefaultFunctions, include: [], associations: [] }, o); if (!s) { return { where: {}, include: [] }; } var where = toWhere(toMiniQueryAST(s), ctx); (_ctx_include = ctx.include).push.apply(_ctx_include, _to_consumable_array(ctx.associations)); return { where: where, include: ctx.include }; } var DefaultFunctions = { date: { arity: 1 }, length: { arity: 1 } }; function checkFunctions(name) { var args = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : [], o = arguments.length > 2 ? arguments[2] : void 0; if (!o.functions[name]) { throw new Error("Invalid function: ".concat(name)); } } function isSameType(a, b) { return a === b || keyOfDataType(a) === keyOfDataType(b); } function keyOfDataType(d) { if (typeof d === "string") { return d.toUpperCase(); } return d; // if ('key' in d) { // // v7 removed // // return d.key; // // return d.usageContext?.model.name // return d // } // // fixme // return d.name; } function resolve(parts, c) { var Model = c.Model; var M = Model; var rest = _to_consumable_array(parts); var all = []; var isAssoc = false; var include; while (rest.length) { var first = rest.shift(); var attr = M.getAttributes()[first]; if (attr) { if (isSameType(attr.type, DataTypes.JSON) || isSameType(attr.type, DataTypes.JSONB)) { var _all; all.push(attr.field); (_all = all).push.apply(_all, _to_consumable_array(rest)); break; } else if (rest.length === 0) { all.push(attr.field); break; } else { throw new SyntaxError("Invalid ref of attr: ".concat(parts.join("."), " type of ").concat(attr.field, " is ").concat(attr.type)); } } else if (rest.length === parts.length - 1) { // first // User.createdAt if (first === M.name) { continue; } } var assoc = M.associations[first]; if (assoc) { if (assoc.isMultiAssociation) { throw new Error("Use has for multi association: ".concat(parts.join("."))); } if (!include) { include = { association: assoc, required: true }; c.include.push(include); } else { // nested include.include = [ { association: assoc, required: true } ]; } all.push(first); M = assoc.target; if (!rest.length) { throw new SyntaxError("Invalid ref of an association: ".concat(parts.join("."))); } isAssoc = true; continue; } throw new SyntaxError("Invalid ref: ".concat(parts.join("."), " of model ").concat(M.name)); } if (isAssoc) { return "$".concat(all.join("."), "$"); } return all.join("."); } function toWhere(ast, o) { var _o_current; var current = (_o_current = o.current) !== null && _o_current !== void 0 ? _o_current : {}; var Model = o.Model; function attrOfIdentifier(name) { // can support auto case convert var attr = Model.getAttributes()[name]; if (!(attr === null || attr === void 0 ? void 0 : attr.field)) { throw new Error("Invalid attribute: ".concat(name)); } return attr; } var setOp = function (left, op, val) { if (typeof op === "string") { op = opOf(op); } var v; switch (left.type) { case "identifier": var _current, _attrOfIdentifier_field; var _; v = (_ = (_current = current)[_attrOfIdentifier_field = attrOfIdentifier(left.name).field]) !== null && _ !== void 0 ? _ : _current[_attrOfIdentifier_field] = {}; break; case "call": { var first = left.value[0]; if (left.value.length !== 1) { throw new SyntaxError("Expected 1 call args, got ".concat(left.value.length)); } var cn; switch (first.type) { case "identifier": cn = attrOfIdentifier(first.name).field; break; case "ref": // fixme hack replace cn = resolve(first.name, o).replaceAll("$", "`"); break; default: throw new SyntaxError("Expected identifier or ref for call, got ".concat(first.type)); } var name = left.name.toLowerCase(); checkFunctions(name, [ first ], o); current = where(fn(name, col(cn)), _define_property({}, op, val)); return; } case "ref": var _current1, _resolve; var _1; // 依然无法 escape `.` // https://sequelize.org/docs/v7/core-concepts/model-querying-basics/#querying-json // v = _.get(current, left.name); // if (!v) { // v = {}; // _.set(current, left.name, v); // } v = (_1 = (_current1 = current)[_resolve = resolve(left.name, o)]) !== null && _1 !== void 0 ? _1 : _current1[_resolve] = {}; break; default: throw new SyntaxError("Expected identifier, ref or call for left side, got ".concat(left.type)); } v[op] = val; }; switch (ast.type) { case "rel": { setOp(ast.a, ast.op, toWhere(ast.b, _object_spread({}, o))); } break; case "between": { setOp(ast.a, ast.op, [ toWhere(ast.b, _object_spread({}, o)), toWhere(ast.c, _object_spread({}, o)) ]); } break; case "logic": { var _current, _op; var op = opOf(ast.op); var _; var c = (_ = (_current = current)[_op = op]) !== null && _ !== void 0 ? _ : _current[_op] = []; // collect - flat - optimize var a = toWhere(ast.a, _object_spread({}, o)); if (a[op]) { var _c; (_c = c).push.apply(_c, _to_consumable_array(a[op])); } else { c.push(a); } var b = toWhere(ast.b, _object_spread({}, o)); if (b[op]) { var _c1; (_c1 = c).push.apply(_c1, _to_consumable_array(b[op])); } else { c.push(b); } } break; case "paren": return [ toWhere(ast.value, _object_spread({}, o)) ]; case "int": case "string": case "float": return ast.value; case "null": return null; case "call": { var name = ast.name.toLowerCase(); checkFunctions(name, ast.value, o); return fn.apply(void 0, [ name ].concat(_to_consumable_array(ast.value.map(function (v) { return toWhere(v, _object_spread({}, o)); })))); } case "identifier": { var attr = attrOfIdentifier(ast.name); return col(attr.field); } default: throw new SyntaxError("Invalid type: ".concat(ast.type)); } return current; } var OpMap = { "not between": "notBetween", "not like": "notLike", ilike: "iLike", "not ilike": "notILike", "not in": "notIn", "is not": "not" }; function opOf(v) { var op = Op[OpMap[v] || v]; if (!op) { throw new SyntaxError("Unsupported operator: ".concat(v)); } return op; }