UNPKG

@mikro-orm/knex

Version:

TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.

122 lines (121 loc) 5.75 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.CriteriaNode = void 0; const node_util_1 = require("node:util"); const core_1 = require("@mikro-orm/core"); /** * Helper for working with deeply nested where/orderBy/having criteria. Uses composite pattern to build tree from the payload. * Auto-joins relations and converts payload from { books: { publisher: { name: '...' } } } to { 'publisher_alias.name': '...' } * @internal */ class CriteriaNode { metadata; entityName; parent; key; payload; prop; index; constructor(metadata, entityName, parent, key, validate = true) { this.metadata = metadata; this.entityName = entityName; this.parent = parent; this.key = key; const meta = parent && metadata.find(parent.entityName); if (meta && key) { const pks = core_1.Utils.splitPrimaryKeys(key); if (pks.length > 1) { return; } for (const k of pks) { this.prop = meta.props.find(prop => prop.name === k || (prop.fieldNames?.length === 1 && prop.fieldNames[0] === k && prop.persist !== false)); const isProp = this.prop || meta.props.find(prop => (prop.fieldNames || []).includes(k)); // do not validate if the key is prefixed or type casted (e.g. `k::text`) if (validate && !isProp && !k.includes('.') && !k.includes('::') && !core_1.Utils.isOperator(k) && !core_1.RawQueryFragment.isKnownFragment(k)) { throw new Error(`Trying to query by not existing property ${entityName}.${k}`); } } } } process(qb, options) { return this.payload; } unwrap() { return this.payload; } shouldInline(payload) { return false; } willAutoJoin(qb, alias, options) { return false; } shouldRename(payload) { const type = this.prop ? this.prop.kind : null; const composite = this.prop?.joinColumns ? this.prop.joinColumns.length > 1 : false; const customExpression = core_1.RawQueryFragment.isKnownFragment(this.key); const scalar = payload === null || core_1.Utils.isPrimaryKey(payload) || payload instanceof RegExp || payload instanceof Date || customExpression; const plainObject = core_1.Utils.isPlainObject(payload); const keys = plainObject ? Object.keys(payload) : []; const operator = plainObject && keys.every(k => core_1.Utils.isOperator(k, false)); if (composite) { return true; } switch (type) { case core_1.ReferenceKind.MANY_TO_ONE: return false; case core_1.ReferenceKind.ONE_TO_ONE: return !this.prop.owner; case core_1.ReferenceKind.ONE_TO_MANY: return scalar || operator; case core_1.ReferenceKind.MANY_TO_MANY: return scalar || operator; default: return false; } } renameFieldToPK(qb, ownerAlias) { const joinAlias = qb.getAliasForJoinPath(this.getPath(), { matchPopulateJoins: true }); if (!joinAlias && this.parent && [core_1.ReferenceKind.MANY_TO_ONE, core_1.ReferenceKind.ONE_TO_ONE].includes(this.prop.kind) && this.prop.owner) { const alias = qb.getAliasForJoinPath(this.parent.getPath()) ?? ownerAlias ?? qb.alias; return core_1.Utils.getPrimaryKeyHash(this.prop.joinColumns.map(col => `${alias}.${col}`)); } const alias = joinAlias ?? ownerAlias ?? qb.alias; if (this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY) { return core_1.Utils.getPrimaryKeyHash(this.prop.inverseJoinColumns.map(col => `${alias}.${col}`)); } return core_1.Utils.getPrimaryKeyHash(this.prop.referencedColumnNames.map(col => `${alias}.${col}`)); } getPath(addIndex = false) { // use index on parent only if we are processing to-many relation const addParentIndex = this.prop && [core_1.ReferenceKind.ONE_TO_MANY, core_1.ReferenceKind.MANY_TO_MANY].includes(this.prop.kind); const parentPath = this.parent?.getPath(addParentIndex) ?? this.entityName; const index = addIndex && this.index != null ? `[${this.index}]` : ''; // ignore group operators to allow easier mapping (e.g. for orderBy) const key = this.key && !['$and', '$or', '$not'].includes(this.key) ? '.' + this.key : ''; const ret = parentPath + index + key; if (this.isPivotJoin()) { // distinguish pivot table join from target entity join return this.getPivotPath(ret); } return ret; } isPivotJoin() { if (!this.key || !this.prop) { return false; } const customExpression = core_1.RawQueryFragment.isKnownFragment(this.key); const scalar = this.payload === null || core_1.Utils.isPrimaryKey(this.payload) || this.payload instanceof RegExp || this.payload instanceof Date || customExpression; const operator = core_1.Utils.isObject(this.payload) && Object.keys(this.payload).every(k => core_1.Utils.isOperator(k, false)); return this.prop.kind === core_1.ReferenceKind.MANY_TO_MANY && (scalar || operator); } getPivotPath(path) { return `${path}[pivot]`; } aliased(field, alias) { return alias ? `${alias}.${field}` : field; } /** @ignore */ [node_util_1.inspect.custom]() { const o = {}; ['entityName', 'key', 'index', 'payload'] .filter(k => this[k] !== undefined) .forEach(k => o[k] = this[k]); return `${this.constructor.name} ${(0, node_util_1.inspect)(o)}`; } } exports.CriteriaNode = CriteriaNode;