payload
Version:
Node, React and MongoDB Headless CMS and Application Framework
120 lines (119 loc) • 14.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "getDataLoader", {
enumerable: true,
get: function() {
return getDataLoader;
}
});
const _dataloader = /*#__PURE__*/ _interop_require_default(require("dataloader"));
const _types = require("../fields/config/types");
const _getIDType = require("../utilities/getIDType");
const _isValidID = require("../utilities/isValidID");
function _interop_require_default(obj) {
return obj && obj.__esModule ? obj : {
default: obj
};
}
// Payload uses `dataloader` to solve the classic GraphQL N+1 problem.
// We keep a list of all documents requested to be populated for any given request
// and then batch together documents within the same collection,
// making only 1 find per each collection, rather than `findByID` per each requested doc.
// This dramatically improves performance for REST and Local API `depth` populations,
// and also ensures complex GraphQL queries perform lightning-fast.
const batchAndLoadDocs = (req)=>async (keys)=>{
const { payload } = req;
// Create docs array of same length as keys, using null as value
// We will replace nulls with injected docs as they are retrieved
const docs = keys.map(()=>null);
// Batch IDs by their `find` args
// so we can make one find query per combination of collection, depth, locale, and fallbackLocale.
// Resulting shape will be as follows:
// {
// // key is stringified set of find args
// '[null,"pages",2,0,"es","en",false,false]': [
// // value is array of IDs to find with these args
// 'q34tl23462346234524',
// '435523540194324280',
// '2346245j35l3j5234532li',
// ],
// // etc
// };
const batchByFindArgs = keys.reduce((batches, key)=>{
const [transactionID, collection, id, depth, currentDepth, locale, fallbackLocale, overrideAccess, showHiddenFields] = JSON.parse(key);
const batchKeyArray = [
transactionID,
collection,
depth,
currentDepth,
locale,
fallbackLocale,
overrideAccess,
showHiddenFields
];
const batchKey = JSON.stringify(batchKeyArray);
const idField = payload.collections?.[collection].config.fields.find((field)=>(0, _types.fieldAffectsData)(field) && field.name === 'id');
let sanitizedID = id;
if (idField?.type === 'number') sanitizedID = parseFloat(id);
if ((0, _isValidID.isValidID)(sanitizedID, (0, _getIDType.getIDType)(idField, payload?.db?.defaultIDType))) {
return {
...batches,
[batchKey]: [
...batches[batchKey] || [],
sanitizedID
]
};
}
return batches;
}, {});
// Run find requests one after another, so as to not hang transactions
await Object.entries(batchByFindArgs).reduce(async (priorFind, [batchKey, ids])=>{
await priorFind;
const [transactionID, collection, depth, currentDepth, locale, fallbackLocale, overrideAccess, showHiddenFields] = JSON.parse(batchKey);
req.transactionID = transactionID;
const result = await payload.find({
collection,
currentDepth,
depth,
disableErrors: true,
fallbackLocale,
locale,
overrideAccess: Boolean(overrideAccess),
pagination: false,
req,
showHiddenFields: Boolean(showHiddenFields),
where: {
id: {
in: ids
}
}
});
// For each returned doc, find index in original keys
// Inject doc within docs array if index exists
result.docs.forEach((doc)=>{
const docKey = JSON.stringify([
req.transactionID,
collection,
doc.id,
depth,
currentDepth,
locale,
fallbackLocale,
overrideAccess,
showHiddenFields
]);
const docsIndex = keys.findIndex((key)=>key === docKey);
if (docsIndex > -1) {
docs[docsIndex] = doc;
}
});
}, Promise.resolve());
// Return docs array,
// which has now been injected with all fetched docs
// and should match the length of the incoming keys arg
return docs;
};
const getDataLoader = (req)=>new _dataloader.default(batchAndLoadDocs(req));
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb2xsZWN0aW9ucy9kYXRhbG9hZGVyLnRzIl0sInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB0eXBlIHsgQmF0Y2hMb2FkRm4gfSBmcm9tICdkYXRhbG9hZGVyJ1xuXG5pbXBvcnQgRGF0YUxvYWRlciBmcm9tICdkYXRhbG9hZGVyJ1xuXG5pbXBvcnQgdHlwZSB7IFBheWxvYWRSZXF1ZXN0IH0gZnJvbSAnLi4vZXhwcmVzcy90eXBlcydcbmltcG9ydCB0eXBlIHsgVHlwZVdpdGhJRCB9IGZyb20gJy4vY29uZmlnL3R5cGVzJ1xuXG5pbXBvcnQgeyBmaWVsZEFmZmVjdHNEYXRhIH0gZnJvbSAnLi4vZmllbGRzL2NvbmZpZy90eXBlcydcbmltcG9ydCB7IGdldElEVHlwZSB9IGZyb20gJy4uL3V0aWxpdGllcy9nZXRJRFR5cGUnXG5pbXBvcnQgeyBpc1ZhbGlkSUQgfSBmcm9tICcuLi91dGlsaXRpZXMvaXNWYWxpZElEJ1xuXG4vLyBQYXlsb2FkIHVzZXMgYGRhdGFsb2FkZXJgIHRvIHNvbHZlIHRoZSBjbGFzc2ljIEdyYXBoUUwgTisxIHByb2JsZW0uXG5cbi8vIFdlIGtlZXAgYSBsaXN0IG9mIGFsbCBkb2N1bWVudHMgcmVxdWVzdGVkIHRvIGJlIHBvcHVsYXRlZCBmb3IgYW55IGdpdmVuIHJlcXVlc3Rcbi8vIGFuZCB0aGVuIGJhdGNoIHRvZ2V0aGVyIGRvY3VtZW50cyB3aXRoaW4gdGhlIHNhbWUgY29sbGVjdGlvbixcbi8vIG1ha2luZyBvbmx5IDEgZmluZCBwZXIgZWFjaCBjb2xsZWN0aW9uLCByYXRoZXIgdGhhbiBgZmluZEJ5SURgIHBlciBlYWNoIHJlcXVlc3RlZCBkb2MuXG5cbi8vIFRoaXMgZHJhbWF0aWNhbGx5IGltcHJvdmVzIHBlcmZvcm1hbmNlIGZvciBSRVNUIGFuZCBMb2NhbCBBUEkgYGRlcHRoYCBwb3B1bGF0aW9ucyxcbi8vIGFuZCBhbHNvIGVuc3VyZXMgY29tcGxleCBHcmFwaFFMIHF1ZXJpZXMgcGVyZm9ybSBsaWdodG5pbmctZmFzdC5cblxuY29uc3QgYmF0Y2hBbmRMb2FkRG9jcyA9XG4gIChyZXE6IFBheWxvYWRSZXF1ZXN0KTogQmF0Y2hMb2FkRm48c3RyaW5nLCBUeXBlV2l0aElEPiA9PlxuICBhc3luYyAoa2V5czogc3RyaW5nW10pOiBQcm9taXNlPFR5cGVXaXRoSURbXT4gPT4ge1xuICAgIGNvbnN0IHsgcGF5bG9hZCB9ID0gcmVxXG5cbiAgICAvLyBDcmVhdGUgZG9jcyBhcnJheSBvZiBzYW1lIGxlbmd0aCBhcyBrZXlzLCB1c2luZyBudWxsIGFzIHZhbHVlXG4gICAgLy8gV2Ugd2lsbCByZXBsYWNlIG51bGxzIHdpdGggaW5qZWN0ZWQgZG9jcyBhcyB0aGV5IGFyZSByZXRyaWV2ZWRcbiAgICBjb25zdCBkb2NzOiAoVHlwZVdpdGhJRCB8IG51bGwpW10gPSBrZXlzLm1hcCgoKSA9PiBudWxsKVxuXG4gICAgLy8gQmF0Y2ggSURzIGJ5IHRoZWlyIGBmaW5kYCBhcmdzXG4gICAgLy8gc28gd2UgY2FuIG1ha2Ugb25lIGZpbmQgcXVlcnkgcGVyIGNvbWJpbmF0aW9uIG9mIGNvbGxlY3Rpb24sIGRlcHRoLCBsb2NhbGUsIGFuZCBmYWxsYmFja0xvY2FsZS5cblxuICAgIC8vIFJlc3VsdGluZyBzaGFwZSB3aWxsIGJlIGFzIGZvbGxvd3M6XG5cbiAgICAvLyB7XG4gICAgLy8gICAvLyBrZXkgaXMgc3RyaW5naWZpZWQgc2V0IG9mIGZpbmQgYXJnc1xuICAgIC8vICAgJ1tudWxsLFwicGFnZXNcIiwyLDAsXCJlc1wiLFwiZW5cIixmYWxzZSxmYWxzZV0nOiBbXG4gICAgLy8gICAgIC8vIHZhbHVlIGlzIGFycmF5IG9mIElEcyB0byBmaW5kIHdpdGggdGhlc2UgYXJnc1xuICAgIC8vICAgICAncTM0dGwyMzQ2MjM0NjIzNDUyNCcsXG4gICAgLy8gICAgICc0MzU1MjM1NDAxOTQzMjQyODAnLFxuICAgIC8vICAgICAnMjM0NjI0NWozNWwzajUyMzQ1MzJsaScsXG4gICAgLy8gICBdLFxuICAgIC8vICAgLy8gZXRjXG4gICAgLy8gfTtcblxuICAgIGNvbnN0IGJhdGNoQnlGaW5kQXJncyA9IGtleXMucmVkdWNlKChiYXRjaGVzLCBrZXkpID0+IHtcbiAgICAgIGNvbnN0IFtcbiAgICAgICAgdHJhbnNhY3Rpb25JRCxcbiAgICAgICAgY29sbGVjdGlvbixcbiAgICAgICAgaWQsXG4gICAgICAgIGRlcHRoLFxuICAgICAgICBjdXJyZW50RGVwdGgsXG4gICAgICAgIGxvY2FsZSxcbiAgICAgICAgZmFsbGJhY2tMb2NhbGUsXG4gICAgICAgIG92ZXJyaWRlQWNjZXNzLFxuICAgICAgICBzaG93SGlkZGVuRmllbGRzLFxuICAgICAgXSA9IEpTT04ucGFyc2Uoa2V5KVxuXG4gICAgICBjb25zdCBiYXRjaEtleUFycmF5ID0gW1xuICAgICAgICB0cmFuc2FjdGlvbklELFxuICAgICAgICBjb2xsZWN0aW9uLFxuICAgICAgICBkZXB0aCxcbiAgICAgICAgY3VycmVudERlcHRoLFxuICAgICAgICBsb2NhbGUsXG4gICAgICAgIGZhbGxiYWNrTG9jYWxlLFxuICAgICAgICBvdmVycmlkZUFjY2VzcyxcbiAgICAgICAgc2hvd0hpZGRlbkZpZWxkcyxcbiAgICAgIF1cblxuICAgICAgY29uc3QgYmF0Y2hLZXkgPSBKU09OLnN0cmluZ2lmeShiYXRjaEtleUFycmF5KVxuXG4gICAgICBjb25zdCBpZEZpZWxkID0gcGF5bG9hZC5jb2xsZWN0aW9ucz8uW2NvbGxlY3Rpb25dLmNvbmZpZy5maWVsZHMuZmluZChcbiAgICAgICAgKGZpZWxkKSA9PiBmaWVsZEFmZmVjdHNEYXRhKGZpZWxkKSAmJiBmaWVsZC5uYW1lID09PSAnaWQnLFxuICAgICAgKVxuXG4gICAgICBsZXQgc2FuaXRpemVkSUQ6IG51bWJlciB8IHN0cmluZyA9IGlkXG5cbiAgICAgIGlmIChpZEZpZWxkPy50eXBlID09PSAnbnVtYmVyJykgc2FuaXRpemVkSUQgPSBwYXJzZUZsb2F0KGlkKVxuXG4gICAgICBpZiAoaXNWYWxpZElEKHNhbml0aXplZElELCBnZXRJRFR5cGUoaWRGaWVsZCwgcGF5bG9hZD8uZGI/LmRlZmF1bHRJRFR5cGUpKSkge1xuICAgICAgICByZXR1cm4ge1xuICAgICAgICAgIC4uLmJhdGNoZXMsXG4gICAgICAgICAgW2JhdGNoS2V5XTogWy4uLihiYXRjaGVzW2JhdGNoS2V5XSB8fCBbXSksIHNhbml0aXplZElEXSxcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIGJhdGNoZXNcbiAgICB9LCB7fSlcblxuICAgIC8vIFJ1biBmaW5kIHJlcXVlc3RzIG9uZSBhZnRlciBhbm90aGVyLCBzbyBhcyB0byBub3QgaGFuZyB0cmFuc2FjdGlvbnNcblxuICAgIGF3YWl0IE9iamVjdC5lbnRyaWVzKGJhdGNoQnlGaW5kQXJncykucmVkdWNlKGFzeW5jIChwcmlvckZpbmQsIFtiYXRjaEtleSwgaWRzXSkgPT4ge1xuICAgICAgYXdhaXQgcHJpb3JGaW5kXG5cbiAgICAgIGNvbnN0IFtcbiAgICAgICAgdHJhbnNhY3Rpb25JRCxcbiAgICAgICAgY29sbGVjdGlvbixcbiAgICAgICAgZGVwdGgsXG4gICAgICAgIGN1cnJlbnREZXB0aCxcbiAgICAgICAgbG9jYWxlLFxuICAgICAgICBmYWxsYmFja0xvY2FsZSxcbiAgICAgICAgb3ZlcnJpZGVBY2Nlc3MsXG4gICAgICAgIHNob3dIaWRkZW5GaWVsZHMsXG4gICAgICBdID0gSlNPTi5wYXJzZShiYXRjaEtleSlcblxuICAgICAgcmVxLnRyYW5zYWN0aW9uSUQgPSB0cmFuc2FjdGlvbklEXG5cbiAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHBheWxvYWQuZmluZCh7XG4gICAgICAgIGNvbGxlY3Rpb24sXG4gICAgICAgIGN1cnJlbnREZXB0aCxcbiAgICAgICAgZGVwdGgsXG4gICAgICAgIGRpc2FibGVFcnJvcnM6IHRydWUsXG4gICAgICAgIGZhbGxiYWNrTG9jYWxlLFxuICAgICAgICBsb2NhbGUsXG4gICAgICAgIG92ZXJyaWRlQWNjZXNzOiBCb29sZWFuKG92ZXJyaWRlQWNjZXNzKSxcbiAgICAgICAgcGFnaW5hdGlvbjogZmFsc2UsXG4gICAgICAgIHJlcSxcbiAgICAgICAgc2hvd0hpZGRlbkZpZWxkczogQm9vbGVhbihzaG93SGlkZGVuRmllbGRzKSxcbiAgICAgICAgd2hlcmU6IHtcbiAgICAgICAgICBpZDoge1xuICAgICAgICAgICAgaW46IGlkcyxcbiAgICAgICAgICB9LFxuICAgICAgICB9LFxuICAgICAgfSlcblxuICAgICAgLy8gRm9yIGVhY2ggcmV0dXJuZWQgZG9jLCBmaW5kIGluZGV4IGluIG9yaWdpbmFsIGtleXNcbiAgICAgIC8vIEluamVjdCBkb2Mgd2l0aGluIGRvY3MgYXJyYXkgaWYgaW5kZXggZXhpc3RzXG5cbiAgICAgIHJlc3VsdC5kb2NzLmZvckVhY2goKGRvYykgPT4ge1xuICAgICAgICBjb25zdCBkb2NLZXkgPSBKU09OLnN0cmluZ2lmeShbXG4gICAgICAgICAgcmVxLnRyYW5zYWN0aW9uSUQsXG4gICAgICAgICAgY29sbGVjdGlvbixcbiAgICAgICAgICBkb2MuaWQsXG4gICAgICAgICAgZGVwdGgsXG4gICAgICAgICAgY3VycmVudERlcHRoLFxuICAgICAgICAgIGxvY2FsZSxcbiAgICAgICAgICBmYWxsYmFja0xvY2FsZSxcbiAgICAgICAgICBvdmVycmlkZUFjY2VzcyxcbiAgICAgICAgICBzaG93SGlkZGVuRmllbGRzLFxuICAgICAgICBdKVxuICAgICAgICBjb25zdCBkb2NzSW5kZXggPSBrZXlzLmZpbmRJbmRleCgoa2V5KSA9PiBrZXkgPT09IGRvY0tleSlcblxuICAgICAgICBpZiAoZG9jc0luZGV4ID4gLTEpIHtcbiAgICAgICAgICBkb2NzW2RvY3NJbmRleF0gPSBkb2NcbiAgICAgICAgfVxuICAgICAgfSlcbiAgICB9LCBQcm9taXNlLnJlc29sdmUoKSlcblxuICAgIC8vIFJldHVybiBkb2NzIGFycmF5LFxuICAgIC8vIHdoaWNoIGhhcyBub3cgYmVlbiBpbmplY3RlZCB3aXRoIGFsbCBmZXRjaGVkIGRvY3NcbiAgICAvLyBhbmQgc2hvdWxkIG1hdGNoIHRoZSBsZW5ndGggb2YgdGhlIGluY29taW5nIGtleXMgYXJnXG4gICAgcmV0dXJuIGRvY3NcbiAgfVxuXG5leHBvcnQgY29uc3QgZ2V0RGF0YUxvYWRlciA9IChyZXE6IFBheWxvYWRSZXF1ZXN0KSA9PiBuZXcgRGF0YUxvYWRlcihiYXRjaEFuZExvYWREb2NzKHJlcSkpXG4iXSwibmFtZXMiOlsiZ2V0RGF0YUxvYWRlciIsImJhdGNoQW5kTG9hZERvY3MiLCJyZXEiLCJrZXlzIiwicGF5bG9hZCIsImRvY3MiLCJtYXAiLCJiYXRjaEJ5RmluZEFyZ3MiLCJyZWR1Y2UiLCJiYXRjaGVzIiwia2V5IiwidHJhbnNhY3Rpb25JRCIsImNvbGxlY3Rpb24iLCJpZCIsImRlcHRoIiwiY3VycmVudERlcHRoIiwibG9jYWxlIiwiZmFsbGJhY2tMb2NhbGUiLCJvdmVycmlkZUFjY2VzcyIsInNob3dIaWRkZW5GaWVsZHMiLCJKU09OIiwicGFyc2UiLCJiYXRjaEtleUFycmF5IiwiYmF0Y2hLZXkiLCJzdHJpbmdpZnkiLCJpZEZpZWxkIiwiY29sbGVjdGlvbnMiLCJjb25maWciLCJmaWVsZHMiLCJmaW5kIiwiZmllbGQiLCJmaWVsZEFmZmVjdHNEYXRhIiwibmFtZSIsInNhbml0aXplZElEIiwidHlwZSIsInBhcnNlRmxvYXQiLCJpc1ZhbGlkSUQiLCJnZXRJRFR5cGUiLCJkYiIsImRlZmF1bHRJRFR5cGUiLCJPYmplY3QiLCJlbnRyaWVzIiwicHJpb3JGaW5kIiwiaWRzIiwicmVzdWx0IiwiZGlzYWJsZUVycm9ycyIsIkJvb2xlYW4iLCJwYWdpbmF0aW9uIiwid2hlcmUiLCJpbiIsImZvckVhY2giLCJkb2MiLCJkb2NLZXkiLCJkb2NzSW5kZXgiLCJmaW5kSW5kZXgiLCJQcm9taXNlIiwicmVzb2x2ZSIsIkRhdGFMb2FkZXIiXSwibWFwcGluZ3MiOiI7Ozs7K0JBeUphQTs7O2VBQUFBOzs7bUVBdkpVO3VCQUtVOzJCQUNQOzJCQUNBOzs7Ozs7QUFFMUIsc0VBQXNFO0FBRXRFLGtGQUFrRjtBQUNs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