diff --git a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts index 7db0b05718..ef2f5ae043 100644 --- a/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts +++ b/packages/nodes-base/nodes/CompareDatasets/CompareDatasets.node.ts @@ -13,10 +13,10 @@ export class CompareDatasets implements INodeType { defaults: { name: 'Compare Datasets' }, // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node inputs: ['main', 'main'], - inputNames: ['Input 1', 'Input 2'], + inputNames: ['Input A', 'Input B'], // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong outputs: ['main', 'main', 'main', 'main'], - outputNames: ['In 1 only', 'Same', 'Different', 'In 2 only'], + outputNames: ['In A only', 'Same', 'Different', 'In B only'], properties: [ { displayName: 'Fields to Match', @@ -33,7 +33,7 @@ export class CompareDatasets implements INodeType { name: 'values', values: [ { - displayName: 'Input 1 Field', + displayName: 'Input A Field', name: 'field1', type: 'string', default: '', @@ -42,7 +42,7 @@ export class CompareDatasets implements INodeType { hint: ' Enter the field name as text', }, { - displayName: 'Input 2 Field', + displayName: 'Input B Field', name: 'field2', type: 'string', default: '', @@ -61,11 +61,11 @@ export class CompareDatasets implements INodeType { default: 'preferInput2', options: [ { - name: 'Use Input 1 Version', + name: 'Use Input A Version', value: 'preferInput1', }, { - name: 'Use Input 2 Version', + name: 'Use Input B Version', value: 'preferInput2', }, { @@ -87,11 +87,11 @@ export class CompareDatasets implements INodeType { default: 'input1', options: [ { - name: 'Input 1 Version', + name: 'Input A Version', value: 'input1', }, { - name: 'Input 2 Version', + name: 'Input B Version', value: 'input2', }, ], @@ -107,7 +107,7 @@ export class CompareDatasets implements INodeType { type: 'string', default: '', // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id - placeholder: 'e.d. id, country', + placeholder: 'e.g. id, country', hint: 'Enter the names of the input fields as text, separated by commas', displayOptions: { show: { @@ -122,6 +122,16 @@ export class CompareDatasets implements INodeType { placeholder: 'Add Option', default: {}, options: [ + { + displayName: 'Fields to Skip Comparing', + name: 'skipFields', + type: 'string', + default: '', + placeholder: 'e.g. updated_at, updated_by', + hint: 'Enter the field names as text, separated by commas', + description: + "Fields that shouldn't be included when checking whether two items are the same", + }, { displayName: 'Disable Dot Notation', name: 'disableDotNotation', @@ -164,14 +174,14 @@ export class CompareDatasets implements INodeType { this.getInputData(0), matchFields.map((pair) => pair.field1 as string), (options.disableDotNotation as boolean) || false, - 'Input 1', + 'Input A', ); const input2 = checkInput( this.getInputData(1), matchFields.map((pair) => pair.field2 as string), (options.disableDotNotation as boolean) || false, - 'Input 2', + 'Input B', ); const resolve = this.getNodeParameter('resolve', 0, '') as string; diff --git a/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts b/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts index cd6e2c3aa7..9f8a7f8f6f 100644 --- a/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts +++ b/packages/nodes-base/nodes/CompareDatasets/GenericFunctions.ts @@ -1,5 +1,5 @@ import { IDataObject, INodeExecutionData } from 'n8n-workflow'; -import { difference, get, intersection, isEmpty, isEqual, set, union } from 'lodash'; +import { difference, get, intersection, isEmpty, isEqual, omit, set, union } from 'lodash'; type PairToMatch = { field1: string; @@ -15,7 +15,8 @@ function compareItems( item1: INodeExecutionData, item2: INodeExecutionData, fieldsToMatch: PairToMatch[], - resolve?: string, + resolve: string, + skipFields: string[], ) { const keys = {} as IDataObject; fieldsToMatch.forEach((field) => { @@ -38,6 +39,7 @@ function compareItems( const differentKeys = difference(allUniqueKeys, sameKeys); const different: IDataObject = {}; + const skipped: IDataObject = {}; differentKeys.forEach((key) => { switch (resolve) { @@ -50,11 +52,17 @@ function compareItems( default: const input1 = item1.json[key] || null; const input2 = item2.json[key] || null; - different[key] = { input1, input2 }; + if (skipFields.includes(key)) { + skipped[key] = { input1, input2 }; + } else { + different[key] = { input1, input2 }; + } } }); - return { json: { keys, same, different } } as INodeExecutionData; + return { + json: { keys, same, different, ...(!isEmpty(skipped) && { skipped }) }, + } as INodeExecutionData; } function combineItems( @@ -157,6 +165,7 @@ export function findMatches( const disableDotNotation = (options.disableDotNotation as boolean) || false; const multipleMatches = (options.multipleMatches as string) || 'first'; + const skipFields = ((options.skipFields as string) || '').split(',').map((field) => field.trim()); const filteredData = { matched: [] as EntryMatches[], @@ -214,7 +223,14 @@ export function findMatches( let entryCopy: INodeExecutionData | undefined; entryMatches.matches.forEach((match) => { - if (isEqual(entryMatches.entry.json, match.json)) { + let entryFromInput1 = entryMatches.entry.json; + let entryFromInput2 = match.json; + + if (skipFields.length) { + entryFromInput1 = omit(entryFromInput1, skipFields); + entryFromInput2 = omit(entryFromInput2, skipFields); + } + if (isEqual(entryFromInput1, entryFromInput2)) { if (!entryCopy) entryCopy = match; } else { switch (options.resolve) { @@ -237,7 +253,13 @@ export function findMatches( break; default: different.push( - compareItems(entryMatches.entry, match, fieldsToMatch, options.resolve as string), + compareItems( + entryMatches.entry, + match, + fieldsToMatch, + options.resolve as string, + skipFields, + ), ); } } @@ -274,6 +296,12 @@ export function checkInput( disableDotNotation: boolean, inputLabel: string, ) { + if (input.some((item) => isEmpty(item.json))) { + input = input.filter((item) => !isEmpty(item.json)); + } + if (input.length === 0) { + return input; + } for (const field of fields) { const isPresent = (input || []).some((entry) => { if (disableDotNotation) {