import { IExecuteFunctions } from 'n8n-core'; import { IDataObject, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow'; import { checkInput, checkMatchFieldsInput, findMatches } from './GenericFunctions'; export class CompareDatasets implements INodeType { description: INodeTypeDescription = { displayName: 'Compare Datasets', name: 'compareDatasets', icon: 'file:compare.svg', group: ['transform'], version: 1, description: 'Compare two inputs for changes', defaults: { name: 'Compare Datasets' }, // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node inputs: ['main', 'main'], inputNames: ['Input A', 'Input B'], // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong outputs: ['main', 'main', 'main', 'main'], outputNames: ['In A only', 'Same', 'Different', 'In B only'], properties: [ { displayName: 'Fields to Match', name: 'mergeByFields', type: 'fixedCollection', placeholder: 'Add Fields to Match', default: { values: [{ field1: '', field2: '' }] }, typeOptions: { multipleValues: true, }, options: [ { displayName: 'Values', name: 'values', values: [ { displayName: 'Input A Field', name: 'field1', type: 'string', default: '', // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', }, { displayName: 'Input B Field', name: 'field2', type: 'string', default: '', // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id', hint: ' Enter the field name as text', }, ], }, ], }, { displayName: 'When There Are Differences', name: 'resolve', type: 'options', default: 'preferInput2', options: [ { name: 'Use Input A Version', value: 'preferInput1', }, { name: 'Use Input B Version', value: 'preferInput2', }, { name: 'Use a Mix of Versions', value: 'mix', description: 'Output uses different inputs for different fields', }, { name: 'Include Both Versions', value: 'includeBoth', description: 'Output contains all data (but structure more complex)', }, ], }, { displayName: 'Prefer', name: 'preferWhenMix', type: 'options', default: 'input1', options: [ { name: 'Input A Version', value: 'input1', }, { name: 'Input B Version', value: 'input2', }, ], displayOptions: { show: { resolve: ['mix'], }, }, }, { displayName: 'For Everything Except', name: 'exceptWhenMix', type: 'string', default: '', // eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id placeholder: 'e.g. id, country', hint: 'Enter the names of the input fields as text, separated by commas', displayOptions: { show: { resolve: ['mix'], }, }, }, { displayName: 'Options', name: 'options', type: 'collection', 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', type: 'boolean', default: false, description: 'Whether to disallow referencing child fields using `parent.child` in the field name', }, { displayName: 'Multiple Matches', name: 'multipleMatches', type: 'options', default: 'first', options: [ { name: 'Include First Match Only', value: 'first', description: 'Only ever output a single item per match', }, { name: 'Include All Matches', value: 'all', description: 'Output multiple items if there are multiple matches', }, ], }, ], }, ], }; async execute(this: IExecuteFunctions): Promise { const matchFields = checkMatchFieldsInput( this.getNodeParameter('mergeByFields.values', 0, []) as IDataObject[], ); const options = this.getNodeParameter('options', 0, {}); const input1 = checkInput( this.getInputData(0), matchFields.map((pair) => pair.field1), (options.disableDotNotation as boolean) || false, 'Input A', ); const input2 = checkInput( this.getInputData(1), matchFields.map((pair) => pair.field2), (options.disableDotNotation as boolean) || false, 'Input B', ); const resolve = this.getNodeParameter('resolve', 0, '') as string; options.resolve = resolve; if (resolve === 'mix') { options.preferWhenMix = this.getNodeParameter('preferWhenMix', 0, '') as string; options.exceptWhenMix = this.getNodeParameter('exceptWhenMix', 0, '') as string; } const matches = findMatches(input1, input2, matchFields, options); return matches; } }