【发布时间】:2018-09-28 19:08:51
【问题描述】:
我有 2 个对象,1 个来自 SQL 数据库,另一个来自 JSON REST API。在使用来自 api 的新数据更新数据库中的一行之前,我不仅要检查 ID 是否已经出现在数据库中(这意味着更新而不是插入),还要检查大部分属性。这样做的原因是,当它确实在数据库端更新时,会添加一个额外的“lastUpdate”日期时间以供以后在 PowerBI 中进行处理,如果我只检查 ID,那么“lastUpdate”将被触发每一个来自 API 的条目已经在数据库中的时间,即使它的属性实际上没有更新。每边的单个对象(它们以数组的形式出现)如下:
案例和说明:
重要提示:此部分是根据 Nina Scholz(现在)接受的解决方案的要求添加的。请在阅读时牢记这一点。
-
如果路径在 API 端不能完全遍历(即它的父级为 null),则它应该返回 Null。例如,DB 端 callerLocationID 的路径在 API 上将是
callerLocation.id,但是当没有设置 callerLocation 时,父callerLocation已经是null,从而使id无法访问 如果一条路径在两边都可以完全通过,则需要比较这些值。
如果无法在 API 端遍历路径,则该路径应为
null。例如,我可以将callerLocationID: null与callerLocation.id进行比较,因为后者是null,而null将与null相同一旦遍历所有路径并比较所有值,我需要知道“是的,它们都相同”(
true)或“不,它们不相同”(false)。无需知道在哪里如果它们不相同,则发送整个对象以进行更新。DB端的路径基本是一成不变的,如果有
null那是因为数据库允许这样,可以接受DB 端以
ID结尾的所有属性(incidentID除外)都引用 API 中的单个嵌套id值。例如callerID引用caller.id、callerBranchID引用callerBranch.id和operatorID引用operator.id-
一些例外情况是:
-
impact引用impact.name -
urgency引用urgency.name -
priority参考priority.name -
duration引用duration.name -
escalationOperator引用escalationOperator.id
-
API 中的所有
optionalField内容都可以忽略
// Database Object
{
incidentID: '0dc1a10f-2899-485a-b814-f72f29c9a15a',
status: 'secondLine',
briefDescription: 'Support niet bereikbaar',
callDate: '2018-04-10T19:01:00.000Z',
lastUpdate: '2018-04-18T14:02:17.000Z',
number: 'M1804 021',
request: '10-04-2018 21:02 Middelkoop, Paul: \nIk kan de servicedesk niet bereiken, telkens in gesprek',
callerID: '4e723042-0037-4e05-a362-e65c620ba734',
callerBranchID: 'f66e7804-b57a-4418-a991-997e574ead29',
callerLocationID: null,
externalNumber: null,
categoryID: 'cafb0af8-e43a-4391-ac9e-a0345abbcc4f',
subcategoryID: 'f56099a9-7c60-45e3-94b4-6555a79d4bd7',
callTypeID: '04b678a6-791e-4662-9bc8-97573555f15e',
entryTypeID: 'a9c486fd-a93e-565e-bfeb-17619fafe1a8',
branchID: null,
locationID: null,
impact: null,
urgency: null,
priority: null,
duration: null,
operatorID: 'a17aba85-13a7-4ac6-8c57-693a512b633e',
operatorGroupID: 'a17aba85-13a7-4ac6-8c57-693a512b633e',
supplierID: null,
targetDate: '2019-04-10T15:30:00.000Z',
onHold: false,
onHoldDate: null,
onHoldDuration: 0,
feedbackMessage: null,
feedbackRating: null,
processingStatus: 'Afgemeld',
completed: true,
completedDate: '2018-04-10T19:09:00.000Z',
closed: true,
closedDate: null,
closureCode: null,
creatorID: '226082ea-8d74-4dee-ae1e-74c33c883792',
creationDate: '2018-04-10T19:02:34.000Z',
timeSpent: 0,
timeSpentFirstLine: 0,
timeSpentSecondLineAndPartials: 0,
costs: 0,
escalationStatus: null,
escalationReason: null,
escalationOperator: null,
modifier: '226082ea-8d74-4dee-ae1e-74c33c883792',
modificationDate: '2018-04-10T19:12:08.000Z',
expectedTimeSpent: 0,
majorCall: false,
majorCallID: null,
publishToSSD: false,
monitored: false,
archivingReason: null
}
和
// API Object
{
id: '0dc1a10f-2899-485a-b814-f72f29c9a15a',
status: 'secondLine',
number: 'M1804 021',
request: '10-04-2018 21:02 Middelkoop, Paul: \nIk kan de servicedesk niet bereiken, telkens in gesprek',
requests: '/tas/api/incidents/id/0dc1a10f-2899-485a-b814-f72f29c9a15a/requests',
action: '/tas/api/incidents/id/0dc1a10f-2899-485a-b814-f72f29c9a15a/actions',
attachments: '/tas/api/incidents/id/0dc1a10f-2899-485a-b814-f72f29c9a15a/attachments',
caller: {
id: '4e723042-0037-4e05-a362-e65c620ba734',
dynamicName: 'Mafficioli del Castelletto, Richard',
branch: {
id: 'f66e7804-b57a-4418-a991-997e574ead29',
name: 'Ask Roger! Delft',
clientReferenceNumber: '',
timeZone: 'Europe/Amsterdam',
extraA: null,
extraB: null
}
},
callerBranch: {
id: 'f66e7804-b57a-4418-a991-997e574ead29',
name: 'Ask Roger! Delft',
clientReferenceNumber: '',
timeZone: 'Europe/Amsterdam',
extraA: null,
extraB: null
},
callerLocation: null,
branchExtraFieldA: null,
branchExtraFieldB: null,
briefDescription: 'Support niet bereikbaar',
externalNumber: '',
category: {
id: 'cafb0af8-e43a-4391-ac9e-a0345abbcc4f',
name: 'Communicatie'
},
subcategory: {
id: 'f56099a9-7c60-45e3-94b4-6555a79d4bd7',
name: 'Vaste telefonie'
},
callType: {
id: '04b678a6-791e-4662-9bc8-97573555f15e',
name: 'Klacht'
},
entryType: {
id: 'a9c486fd-a93e-565e-bfeb-17619fafe1a8',
name: 'Mondeling'
},
object: null,
branch: null,
location: null,
impact: null,
urgency: null,
priority: null,
duration: null,
targetDate: '2019-04-10T15:30:00.000+0000',
onHold: false,
onHoldDate: null,
onHoldDuration: 0,
feedbackMessage: null,
feedbackRating: null,
operator: {
id: 'a17aba85-13a7-4ac6-8c57-693a512b633e',
status: 'operatorGroup',
name: 'Systeembeheer'
},
operatorGroup: {
id: 'a17aba85-13a7-4ac6-8c57-693a512b633e',
name: 'Systeembeheer'
},
supplier: null,
processingStatus: {
id: '70b2967d-e248-4ff9-a632-ec044410d5a6',
name: 'Afgemeld'
},
completed: true,
completedDate: '2018-04-10T19:09:00.000+0000',
closed: true,
closedDate: '2018-04-10T19:12:00.000+0000',
closureCode: null,
timeSpent: 0,
timeSpentFirstLine: 0,
timeSpentSecondLineAndPartials: 0,
costs: 0,
escalationStatus: null,
escalationReason: null,
escalationOperator: null,
callDate: '2018-04-10T19:01:00.000+0000',
creator: {
id: '226082ea-8d74-4dee-ae1e-74c33c883792',
name: 'Middelkoop, Paul'
},
creationDate: '2018-04-10T19:02:34.000+0000',
modifier: {
id: '226082ea-8d74-4dee-ae1e-74c33c883792',
name: 'Middelkoop, Paul'
},
modificationDate: '2018-04-10T19:12:08.000+0000',
majorCall: false,
majorCallObject: null,
publishToSsd: false,
monitored: false,
expectedTimeSpent: 0,
archivingReason: null,
optionalFields1: {
boolean1: false,
boolean2: false,
boolean3: false,
boolean4: false,
boolean5: false,
number1: 0,
number2: 0,
number3: 0,
number4: 0,
number5: 0,
date1: null,
date2: null,
date3: null,
date4: null,
date5: null,
text1: '',
text2: '',
text3: '',
text4: '',
text5: '',
memo1: null,
memo2: null,
memo3: null,
memo4: null,
memo5: null,
searchlist1: null,
searchlist2: null,
searchlist3: null,
searchlist4: null,
searchlist5: null
},
optionalFields2: {
boolean1: false,
boolean2: false,
boolean3: false,
boolean4: false,
boolean5: false,
number1: 0,
number2: 0,
number3: 0,
number4: 0,
number5: 0,
date1: null,
date2: null,
date3: null,
date4: null,
date5: null,
text1: '',
text2: '',
text3: '',
text4: '',
text5: '',
memo1: null,
memo2: null,
memo3: null,
memo4: null,
memo5: null,
searchlist1: null,
searchlist2: null,
searchlist3: null,
searchlist4: null,
searchlist5: null
}
}
到目前为止我已经做了什么
遍历 API 对象数组,并为每一个对象检查 ID 是否是使用 Fuse.JS 的数据库对象数组(阈值为 0,仅用于完美匹配)
结合使用 1 的结果和 UnderscoreJS 中的 .first、.keys 和 .pick 方法来确定当前迭代中的 2 个对象之间哪些键是相同的,以便快速检查这些对象
// tdIncidents is the array of objects from the API
// dbIncidents is the array of objects from the database
// tdinci is my iterator, consider it the "i" in the for loop
// at this point it has already been confirmed that both dbIncidents and tdIncidents have at least 1 entry thus using [0] won't give any problems
const db = _.first(fuse.search(tdIncidents[tdinci].id)),
td = tdIncidents[tdinci],
dbKeys = _.keys(dbIncidents[0]),
tdKeys = _.keys(tdIncidents[0]),
identicalKeysTd = _.pick(td, (value, key) => dbKeys.includes(key)),
identicalKeysDb = _.pick(db, (value, key) => tdKeys.includes(key));
identicalKeysTd 将导致:
{ status: 'secondLine',
number: 'M1804 021',
request: '10-04-2018 21:02 Middelkoop, Paul: \nIk kan de servicedesk niet bereiken, telkens in gesprek',
briefDescription: 'Support niet bereikbaar',
externalNumber: '',
impact: null,
urgency: null,
priority: null,
duration: null,
targetDate: '2019-04-10T15:30:00.000+0000',
onHold: false,
onHoldDate: null,
onHoldDuration: 0,
feedbackMessage: null,
feedbackRating: null,
processingStatus: { id: '70b2967d-e248-4ff9-a632-ec044410d5a6', name: 'Afgemeld' },
completed: true,
completedDate: '2018-04-10T19:09:00.000+0000',
closed: true,
closedDate: '2018-04-10T19:12:00.000+0000',
closureCode: null,
timeSpent: 0,
timeSpentFirstLine: 0,
timeSpentSecondLineAndPartials: 0,
costs: 0,
escalationStatus: null,
escalationReason: null,
escalationOperator: null,
callDate: '2018-04-10T19:01:00.000+0000',
creationDate: '2018-04-10T19:02:34.000+0000',
modifier:
{ id: '226082ea-8d74-4dee-ae1e-74c33c883792',
name: 'Middelkoop, Paul' },
modificationDate: '2018-04-10T19:12:08.000+0000',
majorCall: false,
monitored: false,
expectedTimeSpent: 0,
archivingReason: null }
identicalKeysDb 将导致:
{ status: 'secondLine',
briefDescription: 'Support niet bereikbaar',
callDate: '2018-04-10T19:01:00.000Z',
number: 'M1804 021',
request: '10-04-2018 21:02 Middelkoop, Paul: \nIk kan de servicedesk niet bereiken, telkens in gesprek',
externalNumber: null,
impact: null,
urgency: null,
priority: null,
duration: null,
targetDate: '2019-04-10T15:30:00.000Z',
onHold: false,
onHoldDate: null,
onHoldDuration: 0,
feedbackMessage: null,
feedbackRating: null,
processingStatus: 'Afgemeld',
completed: true,
completedDate: '2018-04-10T19:09:00.000Z',
closed: true,
closedDate: null,
closureCode: null,
creationDate: '2018-04-10T19:02:34.000Z',
timeSpent: 0,
timeSpentFirstLine: 0,
timeSpentSecondLineAndPartials: 0,
costs: 0,
escalationStatus: null,
escalationReason: null,
escalationOperator: null,
modifier: '226082ea-8d74-4dee-ae1e-74c33c883792',
modificationDate: '2018-04-10T19:12:08.000Z',
expectedTimeSpent: 0,
majorCall: false,
monitored: false,
archivingReason: null }
- 此时我想我可以使用 UnderscoreJS 的
.isEqual(_.isEqual(identicalKeysDb, identicalKeysTd)) 检查这两个identicalKeys对象的相等性,但无济于事。除了我在数据库中直接存储了一些没有附加“ID”的键(这可以在数据库端修复)之外,更紧迫的问题是数据库将为externalNumber等值提供null,但是API 将提供''。
在最近的一次尝试中,我尝试了 ES6、plain JS 和 UnderscoreJS 中的许多其他功能(太多了,而且代码早已被删除并且在我的“撤消”链上不再可用),但我找不到任何有效的方法,我真的不想硬编码一个巨大的if () 来检查每个属性与其对应的属性。我不介意需要一些节点包来简化这种比较,所以如果这是解决方案,请也分享一下。
那些实际上发生变化的对象我推送到一个名为existingIncidents 的数组中,该数组稍后会与任何新事件一起返回。这发生如下:
async filterIncidents() {
const dbIncidents = await this.getDbIncidents(this.lastFetchTimestamp),
fuseOpts = {
'shouldSort': true,
'findAllMatches': true,
'threshold': 0,
'location': 0,
'distance': 100,
'maxPatternLength': 36,
'minMatchCharLength': 36,
'keys': ['incidentID']
},
fuse = new Fuse(dbIncidents, fuseOpts),
tdIncidents = await this.getTdIncidents(this.lastFetchTimestamp);
const existIncidents = [],
newIncidents = [];
if (!dbIncidents.length) {
for (const tdinci in tdIncidents) {
newIncidents.push(tdIncidents[tdinci]);
}
} else {
for (const tdinci in tdIncidents) {
if (fuse.search(tdIncidents[tdinci].id).length) {
// The value checking magic I need has to happen here. Some pseudo code:
// if (values are different) {
existIncidents.push(tdIncidents[tdinci]);
// } else {
// do nothing
// }
} else {
newIncidents.push(tdIncidents[tdinci]);
}
}
}
return {
'new': newIncidents,
'existing': existIncidents
};
}
编辑:在底部添加了整个功能
最终编辑:我在此处将一个 runkit 链接转储到 Nina Schulz 的最终解决方案实施,因为我必须根据我的确切用例对其进行调整,并且分享是关怀,也许它会在未来对其他人有所帮助。 固定链接:https://runkit.com/favna/so-compare-objects
【问题讨论】:
-
可能有点矫枉过正,但我认为一个大的 if 语句将是处理边缘情况的最快和最灵活的方法。以后可能会对其进行重构以使其更易于维护。
标签: javascript arrays json node.js object