mirror of
https://github.com/n8n-io/n8n.git
synced 2024-12-30 06:59:41 -08:00
6092f6c41e
* feat: add data path flag * chore: update types * feat: use path for data * feat: add support for multiple values * fix: handle if not prev node * fix: update node * fix: handle multi part path * feat: add support for multiple vals for field * feat: add support for table transforms * feat: use dot notation * feat: fix bug where brackets removed * fix: handle dots, fix unit tests * test: update snapshot * test: fix tests * test: add test for edge case
224 lines
6 KiB
TypeScript
224 lines
6 KiB
TypeScript
import type { IExecuteFunctions } from 'n8n-core';
|
|
import type {
|
|
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:
|
|
'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)',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
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.",
|
|
},
|
|
{
|
|
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, {});
|
|
|
|
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;
|
|
}
|
|
}
|