feat(Compare Datasets Node): Node tweaks

This commit is contained in:
Michael Kret 2022-11-16 16:37:55 +02:00 committed by GitHub
parent 402b75ac28
commit 423ee81e33
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 55 additions and 17 deletions

View file

@ -13,10 +13,10 @@ export class CompareDatasets implements INodeType {
defaults: { name: 'Compare Datasets' }, defaults: { name: 'Compare Datasets' },
// eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node // eslint-disable-next-line n8n-nodes-base/node-class-description-inputs-wrong-regular-node
inputs: ['main', 'main'], 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 // eslint-disable-next-line n8n-nodes-base/node-class-description-outputs-wrong
outputs: ['main', 'main', 'main', 'main'], outputs: ['main', 'main', 'main', 'main'],
outputNames: ['In 1 only', 'Same', 'Different', 'In 2 only'], outputNames: ['In A only', 'Same', 'Different', 'In B only'],
properties: [ properties: [
{ {
displayName: 'Fields to Match', displayName: 'Fields to Match',
@ -33,7 +33,7 @@ export class CompareDatasets implements INodeType {
name: 'values', name: 'values',
values: [ values: [
{ {
displayName: 'Input 1 Field', displayName: 'Input A Field',
name: 'field1', name: 'field1',
type: 'string', type: 'string',
default: '', default: '',
@ -42,7 +42,7 @@ export class CompareDatasets implements INodeType {
hint: ' Enter the field name as text', hint: ' Enter the field name as text',
}, },
{ {
displayName: 'Input 2 Field', displayName: 'Input B Field',
name: 'field2', name: 'field2',
type: 'string', type: 'string',
default: '', default: '',
@ -61,11 +61,11 @@ export class CompareDatasets implements INodeType {
default: 'preferInput2', default: 'preferInput2',
options: [ options: [
{ {
name: 'Use Input 1 Version', name: 'Use Input A Version',
value: 'preferInput1', value: 'preferInput1',
}, },
{ {
name: 'Use Input 2 Version', name: 'Use Input B Version',
value: 'preferInput2', value: 'preferInput2',
}, },
{ {
@ -87,11 +87,11 @@ export class CompareDatasets implements INodeType {
default: 'input1', default: 'input1',
options: [ options: [
{ {
name: 'Input 1 Version', name: 'Input A Version',
value: 'input1', value: 'input1',
}, },
{ {
name: 'Input 2 Version', name: 'Input B Version',
value: 'input2', value: 'input2',
}, },
], ],
@ -107,7 +107,7 @@ export class CompareDatasets implements INodeType {
type: 'string', type: 'string',
default: '', default: '',
// eslint-disable-next-line n8n-nodes-base/node-param-placeholder-miscased-id // 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', hint: 'Enter the names of the input fields as text, separated by commas',
displayOptions: { displayOptions: {
show: { show: {
@ -122,6 +122,16 @@ export class CompareDatasets implements INodeType {
placeholder: 'Add Option', placeholder: 'Add Option',
default: {}, default: {},
options: [ 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', displayName: 'Disable Dot Notation',
name: 'disableDotNotation', name: 'disableDotNotation',
@ -164,14 +174,14 @@ export class CompareDatasets implements INodeType {
this.getInputData(0), this.getInputData(0),
matchFields.map((pair) => pair.field1 as string), matchFields.map((pair) => pair.field1 as string),
(options.disableDotNotation as boolean) || false, (options.disableDotNotation as boolean) || false,
'Input 1', 'Input A',
); );
const input2 = checkInput( const input2 = checkInput(
this.getInputData(1), this.getInputData(1),
matchFields.map((pair) => pair.field2 as string), matchFields.map((pair) => pair.field2 as string),
(options.disableDotNotation as boolean) || false, (options.disableDotNotation as boolean) || false,
'Input 2', 'Input B',
); );
const resolve = this.getNodeParameter('resolve', 0, '') as string; const resolve = this.getNodeParameter('resolve', 0, '') as string;

View file

@ -1,5 +1,5 @@
import { IDataObject, INodeExecutionData } from 'n8n-workflow'; 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 = { type PairToMatch = {
field1: string; field1: string;
@ -15,7 +15,8 @@ function compareItems(
item1: INodeExecutionData, item1: INodeExecutionData,
item2: INodeExecutionData, item2: INodeExecutionData,
fieldsToMatch: PairToMatch[], fieldsToMatch: PairToMatch[],
resolve?: string, resolve: string,
skipFields: string[],
) { ) {
const keys = {} as IDataObject; const keys = {} as IDataObject;
fieldsToMatch.forEach((field) => { fieldsToMatch.forEach((field) => {
@ -38,6 +39,7 @@ function compareItems(
const differentKeys = difference(allUniqueKeys, sameKeys); const differentKeys = difference(allUniqueKeys, sameKeys);
const different: IDataObject = {}; const different: IDataObject = {};
const skipped: IDataObject = {};
differentKeys.forEach((key) => { differentKeys.forEach((key) => {
switch (resolve) { switch (resolve) {
@ -50,11 +52,17 @@ function compareItems(
default: default:
const input1 = item1.json[key] || null; const input1 = item1.json[key] || null;
const input2 = item2.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( function combineItems(
@ -157,6 +165,7 @@ export function findMatches(
const disableDotNotation = (options.disableDotNotation as boolean) || false; const disableDotNotation = (options.disableDotNotation as boolean) || false;
const multipleMatches = (options.multipleMatches as string) || 'first'; const multipleMatches = (options.multipleMatches as string) || 'first';
const skipFields = ((options.skipFields as string) || '').split(',').map((field) => field.trim());
const filteredData = { const filteredData = {
matched: [] as EntryMatches[], matched: [] as EntryMatches[],
@ -214,7 +223,14 @@ export function findMatches(
let entryCopy: INodeExecutionData | undefined; let entryCopy: INodeExecutionData | undefined;
entryMatches.matches.forEach((match) => { 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; if (!entryCopy) entryCopy = match;
} else { } else {
switch (options.resolve) { switch (options.resolve) {
@ -237,7 +253,13 @@ export function findMatches(
break; break;
default: default:
different.push( 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, disableDotNotation: boolean,
inputLabel: string, 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) { for (const field of fields) {
const isPresent = (input || []).some((entry) => { const isPresent = (input || []).some((entry) => {
if (disableDotNotation) { if (disableDotNotation) {