@forestadmin/datasource-toolkit
Version:
121 lines • 16.2 kB
JavaScript
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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
;