mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-25 04:34:06 -08:00
d97edbcffa
* fix(core): Make node execution order configurable, and backward-compatible * ⚡ Also add new Merge-Node behaviour * ⚡ Fix typo * Fix lint issue * update labels * rename legacy to v0 * remove the unnecessary log * default all new workflows to use v1 execution-order * remove the controller changes * clone default settings to avoid it getting modified --------- Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
297 lines
7.5 KiB
TypeScript
297 lines
7.5 KiB
TypeScript
import type {
|
|
IExecuteFunctions,
|
|
IDataObject,
|
|
INodeExecutionData,
|
|
INodeType,
|
|
INodeTypeDescription,
|
|
} from 'n8n-workflow';
|
|
import {
|
|
checkInput,
|
|
checkInputAndThrowError,
|
|
checkMatchFieldsInput,
|
|
findMatches,
|
|
} from './GenericFunctions';
|
|
|
|
export class CompareDatasets implements INodeType {
|
|
description: INodeTypeDescription = {
|
|
displayName: 'Compare Datasets',
|
|
name: 'compareDatasets',
|
|
icon: 'file:compare.svg',
|
|
group: ['transform'],
|
|
version: [1, 2, 2.1, 2.2, 2.3],
|
|
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'],
|
|
requiredInputs: 1,
|
|
// 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:
|
|
'Items from different branches are paired together when the fields below match. If paired, the rest of the fields are compared to determine whether the items are the same or different',
|
|
name: 'infoBox',
|
|
type: 'notice',
|
|
default: '',
|
|
},
|
|
{
|
|
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',
|
|
requiresDataPath: 'single',
|
|
},
|
|
{
|
|
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',
|
|
requiresDataPath: 'single',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
{
|
|
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)',
|
|
},
|
|
],
|
|
displayOptions: {
|
|
show: {
|
|
'@version': [1, 2],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'When There Are Differences',
|
|
name: 'resolve',
|
|
type: 'options',
|
|
default: 'includeBoth',
|
|
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)',
|
|
},
|
|
],
|
|
displayOptions: {
|
|
hide: {
|
|
'@version': [1, 2],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
displayName: 'Fuzzy Compare',
|
|
name: 'fuzzyCompare',
|
|
type: 'boolean',
|
|
default: false,
|
|
description:
|
|
"Whether to tolerate small type differences when comparing fields. E.g. the number 3 and the string '3' are treated as the same.",
|
|
displayOptions: {
|
|
hide: {
|
|
'@version': [1],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
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'],
|
|
},
|
|
},
|
|
requiresDataPath: 'multiple',
|
|
},
|
|
{
|
|
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",
|
|
requiresDataPath: 'multiple',
|
|
},
|
|
{
|
|
displayName: 'Fuzzy Compare',
|
|
name: 'fuzzyCompare',
|
|
type: 'boolean',
|
|
default: false,
|
|
description:
|
|
"Whether to tolerate small type differences when comparing fields. E.g. the number 3 and the string '3' are treated as the same.",
|
|
displayOptions: {
|
|
show: {
|
|
'@version': [1],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
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<INodeExecutionData[][]> {
|
|
const matchFields = checkMatchFieldsInput(
|
|
this.getNodeParameter('mergeByFields.values', 0, []) as IDataObject[],
|
|
);
|
|
|
|
const options = this.getNodeParameter('options', 0, {});
|
|
|
|
options.nodeVersion = this.getNode().typeVersion;
|
|
|
|
if (options.nodeVersion >= 2) {
|
|
options.fuzzyCompare = this.getNodeParameter('fuzzyCompare', 0, false) as boolean;
|
|
}
|
|
|
|
let input1 = this.getInputData(0);
|
|
let input2 = this.getInputData(1);
|
|
if (options.nodeVersion < 2.2) {
|
|
input1 = checkInputAndThrowError(
|
|
input1,
|
|
matchFields.map((pair) => pair.field1),
|
|
(options.disableDotNotation as boolean) || false,
|
|
'Input A',
|
|
);
|
|
|
|
input2 = checkInputAndThrowError(
|
|
input2,
|
|
matchFields.map((pair) => pair.field2),
|
|
(options.disableDotNotation as boolean) || false,
|
|
'Input B',
|
|
);
|
|
} else {
|
|
input1 = checkInput(input1);
|
|
input2 = checkInput(input2);
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|