memoizes the Populate function

This commit is contained in:
James
2020-10-06 08:58:25 -04:00
parent 7f65e68e82
commit a0d6fe5ca7
6 changed files with 82 additions and 93 deletions

View File

@@ -9,10 +9,8 @@ const accessPromise = async ({
req,
id,
relationshipPopulations,
depth,
currentDepth,
hook,
payload,
populate,
}) => {
const resultingData = data;
@@ -30,11 +28,7 @@ const accessPromise = async ({
relationshipPopulations.push(relationshipPopulationPromise({
data,
field,
depth,
currentDepth,
req,
overrideAccess,
payload,
populate,
}));
}
};

View File

@@ -1,6 +1,8 @@
const memoize = require('fast-memoize');
const { ValidationError } = require('../errors');
const sanitizeFallbackLocale = require('../localization/sanitizeFallbackLocale');
const traverseFields = require('./traverseFields');
const executeAccess = require('../auth/executeAccess');
async function performFieldOperations(entityConfig, args) {
const {
@@ -35,12 +37,72 @@ async function performFieldOperations(entityConfig, args) {
// Maintain a top-level list of promises
// so that all async field access / validations / hooks
// can run in parallel
const validationPromises = [];
const accessPromises = [];
const relationshipPopulations = [];
const hookPromises = [];
const errors = [];
// ////////////////////////////////////////////////////////////////
// Create memoized Populate function — with the same input (ID),
// it will return the same result without re-querying.
// Note - this function is recreated each time a request comes in
// ////////////////////////////////////////////////////////////////
const populate = memoize(async (
data,
dataReference,
field,
index,
) => {
const dataToUpdate = dataReference;
const relation = Array.isArray(field.relationTo) ? data.relationTo : field.relationTo;
const relatedCollection = this.collections[relation];
if (relatedCollection) {
const accessResult = !overrideAccess ? await executeAccess({ req, disableErrors: true, id }, relatedCollection.config.access.read) : true;
let populatedRelationship = null;
if (accessResult && (depth && currentDepth <= depth)) {
let idString = Array.isArray(field.relationTo) ? data.value : data;
if (typeof idString !== 'string') {
idString = idString.toString();
}
populatedRelationship = await this.operations.collections.findByID({
req,
collection: relatedCollection,
id: idString,
currentDepth: currentDepth + 1,
disableErrors: true,
depth,
});
}
// If access control fails, update value to null
// If populatedRelationship comes back, update value
if (!accessResult || populatedRelationship) {
if (typeof index === 'number') {
if (Array.isArray(field.relationTo)) {
dataToUpdate[field.name][index].value = populatedRelationship;
} else {
dataToUpdate[field.name][index] = populatedRelationship;
}
} else if (Array.isArray(field.relationTo)) {
dataToUpdate[field.name].value = populatedRelationship;
} else {
dataToUpdate[field.name] = populatedRelationship;
}
}
}
}, {
serializer: (populateArgs) => JSON.stringify(populateArgs[0]),
});
// //////////////////////////////////////////
// Entry point for field validation
// //////////////////////////////////////////
@@ -69,6 +131,7 @@ async function performFieldOperations(entityConfig, args) {
validationPromises,
errors,
payload: this,
populate,
});
await Promise.all(hookPromises);

View File

@@ -1,70 +1,7 @@
const executeAccess = require('../auth/executeAccess');
const populate = async ({
depth,
currentDepth,
req,
overrideAccess,
dataReference,
data,
field,
index,
id,
payload,
}) => {
const dataToUpdate = dataReference;
const relation = Array.isArray(field.relationTo) ? data.relationTo : field.relationTo;
const relatedCollection = payload.collections[relation];
if (relatedCollection) {
const accessResult = !overrideAccess ? await executeAccess({ req, disableErrors: true, id }, relatedCollection.config.access.read) : true;
let populatedRelationship = null;
if (accessResult && (depth && currentDepth <= depth)) {
let idString = Array.isArray(field.relationTo) ? data.value : data;
if (typeof idString !== 'string') {
idString = idString.toString();
}
populatedRelationship = await payload.operations.collections.findByID({
req,
collection: relatedCollection,
id: idString,
currentDepth: currentDepth + 1,
disableErrors: true,
depth,
});
}
// If access control fails, update value to null
// If populatedRelationship comes back, update value
if (!accessResult || populatedRelationship) {
if (typeof index === 'number') {
if (Array.isArray(field.relationTo)) {
dataToUpdate[field.name][index].value = populatedRelationship;
} else {
dataToUpdate[field.name][index] = populatedRelationship;
}
} else if (Array.isArray(field.relationTo)) {
dataToUpdate[field.name].value = populatedRelationship;
} else {
dataToUpdate[field.name] = populatedRelationship;
}
}
}
};
const relationshipPopulationPromise = ({
data,
field,
depth,
currentDepth,
req,
overrideAccess,
payload,
populate,
}) => async () => {
const resultingData = data;
@@ -74,17 +11,12 @@ const relationshipPopulationPromise = ({
data[field.name].forEach((relatedDoc, index) => {
const rowPromise = async () => {
if (relatedDoc) {
await populate({
depth,
currentDepth,
req,
overrideAccess,
data: relatedDoc,
dataReference: resultingData,
await populate(
relatedDoc,
resultingData,
field,
index,
payload,
});
);
}
};
@@ -93,16 +25,11 @@ const relationshipPopulationPromise = ({
await Promise.all(rowPromises);
} else if (data[field.name]) {
await populate({
depth,
currentDepth,
req,
overrideAccess,
dataReference: resultingData,
data: data[field.name],
await populate(
data[field.name],
resultingData,
field,
payload,
});
);
}
};

View File

@@ -27,6 +27,7 @@ const traverseFields = (args) => {
validationPromises,
errors,
payload,
populate,
} = args;
fields.forEach((field) => {
@@ -60,6 +61,7 @@ const traverseFields = (args) => {
}
accessPromises.push(accessPromise({
populate,
data,
originalDoc,
field,
@@ -68,10 +70,7 @@ const traverseFields = (args) => {
req,
id,
relationshipPopulations,
depth,
currentDepth,
hook,
payload,
}));
hookPromises.push(hookPromise({