UNPKG

@goatlab/fluent

Version:

Readable query Interface & API generator for TS and Node

151 lines 6.38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.loadRelations = void 0; const js_utils_1 = require("@goatlab/js-utils"); const loadRelations = async ({ data, relations, modelRelations, provider, self, }) => { if (!relations) { return data; } // Firebase has a 10 query limit const chunkSize = provider === 'typeorm' ? 100 : 10; for (const relation of Object.keys(relations)) { validateRelation(relation, modelRelations, self); const relationModel = modelRelations[relation]; const Repository = self[relation](); if (relationModel.isOneToMany) { data = await loadOneToManyRelation(data, relationModel, Repository, chunkSize); } else if (relationModel.isManyToOne) { data = await loadManyToOneRelation(data, relationModel, Repository, chunkSize); } else if (relationModel.isManyToMany) { data = await loadManyToManyRelation(data, relationModel, Repository, self, chunkSize); } } return data; }; exports.loadRelations = loadRelations; function validateRelation(relation, modelRelations, self) { if (!modelRelations[relation]) { throw new Error(`Relationship does not exist: ${relation}. Did you create it in your DB entity?`); } if (!self[relation]) { throw new Error(`Relationship does not exist: ${relation}. Did you create it in your Repository?`); } } async function loadOneToManyRelation(data, relationModel, Repository, chunkSize) { const ids = new Set(data.map(d => d.id)); const chunks = js_utils_1.Arrays.chunk(Array.from(ids), chunkSize); const promises = chunks.map(relatedIds => Repository.findMany({ where: { [relationModel.inverseSidePropertyPath]: { in: relatedIds, }, }, })); const relatedResults = js_utils_1.Arrays.collapse(await Promise.all(promises)); const grouped = js_utils_1.Arrays.groupBy(relatedResults, r => r[relationModel.inverseSidePropertyPath]); return data.map(d => { if (grouped[d.id]) { d[relationModel.propertyName] = grouped[d.id]; } return d; }); } async function loadManyToOneRelation(data, relationModel, Repository, chunkSize) { const ids = js_utils_1.Arrays.deDuplicate(data.map(d => d[relationModel.joinColumns[0].propertyPath])); const chunks = js_utils_1.Arrays.chunk(ids, chunkSize); const promises = chunks.map(relatedIds => Repository.findMany({ where: { id: { in: relatedIds, }, }, })); const relatedResults = js_utils_1.Arrays.collapse(await Promise.all(promises)); const grouped = js_utils_1.Arrays.groupBy(relatedResults, r => r.id); data.map(d => { if (grouped[d[relationModel.joinColumns[0].propertyPath]]) { d[relationModel.propertyName] = grouped[d[relationModel.joinColumns[0].propertyPath]][0]; } }); return data; } async function loadManyToManyRelation(data, relationModel, Repository, self, chunkSize) { const ids = js_utils_1.Arrays.deDuplicate(data.map(d => d.id)); const chunks = js_utils_1.Arrays.chunk(ids, chunkSize); if (relationModel.joinColumns.length === 0) { return data; } const pivotData = getPivotData(self, relationModel, Repository); // Get Pivot Table Results const pivotResults = await loadPivotResults(chunks, pivotData.pivotForeignField, pivotData.pivotRepository); // Get relationship table results const relatedResults = await loadRelatedResults(pivotResults, relationModel, Repository, chunkSize); // Map results back to data mapManyToManyResults(data, pivotResults, relatedResults, relationModel, pivotData.calleeKey); return data; } function getPivotData(self, relationModel, Repository) { const pivotForeignField = self.modelRelations[relationModel.propertyName].joinColumns[0].propertyPath; const inverseForeignField = self.modelRelations[relationModel.propertyName].inverseJoinColumns[0] .propertyPath; const pivotRepository = Repository?.relatedQuery.pivot; const calleeKey = Repository?.relatedQuery.key; if (!pivotForeignField || !inverseForeignField || !pivotRepository || !calleeKey) { throw new Error('The Many-to-Many relationship is not properly defined.Please check both your Model and Repository'); } return { pivotForeignField, inverseForeignField, pivotRepository, calleeKey, }; } async function loadPivotResults(chunks, pivotForeignField, pivotRepository) { const promises = chunks.map(pivotIds => pivotRepository.findMany({ where: { [pivotForeignField]: { in: pivotIds, }, }, })); return js_utils_1.Arrays.collapse(await Promise.all(promises)); } async function loadRelatedResults(pivotResults, relationModel, Repository, chunkSize) { const inverseForeignField = relationModel.inverseJoinColumns[0].propertyPath; const uniquePivotIds = pivotResults.map(p => p[inverseForeignField]); const relationChunks = js_utils_1.Arrays.chunk(uniquePivotIds, chunkSize); const relationPromises = relationChunks.map(relatedIds => Repository.findMany({ where: { id: { in: relatedIds, }, }, })); const relatedResults = js_utils_1.Arrays.collapse(await Promise.all(relationPromises)); return relatedResults.map(r => ({ ...r, pivot: pivotResults.find(p => p[inverseForeignField] === r.id), })); } function mapManyToManyResults(data, pivotResults, relatedResults, relationModel, calleeKey) { const groupedPivot = js_utils_1.Arrays.groupBy(pivotResults, r => r[relationModel.joinColumns[0].propertyName]); const groupedRelated = js_utils_1.Arrays.groupBy(relatedResults, r => r.id); data.map(d => { groupedPivot[d.id]?.forEach(gp => { if (!d[calleeKey]) { d[calleeKey] = []; } const mapped = groupedRelated[gp[relationModel.inverseJoinColumns[0].propertyName]]; if (mapped) { d[calleeKey] = [...d[calleeKey], ...mapped]; } }); }); } //# sourceMappingURL=loadRelations.js.map