You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
102 lines
3.9 KiB
102 lines
3.9 KiB
3 years ago
|
"use strict";
|
||
|
|
||
|
Object.defineProperty(exports, "__esModule", {
|
||
|
value: true
|
||
|
});
|
||
|
exports.populateProperty = populateProperty;
|
||
|
|
||
|
const isValueSearchable = value => ['string', 'bigint', 'number'].includes(typeof value) && value !== null && value !== '';
|
||
|
/**
|
||
|
* It populates one property in given records
|
||
|
*
|
||
|
* @param {Array<BaseRecord>} records array of records to populate
|
||
|
* @param {PropertyDecorator} property Decorator for the reference property to populate
|
||
|
* @private
|
||
|
* @hide
|
||
|
*/
|
||
|
|
||
|
|
||
|
async function populateProperty(records, property) {
|
||
|
const decoratedResource = property.resource();
|
||
|
|
||
|
if (!records || !records.length) {
|
||
|
return records;
|
||
|
}
|
||
|
|
||
|
const referencedResource = property.reference();
|
||
|
|
||
|
if (!referencedResource) {
|
||
|
throw new Error([`There is no reference resource named: "${property.property.reference}"`, `for property: "${decoratedResource.id()}.properties.${property.propertyPath}"`].join('\n'));
|
||
|
} // I will describe the process for following data:
|
||
|
// - decoratedResource = 'Comment'
|
||
|
// - referenceResource = 'User'
|
||
|
// property.path = 'userId'
|
||
|
// first, we create externalIdsMap[1] = null where 1 is userId. This make keys unique and assign
|
||
|
// nulls to each of them
|
||
|
|
||
|
|
||
|
const externalIdsMap = records.reduce((memo, baseRecord) => {
|
||
|
const foreignKeyValue = baseRecord.get(property.propertyPath, {
|
||
|
includeAllSiblings: true
|
||
|
}); // 2 kind of properties returning arrays
|
||
|
// - the one with the array type
|
||
|
// - the one which are nested within an arrays (fetched by the help of
|
||
|
// the options { includeAllSiblings: true } in baseRecord.get().
|
||
|
// so we have to take it all into consideration
|
||
|
|
||
|
if (Array.isArray(foreignKeyValue)) {
|
||
|
return foreignKeyValue.reduce((arrayMemo, valueInArray) => ({ ...arrayMemo,
|
||
|
...(isValueSearchable(valueInArray) ? {
|
||
|
[valueInArray]: valueInArray
|
||
|
} : {})
|
||
|
}), memo);
|
||
|
}
|
||
|
|
||
|
if (!isValueSearchable(foreignKeyValue)) {
|
||
|
return memo;
|
||
|
}
|
||
|
|
||
|
memo[foreignKeyValue] = foreignKeyValue;
|
||
|
return memo;
|
||
|
}, {});
|
||
|
const uniqueExternalIds = Object.values(externalIdsMap); // when no record has reference filled (ie `userId`) = return input `records`
|
||
|
|
||
|
if (!uniqueExternalIds.length) {
|
||
|
return records;
|
||
|
} // now find all referenced records: all users
|
||
|
|
||
|
|
||
|
const referenceRecords = await referencedResource.findMany(uniqueExternalIds); // even if record has value for this reference - it might not have the referenced record itself
|
||
|
// this happens quite often in mongodb where there are no constrains on the database
|
||
|
|
||
|
if (!referenceRecords || !referenceRecords.length) {
|
||
|
return records;
|
||
|
} // now assign these users to `externalIdsMap` instead of the empty object we had. To speed up
|
||
|
// assigning them to record#populated we will do in the next step by calling:
|
||
|
// `externalIdsMap[id]` to get populated record instead of finding them in an array
|
||
|
|
||
|
|
||
|
referenceRecords.forEach(referenceRecord => {
|
||
|
// example: externalIds[1] = { ...userRecord } | null (if not found)
|
||
|
const foreignKeyValue = referenceRecord.id();
|
||
|
externalIdsMap[foreignKeyValue] = referenceRecord;
|
||
|
});
|
||
|
return records.map(record => {
|
||
|
// first lets extract all the existing params from the given record which belongs to given
|
||
|
// property. Usually it will be just one element, but for arrays and items nested inside arrays
|
||
|
// there will be more like this for array:
|
||
|
// {
|
||
|
// 'professions.0': '5f7462621eb3495ea0f0edd7',
|
||
|
// 'professions.1': '5f7462621eb3495ea0f0edd6',
|
||
|
// }
|
||
|
const referenceParams = record.selectParams(property.propertyPath, {
|
||
|
includeAllSiblings: true
|
||
|
}) || {}; // next we copy the exact params structure to record.populated changing the value with found
|
||
|
// record
|
||
|
|
||
|
Object.entries(referenceParams).forEach(([path, foreignKeyValueItem]) => {
|
||
|
record.populate(path, externalIdsMap[foreignKeyValueItem]);
|
||
|
});
|
||
|
return record;
|
||
|
});
|
||
|
}
|