diff --git a/lib/change-log.js b/lib/change-log.js index 618a83f..a53beb7 100644 --- a/lib/change-log.js +++ b/lib/change-log.js @@ -167,12 +167,12 @@ const _getChildChangeObjId = async function ( const _formatCompositionContext = async function (changes, reqData) { const childNodeChanges = [] - for (const change of changes) { + await Promise.all(changes.map(async (change) => { if (typeof change.valueChangedTo === "object") { if (!Array.isArray(change.valueChangedTo)) { change.valueChangedTo = [change.valueChangedTo] } - for (const childNodeChange of change.valueChangedTo) { + await Promise.all(change.valueChangedTo.map(async (childNodeChange) => { const curChange = Object.assign({}, change) const path = childNodeChange._path.split('/') const curNodePathVal = path.pop() @@ -184,10 +184,10 @@ const _formatCompositionContext = async function (changes, reqData) { reqData ) _formatCompositionValue(curChange, objId, childNodeChange, childNodeChanges) - } + })) change.valueChangedTo = undefined } - } + })) changes.push(...childNodeChanges) } @@ -248,7 +248,7 @@ const _getObjectIdByPath = async function ( const _formatObjectID = async function (changes, reqData) { const objectIdCache = new Map() - for (const change of changes) { + await Promise.all(changes.map(async (change) => { const path = change.serviceEntityPath.split('/') const curNodePathVal = path.pop() const parentNodePathVal = path.pop() @@ -276,7 +276,7 @@ const _formatObjectID = async function (changes, reqData) { change.entityID = curNodeObjId change.parentEntityID = parentNodeObjId change.parentKey = getUUIDFromPathVal(parentNodePathVal) - } + })) } const _isCompositionContextPath = function (aPath, hasComp) { @@ -290,6 +290,7 @@ const _isCompositionContextPath = function (aPath, hasComp) { } const _formatChangeLog = async function (changes, req) { + cds.context.dataloaders = {} await _formatObjectID(changes, req.data) await _formatAssociationContext(changes, req.data) await _formatCompositionContext(changes, req.data) @@ -317,9 +318,9 @@ function _trackedChanges4 (srv, target, diff) { if (from === to) return /** - * - * For the Inline entity such as Items, - * further filtering is required on the keys + * + * For the Inline entity such as Items, + * further filtering is required on the keys * within the 'association' and 'foreign key' to ultimately retain the keys of the entity itself. * entity Order : cuid { * title : String; diff --git a/lib/entity-helper.js b/lib/entity-helper.js index be564ce..941602e 100644 --- a/lib/entity-helper.js +++ b/lib/entity-helper.js @@ -1,4 +1,5 @@ const cds = require("@sap/cds") +const DataLoader = require("dataloader"); const LOG = cds.log("change-log") @@ -22,18 +23,28 @@ const getEntityByContextPath = function (aPath, hasComp = false) { const getObjIdElementNamesInArray = function (elements) { if (Array.isArray(elements)) return elements.map(e => { - const splitted = (e["="]||e).split('.') + const splitted = (e["="] || e).split('.') splitted.shift() return splitted.join('.') }) else return [] } -const getCurObjFromDbQuery = async function (entityName, queryVal, /**optional*/ queryKey='ID') { +const getCurObjFromDbQuery = async function (entityName, queryVal, /**optional*/ queryKey = 'ID') { + if (!(entityName in cds.context.dataloaders)) { + cds.context.dataloaders[entityName] = new DataLoader(async (keys) => { + // REVISIT: This always reads all elements -> should read required ones only! + const results = await SELECT.from(entityName).where(queryKey, 'in', keys) + const resultsByKey = results.reduce((acc, instance) => { + const key = instance[queryKey] + acc[key] = instance + return acc + }, {}) + return keys.map(key => resultsByKey[key] || {}) + }) + } if (!queryVal) return {} - // REVISIT: This always reads all elements -> should read required ones only! - const obj = await SELECT.one.from(entityName).where({[queryKey]: queryVal}) - return obj || {} + return cds.context.dataloaders[entityName].load(queryVal) } const getCurObjFromReqData = function (reqData, nodePathVal, pathVal) { @@ -71,8 +82,8 @@ const getCurObjFromReqData = function (reqData, nodePathVal, pathVal) { } -async function getObjectId (reqData, entityName, fields, curObj) { - let all = [], { curObjFromReqData: req_data={}, curObjFromDbQuery: db_data={} } = curObj +async function getObjectId(reqData, entityName, fields, curObj) { + let all = [], {curObjFromReqData: req_data = {}, curObjFromDbQuery: db_data = {}} = curObj let entity = cds.model.definitions[entityName] if (!fields?.length) fields = entity["@changelog"]?.map?.(k => k['='] || k) || [] for (let field of fields) { @@ -80,12 +91,13 @@ async function getObjectId (reqData, entityName, fields, curObj) { if (path.length > 1) { let current = entity, _db_data = db_data while (path.length > 1) { - let assoc = current.elements[path[0]]; if (!assoc?.isAssociation) break + let assoc = current.elements[path[0]]; + if (!assoc?.isAssociation) break let foreignKey = assoc.keys?.[0]?.$generatedFieldName let IDval = - req_data[foreignKey] && current.name === entityName - ? req_data[foreignKey] - : _db_data[foreignKey] + req_data[foreignKey] && current.name === entityName + ? req_data[foreignKey] + : _db_data[foreignKey] if (!IDval) { _db_data = {}; } else try { @@ -125,7 +137,6 @@ async function getObjectId (reqData, entityName, fields, curObj) { } - const getDBEntity = (entity) => { if (typeof entity === 'string') entity = cds.model.definitions[entity] let proto = Reflect.getPrototypeOf(entity) @@ -133,11 +144,11 @@ const getDBEntity = (entity) => { } const getValueEntityType = function (entityName, fields) { - const types=[], entity = cds.model.definitions[entityName] + const types = [], entity = cds.model.definitions[entityName] for (let field of fields) { let current = entity, path = field.split('.') if (path.length > 1) { - for (;;) { + for (; ;) { let target = current.elements[path[0]]?._target if (target) current = target; else break path.shift() @@ -167,20 +178,20 @@ const hasComposition = function (parentEntity, subEntity) { } const _getCompositionObjFromReq = function (obj, targetID) { - if (obj?.ID === targetID) { - return obj; - } + if (obj?.ID === targetID) { + return obj; + } - for (const key in obj) { - if (typeof obj[key] === "object" && obj[key] !== null) { - const result = _getCompositionObjFromReq(obj[key], targetID); - if (result) { - return result; - } - } + for (const key in obj) { + if (typeof obj[key] === "object" && obj[key] !== null) { + const result = _getCompositionObjFromReq(obj[key], targetID); + if (result) { + return result; + } } + } - return null; + return null; }; module.exports = { diff --git a/package.json b/package.json index 255f393..1d707be 100644 --- a/package.json +++ b/package.json @@ -37,5 +37,8 @@ "model": "@cap-js/change-tracking" } } + }, + "dependencies": { + "dataloader": "^2.2.2" } }