UNPKG

@forestadmin/datasource-toolkit

Version:
121 lines 16.2 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const schema_1 = __importDefault(require("./schema")); const factory_1 = __importDefault(require("../interfaces/query/condition-tree/factory")); const factory_2 = __importDefault(require("../interfaces/query/filter/factory")); const unpaginated_1 = __importDefault(require("../interfaces/query/filter/unpaginated")); const projection_1 = __importDefault(require("../interfaces/query/projection")); class CollectionUtils { static getFieldSchema(collection, path) { const { fields } = collection.schema; const index = path.indexOf(':'); if (index === -1) { schema_1.default.throwIfMissingField(collection.schema, path, collection.name); return fields[path]; } const associationName = path.substring(0, index); const schema = schema_1.default.getRelation(collection.schema, associationName, collection.name); if (schema.type !== 'ManyToOne' && schema.type !== 'OneToOne') { throw new Error(`Unexpected field type '${schema.type}': '${collection.name}.${associationName}'`); } return CollectionUtils.getFieldSchema(collection.dataSource.getCollection(schema.foreignCollection), path.substring(index + 1)); } static getInverseRelation(collection, relationName) { const relation = schema_1.default.getRelation(collection.schema, relationName, collection.name); const foreignCollection = collection.dataSource.getCollection(relation.foreignCollection); const inverse = Object.entries(foreignCollection.schema.fields).find(([, field]) => { const isManyToManyInverse = field.type === 'ManyToMany' && relation.type === 'ManyToMany' && field.originKey === relation.foreignKey && field.throughCollection === relation.throughCollection && field.foreignKey === relation.originKey; const isManyToOneInverse = field.type === 'ManyToOne' && (relation.type === 'OneToMany' || relation.type === 'OneToOne') && field.foreignKey === relation.originKey; const isOtherInverse = (field.type === 'OneToMany' || field.type === 'OneToOne') && relation.type === 'ManyToOne' && field.originKey === relation.foreignKey; return ((isManyToManyInverse || isManyToOneInverse || isOtherInverse) && field.foreignCollection === collection.name); }); return inverse ? inverse[0] : null; } static getThroughOrigin(collection, relationName) { const relation = schema_1.default.getRelation(collection.schema, relationName, collection.name); if (relation.type !== 'ManyToMany') throw new Error('Relation must be many to many'); const throughCollection = collection.dataSource.getCollection(relation.throughCollection); const originRelation = Object.entries(throughCollection.schema.fields).find(([, field]) => { return (field.type === 'ManyToOne' && field.foreignCollection === collection.name && field.foreignKey === relation.originKey && field.foreignKeyTarget === relation.originKeyTarget); }); return originRelation ? originRelation[0] : null; } static getThroughTarget(collection, relationName) { const relation = schema_1.default.getRelation(collection.schema, relationName, collection.name); if (relation.type !== 'ManyToMany') throw new Error('Relation must be many to many'); const throughCollection = collection.dataSource.getCollection(relation.throughCollection); const foreignRelation = Object.entries(throughCollection.schema.fields).find(([, field]) => { return (field.type === 'ManyToOne' && field.foreignCollection === relation.foreignCollection && field.foreignKey === relation.foreignKey && field.foreignKeyTarget === relation.foreignKeyTarget); }); return foreignRelation ? foreignRelation[0] : null; } static async listRelation(collection, id, relationName, caller, foreignFilter, projection) { const relation = schema_1.default.getToManyRelation(collection.schema, relationName); // Optimization for many to many when there is not search/segment. if (relation.type === 'ManyToMany' && foreignFilter.isNestable) { const foreignRelation = CollectionUtils.getThroughTarget(collection, relationName); if (foreignRelation) { const through = collection.dataSource.getCollection(relation.throughCollection); const records = await through.list(caller, await factory_2.default.makeThroughFilter(collection, id, relationName, caller, foreignFilter), projection.nest(foreignRelation)); // Exclude null records, which may happen in case of a broken relation. // This happens on databases that don't support enforced foreign keys (e.g. Mongo) return records.map(r => r[foreignRelation]).filter(Boolean); } } // Otherwise fetch the target table (this works with both relation types) return collection.dataSource .getCollection(relation.foreignCollection) .list(caller, await factory_2.default.makeForeignFilter(collection, id, relationName, caller, foreignFilter), projection); } static async aggregateRelation(collection, id, relationName, caller, foreignFilter, aggregation, limit) { const relation = schema_1.default.getToManyRelation(collection.schema, relationName); // Optimization for many to many when there is not search/segment (saves one query) if (relation.type === 'ManyToMany' && foreignFilter.isNestable) { const foreignRelation = CollectionUtils.getThroughTarget(collection, relationName); if (foreignRelation) { const through = collection.dataSource.getCollection(relation.throughCollection); const records = await through.aggregate(caller, await factory_2.default.makeThroughFilter(collection, id, relationName, caller, foreignFilter), aggregation.nest(foreignRelation), limit); // unnest aggregation result return records.map(({ value, group }) => ({ value, group: Object.entries(group) .map(([key, v]) => [key.substring(key.indexOf(':') + 1), v]) .reduce((memo, [key, v]) => ({ ...memo, [key]: v }), {}), })); } } // Otherwise fetch the target table (this works with both relation types) return collection.dataSource .getCollection(relation.foreignCollection) .aggregate(caller, await factory_2.default.makeForeignFilter(collection, id, relationName, caller, foreignFilter), aggregation, limit); } static async getValue(collection, caller, id, field) { const index = schema_1.default.getPrimaryKeys(collection.schema).indexOf(field); if (index !== -1) return id[index]; const [record] = await collection.list(caller, new unpaginated_1.default({ conditionTree: factory_1.default.matchIds(collection.schema, [id]) }), new projection_1.default(field)); return record[field]; } } exports.default = CollectionUtils; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29sbGVjdGlvbi5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy91dGlscy9jb2xsZWN0aW9uLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsc0RBQW1DO0FBSW5DLHlGQUE4RTtBQUM5RSxpRkFBK0Q7QUFFL0QseUZBQTREO0FBQzVELGdGQUF3RDtBQUl4RCxNQUFxQixlQUFlO0lBQ2xDLE1BQU0sQ0FBQyxjQUFjLENBQUMsVUFBc0IsRUFBRSxJQUFZO1FBQ3hELE1BQU0sRUFBRSxNQUFNLEVBQUUsR0FBRyxVQUFVLENBQUMsTUFBTSxDQUFDO1FBQ3JDLE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUM7UUFFaEMsSUFBSSxLQUFLLEtBQUssQ0FBQyxDQUFDLEVBQUU7WUFDaEIsZ0JBQVcsQ0FBQyxtQkFBbUIsQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLElBQUksRUFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7WUFFMUUsT0FBTyxNQUFNLENBQUMsSUFBSSxDQUFDLENBQUM7U0FDckI7UUFFRCxNQUFNLGVBQWUsR0FBRyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNqRCxNQUFNLE1BQU0sR0FBRyxnQkFBVyxDQUFDLFdBQVcsQ0FBQyxVQUFVLENBQUMsTUFBTSxFQUFFLGVBQWUsRUFBRSxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUM7UUFFNUYsSUFBSSxNQUFNLENBQUMsSUFBSSxLQUFLLFdBQVcsSUFBSSxNQUFNLENBQUMsSUFBSSxLQUFLLFVBQVUsRUFBRTtZQUM3RCxNQUFNLElBQUksS0FBSyxDQUNiLDBCQUEwQixNQUFNLENBQUMsSUFBSSxPQUFPLFVBQVUsQ0FBQyxJQUFJLElBQUksZUFBZSxHQUFHLENBQ2xGLENBQUM7U0FDSDtRQUVELE9BQU8sZUFBZSxDQUFDLGNBQWMsQ0FDbkMsVUFBVSxDQUFDLFVBQVUsQ0FB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