From bef7d4723041ce188fec7ba74b241013dd431294 Mon Sep 17 00:00:00 2001 From: Sebastian Listl <s.listl@adito.de> Date: Wed, 30 Sep 2020 10:37:25 +0200 Subject: [PATCH] #1065733 Duplicate contact merge refactoring --- process/DuplicateScanner_lib/process.js | 238 ++++++++++-------------- 1 file changed, 99 insertions(+), 139 deletions(-) diff --git a/process/DuplicateScanner_lib/process.js b/process/DuplicateScanner_lib/process.js index 23be5bb109..9c340dd052 100644 --- a/process/DuplicateScanner_lib/process.js +++ b/process/DuplicateScanner_lib/process.js @@ -613,14 +613,10 @@ DuplicateScannerUtils.TranslateEntityToIndexFields = function(pEntityName, pEnti * * @param {String} pSourceContactId The contact to be integrated into another * @param {String} pTargetContactId The contact in which the source gets integrated - * @returns {String} + * @returns {Boolean} if the merge was sucessful */ DuplicateScannerUtils.MergePerson = function(pSourceContactId, pTargetContactId) { - var updateStatementsCurrentAlias = []; - var updateStatementsSystemAlias = []; - var deleteStatements = []; - var sourcePersonId = newSelect("PERSON_ID") .from("CONTACT") .where("CONTACT.CONTACTID", pSourceContactId) @@ -629,41 +625,29 @@ DuplicateScannerUtils.MergePerson = function(pSourceContactId, pTargetContactId) .from("CONTACT") .where("CONTACT.CONTACTID", pTargetContactId) .cell(); - - var tableInfosCurrentAlias = _DuplicateScannerUtils._getMergeUpdateTableInfosCurrentAlias(); - var tableInfosSystemAlias = _DuplicateScannerUtils._getMergeUpdateTableInfosSystemAlias(); - - updateStatementsCurrentAlias.push(_DuplicateScannerUtils._buildUpdateResetStandardCommunications(pSourceContactId)); - updateStatementsCurrentAlias = updateStatementsCurrentAlias.concat(_DuplicateScannerUtils._buildUpdateContactIdStatements(tableInfosCurrentAlias, pSourceContactId, pTargetContactId)); - updateStatementsCurrentAlias = updateStatementsCurrentAlias.concat(_DuplicateScannerUtils._buildUpdateAttachParticipantsToNewContactQuery("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", pSourceContactId, pTargetContactId)); - - updateStatementsSystemAlias = updateStatementsSystemAlias.concat(_DuplicateScannerUtils._buildUpdateContactIdStatements(tableInfosSystemAlias, pSourceContactId, pTargetContactId, SqlUtils.getSystemAlias())); - - deleteStatements = deleteStatements.concat(_DuplicateScannerUtils._buildDeleteRemoveObsoleteParticipantsRecordsQuery("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", pSourceContactId, pTargetContactId)); + var isLinkedDataUpdated = _DuplicateScannerUtils._migrateLinkedContactData(pSourceContactId, pTargetContactId); + var isParticipantsUpdated = _DuplicateScannerUtils._migrateParticipantsToNewContact("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", + pSourceContactId, pTargetContactId); + + var deleteStatements = []; if (sourcePersonId != targetPersonId) - deleteStatements.push(["PERSON", newWhere("PERSON.PERSONID", sourcePersonId).build()]); + deleteStatements.push(newWhere("PERSON.PERSONID", sourcePersonId).buildDeleteStatement()); - deleteStatements.push(["CONTACT", newWhere("CONTACT.CONTACTID", pSourceContactId).build()]); + deleteStatements.push(newWhere("CONTACT.CONTACTID", pSourceContactId).buildDeleteStatement()); deleteStatements = deleteStatements.concat(_DuplicateScannerUtils._buildDeleteCachedUnrelatedDuplicateQuery(pSourceContactId)); - for (var i = 0; i < updateStatementsCurrentAlias.length; i++) { - if(updateStatementsCurrentAlias[i][0] == "RemoveThisItem") //see _DuplicateScannerUtils._buildUpdateContactIdStatements for further information - updateStatementsCurrentAlias.splice(i,1); - } - //update binary var metaData = db.getBinaryMetadata("CONTACT", "DOCUMENT", pSourceContactId, true, SqlUtils.getBinariesAlias()); - for (let i = 0; i < metaData.length; i++) { - var updateBinary = db.updateBinaryAssignment(metaData[i]["id"], "CONTACT", "DOCUMENT", pTargetContactId, SqlUtils.getBinariesAlias()); - } - var affectedRowsCurrentAlias = db.updates(updateStatementsCurrentAlias); - var affectedRowsSystemAlias = db.updates(updateStatementsSystemAlias, SqlUtils.getSystemAlias()); + metaData.forEach(function (binaryMetaData) + { + db.updateBinaryAssignment(binaryMetaData.id, "CONTACT", "DOCUMENT", pTargetContactId, SqlUtils.getBinariesAlias()); + }); var deletedRows = db.deletes(deleteStatements) DuplicateScannerUtils.DeleteCachedDuplicate(pSourceContactId); - return (affectedRowsCurrentAlias > 0 && deletedRows > 0); + return ((isLinkedDataUpdated || isParticipantsUpdated) && deletedRows > 0); } DuplicateScannerUtils.CreateMergeSuccessActivity = function(pSourceContactId, pTargetContactId, pCurrentContactId, pContext) @@ -687,30 +671,30 @@ DuplicateScannerUtils.MergeOrganisation = function(pSourceContactId, pTargetCont let deleteStatements = []; var sourceOrganisationId = newSelect("ORGANISATION_ID") - .from("CONTACT") - .where("CONTACT.CONTACTID", pSourceContactId) - .cell(); - - var tableInfosCurrentAlias = _DuplicateScannerUtils._getMergeUpdateTableInfosCurrentAlias(); - var tableInfosSystemAlias = _DuplicateScannerUtils._getMergeUpdateTableInfosSystemAlias(); - - updateStatementsCurrentAlias = updateStatementsCurrentAlias.concat(_DuplicateScannerUtils._buildUpdateResetStandardCommunications(pSourceContactId)); - updateStatementsCurrentAlias = updateStatementsCurrentAlias.concat(_DuplicateScannerUtils._buildUpdateContactIdStatements(tableInfosCurrentAlias, pSourceContactId, pTargetContactId)); - updateStatementsCurrentAlias = updateStatementsCurrentAlias.concat(_DuplicateScannerUtils._buildUpdateAttachParticipantsToNewContactQuery("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", pSourceContactId, pTargetContactId)); - - updateStatementsSystemAlias = updateStatementsSystemAlias.concat(_DuplicateScannerUtils._buildUpdateContactIdStatements(tableInfosSystemAlias, pSourceContactId, pTargetContactId)); - - deleteStatements = deleteStatements.concat(_DuplicateScannerUtils._buildDeleteRemoveObsoleteParticipantsRecordsQuery("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", pSourceContactId, pTargetContactId)); + .from("CONTACT") + .where("CONTACT.CONTACTID", pSourceContactId) + .cell(); + + var isLinkedDataUpdated = _DuplicateScannerUtils._migrateLinkedContactData(pSourceContactId, pTargetContactId); + var isParticipantsUpdated = _DuplicateScannerUtils._migrateParticipantsToNewContact("CAMPAIGNPARTICIPANT", "CONTACT_ID", "CAMPAIGN_ID", + pSourceContactId, pTargetContactId); + + var deleteStatements = []; + deleteStatements.push(newWhere("CONTACT.CONTACTID", pSourceContactId).buildDeleteStatement()); deleteStatements = deleteStatements.concat(_DuplicateScannerUtils._buildDeleteOrganisationAndContactQuery(sourceOrganisationId, pSourceContactId)); deleteStatements = deleteStatements.concat(_DuplicateScannerUtils._buildDeleteCachedUnrelatedDuplicateQuery(pSourceContactId)); - - let affectedRowsCurrentAlias = db.updates(updateStatementsCurrentAlias); - let affectedRowsSystemAlias = db.updates(updateStatementsSystemAlias, SqlUtils.getSystemAlias()); - let deletedRows = db.deletes(deleteStatements) + + //update binary + var metaData = db.getBinaryMetadata("CONTACT", "DOCUMENT", pSourceContactId, true, SqlUtils.getBinariesAlias()); + metaData.forEach(function (binaryMetaData) + { + db.updateBinaryAssignment(binaryMetaData.id, "CONTACT", "DOCUMENT", pTargetContactId, SqlUtils.getBinariesAlias()); + }); + var deletedRows = db.deletes(deleteStatements) DuplicateScannerUtils.DeleteCachedDuplicate(pSourceContactId); - return (affectedRowsCurrentAlias > 0 && deletedRows >= 2); + return ((isLinkedDataUpdated || isParticipantsUpdated) && deletedRows >= 2); } /* @@ -1092,7 +1076,7 @@ _DuplicateScannerUtils._createInsertDuplicatesClusterQuery = function (pDuplicat let duplicatesToInsertQueries = []; let cols = ["ID", "CLUSTERID", "DUPLICATEID", "TARGET_ENTITY"]; - if(pClusterId == undefined || pClusterId == null || pClusterId == "") + if (!pClusterId) pClusterId = util.getNewUUID(); for (let i = 0; i < pDuplicatesRay.length; i++) @@ -1120,36 +1104,24 @@ _DuplicateScannerUtils._deleteDuplicateClusters = function () * This is because otherwise there would now be in total two "participants" in the same "group" as opposed to one before. * Also if they already are in the same "group" those records shouldn't be updated because it would lead to the same outcome. * - * Mandatory: All records ignored for the time being have to be deleted aswell! See #_DuplicateScannerUtils._buildRemoveObsoleteParticipantsRecordsDeleteQuery - * @returns {String[]} Query to update records + * @returns {Boolean} If records have been updated */ -_DuplicateScannerUtils._buildUpdateAttachParticipantsToNewContactQuery = function (pTableName, pContactIdColumn, pAssignableIdColumn, pSourceContactId, pTargetContactId, updateStatements) -{ - var selectAssignableIdsOfTargetContactQuery = newSelect(pAssignableIdColumn) - .from(pTableName) - .where([pTableName, pContactIdColumn], pTargetContactId); - - var subSelectTable = newSelect(["subselect." + pAssignableIdColumn]).from(selectAssignableIdsOfTargetContactQuery,"subselect"); - - let updateCondition = newWhere([pTableName, pAssignableIdColumn], subSelectTable, SqlBuilder.NOT_IN()) - .and([pTableName, pContactIdColumn], pSourceContactId) - - return [[pTableName, [pContactIdColumn], null, [pTargetContactId], updateCondition.build()]]; -} - - -_DuplicateScannerUtils._buildDeleteRemoveObsoleteParticipantsRecordsQuery = function (pTableName, pContactIdColumn, pAssignableIdColumn, pSourceContactId, pTargetContactId, updateStatements) +_DuplicateScannerUtils._migrateParticipantsToNewContact = function (pTableName, pContactIdColumn, pAssignableIdColumn, pSourceContactId, pTargetContactId) { - var selectAssignableIdsOfTargetContactQuery = newSelect(pAssignableIdColumn) - .from(pTableName) - .where([pTableName, pContactIdColumn], pTargetContactId); - - var subSelectTable = newSelect(["subselect." + pAssignableIdColumn]).from(selectAssignableIdsOfTargetContactQuery,"subselect"); - - let deleteCondition = newWhere([pTableName, pAssignableIdColumn], subSelectTable, SqlBuilder.IN()) - .and([pTableName, pAssignableIdColumn], pSourceContactId) + var excludedIds = newSelect(pAssignableIdColumn) + .from(pTableName) + .where([pTableName, pContactIdColumn], pTargetContactId); - return [[pTableName, deleteCondition.build()]]; + var updateCount = newWhere([pTableName, pAssignableIdColumn], excludedIds, SqlBuilder.NOT_IN()) + .and([pTableName, pContactIdColumn], pSourceContactId) + .updateFields(new Map().set(pContactIdColumn, pTargetContactId), pTableName); + + var deleteCount = newWhere([pTableName, pAssignableIdColumn], excludedIds, SqlBuilder.IN()) + .and([pTableName, pContactIdColumn], pSourceContactId) + .tableName(pTableName) + .deleteData(); + + return updateCount > 0 || deleteCount > 0; } _DuplicateScannerUtils._buildDeleteOrganisationAndContactQuery = function(pSourceOrganisationId, pSourceContactId) @@ -1185,72 +1157,70 @@ _DuplicateScannerUtils._getIgnoreSourceRecordPattern = function(pRecordIdValueTo return null; } -_DuplicateScannerUtils._buildUpdateContactIdStatements = function(pTableInfos, pSourceContactId, pTargetContactId, pAlias) +_DuplicateScannerUtils._migrateLinkedContactData = function (pSourceContactId, pTargetContactId) { - return pTableInfos.map(function ([tableName, columnName, additionalCondition]) + var updateStatements = new Map(); + var resetStandardCommunications = _DuplicateScannerUtils._buildUpdateResetStandardCommunications(pSourceContactId); + var currentAlias = db.getCurrentAlias(); + updateStatements.set(currentAlias, [resetStandardCommunications]); + _DuplicateScannerUtils._getLinkedTableInfos.forEach(function ([tableName, columnName, additionalCondition, alias]) { + if (!alias) + alias = currentAlias; + var updateValues = {}; updateValues[columnName] = pTargetContactId; - var exclude = false; - if(tableName == "COMMUNICATION") + var targetStandard = newSelect("CONTACT.ADDRESS_ID") + .from("CONTACT") + .where("CONTACT.CONTACTID", pTargetContactId) + .cell(); + + //set the standardaddress of the sourceContact as standard of the targetContact if it doesn't have one set yet + if(!targetStandard) { - var sourceComm = newSelect("COMMUNICATION.ADDR") - .from("COMMUNICATION") - .where("COMMUNICATION.CONTACT_ID", pSourceContactId) - .arrayColumn(); - - var targetComm = newSelect("COMMUNICATION.ADDR") - .from("COMMUNICATION") - .where("COMMUNICATION.CONTACT_ID", pTargetContactId) - .arrayColumn(); - //set exclude to true if both of the datatasets have the exact same address (we don't want one person to have the same address twice) - if(sourceComm.length > 0 && targetComm.length > 0 ) - { - for (var i = 0; i < sourceComm.length; i++) { - for (var x = 0; x < targetComm.length; x++) { - if(sourceComm[i] == targetComm[x]) - exclude = true; - } - } - } - - var targetStandard = newSelect("CONTACT.ADDRESS_ID") - .from("CONTACT") - .where("CONTACT.CONTACTID", pTargetContactId) - .cell(); - - //set the standardaddress of the sourceContact as standard of the targetContact if it doesn't have one set yet - if(!targetStandard) - { - var sourceStandard = newSelect("CONTACT.ADDRESS_ID") - .from("CONTACT") - .where("CONTACT.CONTACTID", pSourceContactId) - .cell(); - newWhere("CONTACT.CONTACTID", pTargetContactId).updateData(true, "CONTACT", ["ADDRESS_ID"], null, [sourceStandard]); - } + var sourceStandard = newSelect("CONTACT.ADDRESS_ID") + .from("CONTACT") + .where("CONTACT.CONTACTID", pSourceContactId) + .cell(); + newWhere("CONTACT.CONTACTID", pTargetContactId).updateFields({"ADDRESS_ID": sourceStandard}); } - if(exclude) - { - newWhere("COMMUNICATION.CONTACT_ID", pSourceContactId).deleteData(true, "COMMUNICATION"); //delete the address of the sourceContact - return ["RemoveThisItem"] - } + var updateCondition = newWhere([tableName, columnName], pSourceContactId).andIfSet(additionalCondition); + + //don't use communications that the target already has + if (tableName == "COMMUNICATION") + updateCondition.and(null, newSelect("targetComm.COMMUNICATIONID") + .from("COMMUNICATION", "targetComm") + .where(["COMMUNICATION", "CONTACT_ID", "targetComm"], pTargetContactId) + .and("targetComm.ADDR = COMMUNICATION.ADDR"), + SqlBuilder.NOT_EXISTS()); + + var updateStatement = updateCondition.buildUpdateStatement(updateValues, tableName); + if (updateStatements.has(alias)) + updateStatements.get(alias).push(updateStatement); else - { - return newWhere([tableName, columnName], pSourceContactId, undefined, undefined, pAlias) - .andIfSet(additionalCondition) - .buildUpdateStatement(updateValues, tableName) - } + updateStatements.set(alias, [updateStatement]); + }); + + var totalChanges = 0; + + updateStatements.forEach(function (statements, alias) + { + totalChanges += db.updates(statements, alias); }); + + totalChanges += newWhere("COMMUNICATION.CONTACT_ID", pSourceContactId).deleteData(); //delete leftover communications from the source contact + + return totalChanges > 0; } /* * Contains all Tables and their fields which may contain the contact id to be replaced for the data alias * - * @returns {String[[]]} Array in the format [TableName, ContactIdColumnName, AdditionalCondition] + * @returns {String[[]]} Array in the format [TableName, ContactIdColumnName, AdditionalCondition, alias] */ -_DuplicateScannerUtils._getMergeUpdateTableInfosCurrentAlias = function() +_DuplicateScannerUtils._getLinkedTableInfos = function() { return[ ["AB_APPOINTMENTLINK", "OBJECT_ROWID", ""], @@ -1284,22 +1254,12 @@ _DuplicateScannerUtils._getMergeUpdateTableInfosCurrentAlias = function() ["DSGVOINFO", "CONTACT_ID", ""], ["TIMETRACKING", "CONTACT_ID", ""], ["ACTIVITYLINK", "OBJECT_ROWID", ""], - ["AB_ATTRIBUTERELATION", "OBJECT_ROWID", ""] + ["AB_ATTRIBUTERELATION", "OBJECT_ROWID", ""], + + ["ASYS_CALENDARLINK", "DBID", "", SqlUtils.getSystemAlias()] ]; } -/* - * Contains all Tables and their fields which may contain the contact id to be replaced for the system alias - * in the past ASYS_BINARY had also been updated like this, but know we are using - * the function db.updateBinaryAssignment (see also DuplicateScannerUtils.MergePerson). - * - * @returns {String[[]]} Array in the format [TableName, ContactIdColumnName, AdditionalCondition] - */ -_DuplicateScannerUtils._getMergeUpdateTableInfosSystemAlias = function() -{ - return [["ASYS_CALENDARLINK", "DBID", ""]]; -} - /* * Returns wether or not a value should be substring'd * -- GitLab