Skip to content

Commit

Permalink
use dataloader to collect calls to DB fixes cap-js#30
Browse files Browse the repository at this point in the history
  • Loading branch information
d060338 authored and d060338 committed Oct 7, 2024
1 parent 457a2a0 commit a44f713
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 34 deletions.
19 changes: 10 additions & 9 deletions lib/change-log.js
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -184,10 +184,10 @@ const _formatCompositionContext = async function (changes, reqData) {
reqData
)
_formatCompositionValue(curChange, objId, childNodeChange, childNodeChanges)
}
}))
change.valueChangedTo = undefined
}
}
}))
changes.push(...childNodeChanges)
}

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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) {
Expand All @@ -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)
Expand Down Expand Up @@ -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;
Expand Down
61 changes: 36 additions & 25 deletions lib/entity-helper.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const cds = require("@sap/cds")
const DataLoader = require("dataloader");
const LOG = cds.log("change-log")


Expand All @@ -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) {
Expand Down Expand Up @@ -71,21 +82,22 @@ 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) {
let path = field.split('.')
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 {
Expand Down Expand Up @@ -125,19 +137,18 @@ async function getObjectId (reqData, entityName, fields, curObj) {
}



const getDBEntity = (entity) => {
if (typeof entity === 'string') entity = cds.model.definitions[entity]
let proto = Reflect.getPrototypeOf(entity)
if (proto instanceof cds.entity) return proto
}

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()
Expand Down Expand Up @@ -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 = {
Expand Down
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,5 +37,8 @@
"model": "@cap-js/change-tracking"
}
}
},
"dependencies": {
"dataloader": "^2.2.2"
}
}

0 comments on commit a44f713

Please sign in to comment.