first commit
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
Security: Sync from Public / sync-from-public (push) Has been cancelled
Test: Benchmark Nightly / build (push) Has been cancelled
Test: Benchmark Nightly / Notify Cats on failure (push) Has been cancelled
CI: Python / Checks (push) Has been cancelled
Test: Evals Python / Workflow Comparison Python (push) Has been cancelled
Util: Check Docs URLs / check-docs-urls (push) Has been cancelled
Test: Visual Storybook / Cloudflare Pages (push) Has been cancelled
Test: E2E Performance / build-and-test-performance (push) Has been cancelled
Test: Workflows Nightly / Run Workflow Tests (push) Has been cancelled
Util: Cleanup CI Docker Images / Delete stale CI images (push) Has been cancelled
Test: Benchmark Destroy Env / build (push) Has been cancelled
Util: Update Node Popularity / update-popularity (push) Has been cancelled
Test: E2E Coverage Weekly / Coverage Tests (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"node": "n8n-nodes-base.itemLists",
|
||||
"nodeVersion": "1.0",
|
||||
"codexVersion": "1.0",
|
||||
"details": "",
|
||||
"categories": ["Core Nodes"],
|
||||
"resources": {
|
||||
"primaryDocumentation": [
|
||||
{
|
||||
"url": "https://docs.n8n.io/integrations/builtin/core-nodes/n8n-nodes-base.itemlists/"
|
||||
}
|
||||
],
|
||||
"generic": []
|
||||
},
|
||||
"alias": [
|
||||
"Aggregate",
|
||||
"Dedupe",
|
||||
"Deduplicate",
|
||||
"Duplicates",
|
||||
"Limit",
|
||||
"Remove",
|
||||
"Slice",
|
||||
"Sort",
|
||||
"Split",
|
||||
"Unique",
|
||||
"JSON",
|
||||
"Transform",
|
||||
"Array",
|
||||
"List",
|
||||
"Object",
|
||||
"Item",
|
||||
"Map",
|
||||
"Format",
|
||||
"Nested",
|
||||
"Iterate",
|
||||
"Summarise",
|
||||
"Summarize",
|
||||
"Group",
|
||||
"Pivot",
|
||||
"Sum",
|
||||
"Count",
|
||||
"Min",
|
||||
"Max"
|
||||
],
|
||||
"subcategories": {
|
||||
"Core Nodes": ["Helpers", "Data Transformation"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import type { INodeTypeBaseDescription, IVersionedNodeType } from 'n8n-workflow';
|
||||
import { VersionedNodeType } from 'n8n-workflow';
|
||||
|
||||
import { ItemListsV1 } from './V1/ItemListsV1.node';
|
||||
import { ItemListsV2 } from './V2/ItemListsV2.node';
|
||||
import { ItemListsV3 } from './V3/ItemListsV3.node';
|
||||
|
||||
export class ItemLists extends VersionedNodeType {
|
||||
constructor() {
|
||||
const baseDescription: INodeTypeBaseDescription = {
|
||||
displayName: 'Item Lists',
|
||||
name: 'itemLists',
|
||||
icon: 'file:itemLists.svg',
|
||||
group: ['input'],
|
||||
hidden: true,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Helper for working with lists of items and transforming arrays',
|
||||
defaultVersion: 3.1,
|
||||
};
|
||||
|
||||
const nodeVersions: IVersionedNodeType['nodeVersions'] = {
|
||||
1: new ItemListsV1(baseDescription),
|
||||
2: new ItemListsV2(baseDescription),
|
||||
2.1: new ItemListsV2(baseDescription),
|
||||
2.2: new ItemListsV2(baseDescription),
|
||||
3: new ItemListsV3(baseDescription),
|
||||
3.1: new ItemListsV3(baseDescription),
|
||||
};
|
||||
|
||||
super(nodeVersions, baseDescription);
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,581 @@
|
||||
import get from 'lodash/get';
|
||||
import type {
|
||||
GenericValue,
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
type AggregationType =
|
||||
| 'append'
|
||||
| 'average'
|
||||
| 'concatenate'
|
||||
| 'count'
|
||||
| 'countUnique'
|
||||
| 'max'
|
||||
| 'min'
|
||||
| 'sum';
|
||||
|
||||
type Aggregation = {
|
||||
aggregation: AggregationType;
|
||||
field: string;
|
||||
includeEmpty?: boolean;
|
||||
separateBy?: string;
|
||||
customSeparator?: string;
|
||||
};
|
||||
|
||||
type Aggregations = Aggregation[];
|
||||
|
||||
const AggregationDisplayNames = {
|
||||
append: 'appended_',
|
||||
average: 'average_',
|
||||
concatenate: 'concatenated_',
|
||||
count: 'count_',
|
||||
countUnique: 'unique_count_',
|
||||
max: 'max_',
|
||||
min: 'min_',
|
||||
sum: 'sum_',
|
||||
};
|
||||
|
||||
const NUMERICAL_AGGREGATIONS = ['average', 'max', 'min', 'sum'];
|
||||
|
||||
type SummarizeOptions = {
|
||||
disableDotNotation?: boolean;
|
||||
outputFormat?: 'separateItems' | 'singleItem';
|
||||
skipEmptySplitFields?: boolean;
|
||||
};
|
||||
|
||||
type ValueGetterFn = (
|
||||
item: IDataObject,
|
||||
field: string,
|
||||
) => IDataObject | IDataObject[] | GenericValue | GenericValue[];
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Fields to Summarize',
|
||||
name: 'fieldsToSummarize',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Field',
|
||||
default: { values: [{ aggregation: 'count', field: '' }] },
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: '',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Aggregation',
|
||||
name: 'aggregation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Append',
|
||||
value: 'append',
|
||||
},
|
||||
{
|
||||
name: 'Average',
|
||||
value: 'average',
|
||||
},
|
||||
{
|
||||
name: 'Concatenate',
|
||||
value: 'concatenate',
|
||||
},
|
||||
{
|
||||
name: 'Count',
|
||||
value: 'count',
|
||||
},
|
||||
{
|
||||
name: 'Count Unique',
|
||||
value: 'countUnique',
|
||||
},
|
||||
{
|
||||
name: 'Max',
|
||||
value: 'max',
|
||||
},
|
||||
{
|
||||
name: 'Min',
|
||||
value: 'min',
|
||||
},
|
||||
{
|
||||
name: 'Sum',
|
||||
value: 'sum',
|
||||
},
|
||||
],
|
||||
default: 'count',
|
||||
description: 'How to combine the values of the field you want to summarize',
|
||||
},
|
||||
//field repeated to have different descriptions for different aggregations --------------------------------
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The name of an input field that you want to summarize',
|
||||
placeholder: 'e.g. cost',
|
||||
hint: ' Enter the field name as text',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
aggregation: [...NUMERICAL_AGGREGATIONS, 'countUnique', 'count'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'The name of an input field that you want to summarize. The field should contain numerical values; null, undefined, empty strings would be ignored.',
|
||||
placeholder: 'e.g. cost',
|
||||
hint: ' Enter the field name as text',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: NUMERICAL_AGGREGATIONS,
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'The name of an input field that you want to summarize; null, undefined, empty strings would be ignored',
|
||||
placeholder: 'e.g. cost',
|
||||
hint: ' Enter the field name as text',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['countUnique', 'count'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
displayName: 'Include Empty Values',
|
||||
name: 'includeEmpty',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['append', 'concatenate'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Separator',
|
||||
name: 'separateBy',
|
||||
type: 'options',
|
||||
default: ',',
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
|
||||
options: [
|
||||
{
|
||||
name: 'Comma',
|
||||
value: ',',
|
||||
},
|
||||
{
|
||||
name: 'Comma and Space',
|
||||
value: ', ',
|
||||
},
|
||||
{
|
||||
name: 'New Line',
|
||||
value: '\n',
|
||||
},
|
||||
{
|
||||
name: 'None',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
name: 'Space',
|
||||
value: ' ',
|
||||
},
|
||||
{
|
||||
name: 'Other',
|
||||
value: 'other',
|
||||
},
|
||||
],
|
||||
hint: 'What to insert between values',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['concatenate'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Separator',
|
||||
name: 'customSeparator',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['concatenate'],
|
||||
separateBy: ['other'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['summarize'],
|
||||
},
|
||||
},
|
||||
},
|
||||
// fieldsToSplitBy repeated to have different displayName for singleItem and separateItems -----------------------------
|
||||
{
|
||||
displayName: 'Fields to Split By',
|
||||
name: 'fieldsToSplitBy',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. country, city',
|
||||
default: '',
|
||||
description: 'The name of the input fields that you want to split the summary by',
|
||||
hint: 'Enter the name of the fields as text (separated by commas)',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['summarize'],
|
||||
},
|
||||
hide: {
|
||||
'/options.outputFormat': ['singleItem'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'multiple',
|
||||
},
|
||||
{
|
||||
displayName: 'Fields to Group By',
|
||||
name: 'fieldsToSplitBy',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. country, city',
|
||||
default: '',
|
||||
description: 'The name of the input fields that you want to split the summary by',
|
||||
hint: 'Enter the name of the fields as text (separated by commas)',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['summarize'],
|
||||
'/options.outputFormat': ['singleItem'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'multiple',
|
||||
},
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['summarize'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
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: 'Output Format',
|
||||
name: 'outputFormat',
|
||||
type: 'options',
|
||||
default: 'separateItems',
|
||||
options: [
|
||||
{
|
||||
name: 'Each Split in a Separate Item',
|
||||
value: 'separateItems',
|
||||
},
|
||||
{
|
||||
name: 'All Splits in a Single Item',
|
||||
value: 'singleItem',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
displayName: 'Ignore items without valid fields to group by',
|
||||
name: 'skipEmptySplitFields',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function isEmpty<T>(value: T) {
|
||||
return value === undefined || value === null || value === '';
|
||||
}
|
||||
|
||||
const fieldValueGetter = (disableDotNotation?: boolean) => {
|
||||
if (disableDotNotation) {
|
||||
return (item: IDataObject, field: string) => item[field];
|
||||
} else {
|
||||
return (item: IDataObject, field: string) => get(item, field);
|
||||
}
|
||||
};
|
||||
|
||||
function checkIfFieldExists(
|
||||
this: IExecuteFunctions,
|
||||
items: IDataObject[],
|
||||
aggregations: Aggregations,
|
||||
getValue: ValueGetterFn,
|
||||
) {
|
||||
for (const aggregation of aggregations) {
|
||||
if (aggregation.field === '') {
|
||||
continue;
|
||||
}
|
||||
const exist = items.some((item) => getValue(item, aggregation.field) !== undefined);
|
||||
if (!exist) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The field '${aggregation.field}' does not exist in any items`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function aggregate(items: IDataObject[], entry: Aggregation, getValue: ValueGetterFn) {
|
||||
const { aggregation, field } = entry;
|
||||
let data = [...items];
|
||||
|
||||
if (NUMERICAL_AGGREGATIONS.includes(aggregation)) {
|
||||
data = data.filter(
|
||||
(item) => typeof getValue(item, field) === 'number' && !isEmpty(getValue(item, field)),
|
||||
);
|
||||
}
|
||||
|
||||
switch (aggregation) {
|
||||
//combine operations
|
||||
case 'append':
|
||||
if (!entry.includeEmpty) {
|
||||
data = data.filter((item) => !isEmpty(getValue(item, field)));
|
||||
}
|
||||
return data.map((item) => getValue(item, field));
|
||||
case 'concatenate':
|
||||
const separateBy = entry.separateBy === 'other' ? entry.customSeparator : entry.separateBy;
|
||||
if (!entry.includeEmpty) {
|
||||
data = data.filter((item) => !isEmpty(getValue(item, field)));
|
||||
}
|
||||
return data
|
||||
.map((item) => {
|
||||
let value = getValue(item, field);
|
||||
if (typeof value === 'object') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
if (typeof value === 'undefined') {
|
||||
value = 'undefined';
|
||||
}
|
||||
|
||||
return value;
|
||||
})
|
||||
.join(separateBy);
|
||||
|
||||
//numerical operations
|
||||
case 'average':
|
||||
return (
|
||||
data.reduce((acc, item) => {
|
||||
return acc + (getValue(item, field) as number);
|
||||
}, 0) / data.length
|
||||
);
|
||||
case 'sum':
|
||||
return data.reduce((acc, item) => {
|
||||
return acc + (getValue(item, field) as number);
|
||||
}, 0);
|
||||
case 'min':
|
||||
return Math.min(
|
||||
...(data.map((item) => {
|
||||
return getValue(item, field);
|
||||
}) as number[]),
|
||||
);
|
||||
case 'max':
|
||||
return Math.max(
|
||||
...(data.map((item) => {
|
||||
return getValue(item, field);
|
||||
}) as number[]),
|
||||
);
|
||||
|
||||
//count operations
|
||||
case 'countUnique':
|
||||
return new Set(data.map((item) => getValue(item, field)).filter((item) => !isEmpty(item)))
|
||||
.size;
|
||||
default:
|
||||
//count by default
|
||||
return data.filter((item) => !isEmpty(getValue(item, field))).length;
|
||||
}
|
||||
}
|
||||
|
||||
function aggregateData(
|
||||
data: IDataObject[],
|
||||
fieldsToSummarize: Aggregations,
|
||||
options: SummarizeOptions,
|
||||
getValue: ValueGetterFn,
|
||||
) {
|
||||
const returnData = fieldsToSummarize.reduce((acc, aggregation) => {
|
||||
acc[`${AggregationDisplayNames[aggregation.aggregation]}${aggregation.field}`] = aggregate(
|
||||
data,
|
||||
aggregation,
|
||||
getValue,
|
||||
);
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
if (options.outputFormat === 'singleItem') {
|
||||
return returnData;
|
||||
} else {
|
||||
return { ...returnData, pairedItems: data.map((item) => item._itemIndex as number) };
|
||||
}
|
||||
}
|
||||
|
||||
function splitData(
|
||||
splitKeys: string[],
|
||||
data: IDataObject[],
|
||||
fieldsToSummarize: Aggregations,
|
||||
options: SummarizeOptions,
|
||||
getValue: ValueGetterFn,
|
||||
) {
|
||||
if (!splitKeys || splitKeys.length === 0) {
|
||||
return aggregateData(data, fieldsToSummarize, options, getValue);
|
||||
}
|
||||
|
||||
const [firstSplitKey, ...restSplitKeys] = splitKeys;
|
||||
|
||||
const groupedData = data.reduce((acc, item) => {
|
||||
let keyValuee = getValue(item, firstSplitKey) as string;
|
||||
|
||||
if (typeof keyValuee === 'object') {
|
||||
keyValuee = JSON.stringify(keyValuee);
|
||||
}
|
||||
|
||||
if (options.skipEmptySplitFields && typeof keyValuee !== 'number' && !keyValuee) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (acc[keyValuee] === undefined) {
|
||||
acc[keyValuee] = [item];
|
||||
} else {
|
||||
(acc[keyValuee] as IDataObject[]).push(item);
|
||||
}
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
|
||||
return Object.keys(groupedData).reduce((acc, key) => {
|
||||
const value = groupedData[key] as IDataObject[];
|
||||
acc[key] = splitData(restSplitKeys, value, fieldsToSummarize, options, getValue);
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
}
|
||||
|
||||
function aggregationToArray(
|
||||
aggregationResult: IDataObject,
|
||||
fieldsToSplitBy: string[],
|
||||
previousStage: IDataObject = {},
|
||||
) {
|
||||
const returnData: IDataObject[] = [];
|
||||
|
||||
const splitFieldName = fieldsToSplitBy[0];
|
||||
const isNext = fieldsToSplitBy[1];
|
||||
|
||||
if (isNext === undefined) {
|
||||
for (const fieldName of Object.keys(aggregationResult)) {
|
||||
returnData.push({
|
||||
...previousStage,
|
||||
[splitFieldName]: fieldName,
|
||||
...(aggregationResult[fieldName] as IDataObject),
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
} else {
|
||||
for (const key of Object.keys(aggregationResult)) {
|
||||
returnData.push(
|
||||
...aggregationToArray(aggregationResult[key] as IDataObject, fieldsToSplitBy.slice(1), {
|
||||
...previousStage,
|
||||
[splitFieldName]: key,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
}
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
): Promise<INodeExecutionData[][]> {
|
||||
const newItems = items.map(({ json }, i) => ({ ...json, _itemIndex: i }));
|
||||
|
||||
const options = this.getNodeParameter('options', 0, {}) as SummarizeOptions;
|
||||
|
||||
const fieldsToSplitBy = (this.getNodeParameter('fieldsToSplitBy', 0, '') as string)
|
||||
.split(',')
|
||||
.map((field) => field.trim())
|
||||
.filter((field) => field);
|
||||
|
||||
const fieldsToSummarize = this.getNodeParameter(
|
||||
'fieldsToSummarize.values',
|
||||
0,
|
||||
[],
|
||||
) as Aggregations;
|
||||
|
||||
if (fieldsToSummarize.filter((aggregation) => aggregation.field !== '').length === 0) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
"You need to add at least one aggregation to 'Fields to Summarize' with non empty 'Field'",
|
||||
);
|
||||
}
|
||||
|
||||
const getValue = fieldValueGetter(options.disableDotNotation);
|
||||
|
||||
checkIfFieldExists.call(this, newItems, fieldsToSummarize, getValue);
|
||||
|
||||
const aggregationResult = splitData(
|
||||
fieldsToSplitBy,
|
||||
newItems,
|
||||
fieldsToSummarize,
|
||||
options,
|
||||
getValue,
|
||||
);
|
||||
|
||||
if (options.outputFormat === 'singleItem') {
|
||||
const executionData: INodeExecutionData = {
|
||||
json: aggregationResult,
|
||||
pairedItem: newItems.map((_v, index) => ({
|
||||
item: index,
|
||||
})),
|
||||
};
|
||||
return [[executionData]];
|
||||
} else {
|
||||
if (!fieldsToSplitBy.length) {
|
||||
const { pairedItems, ...json } = aggregationResult;
|
||||
const executionData: INodeExecutionData = {
|
||||
json,
|
||||
pairedItem: ((pairedItems as number[]) || []).map((index: number) => ({
|
||||
item: index,
|
||||
})),
|
||||
};
|
||||
return [[executionData]];
|
||||
}
|
||||
const returnData = aggregationToArray(aggregationResult, fieldsToSplitBy);
|
||||
const executionData = returnData.map((item) => {
|
||||
const { pairedItems, ...json } = item;
|
||||
return {
|
||||
json,
|
||||
pairedItem: ((pairedItems as number[]) || []).map((index: number) => ({
|
||||
item: index,
|
||||
})),
|
||||
};
|
||||
});
|
||||
return [executionData];
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,617 @@
|
||||
import get from 'lodash/get';
|
||||
import type {
|
||||
GenericValue,
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
type AggregationType =
|
||||
| 'append'
|
||||
| 'average'
|
||||
| 'concatenate'
|
||||
| 'count'
|
||||
| 'countUnique'
|
||||
| 'max'
|
||||
| 'min'
|
||||
| 'sum';
|
||||
|
||||
type Aggregation = {
|
||||
aggregation: AggregationType;
|
||||
field: string;
|
||||
includeEmpty?: boolean;
|
||||
separateBy?: string;
|
||||
customSeparator?: string;
|
||||
};
|
||||
|
||||
type Aggregations = Aggregation[];
|
||||
|
||||
const AggregationDisplayNames = {
|
||||
append: 'appended_',
|
||||
average: 'average_',
|
||||
concatenate: 'concatenated_',
|
||||
count: 'count_',
|
||||
countUnique: 'unique_count_',
|
||||
max: 'max_',
|
||||
min: 'min_',
|
||||
sum: 'sum_',
|
||||
};
|
||||
|
||||
const NUMERICAL_AGGREGATIONS = ['average', 'max', 'min', 'sum'];
|
||||
|
||||
type SummarizeOptions = {
|
||||
disableDotNotation?: boolean;
|
||||
outputFormat?: 'separateItems' | 'singleItem';
|
||||
skipEmptySplitFields?: boolean;
|
||||
};
|
||||
|
||||
type ValueGetterFn = (
|
||||
item: IDataObject,
|
||||
field: string,
|
||||
) => IDataObject | IDataObject[] | GenericValue | GenericValue[];
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Fields to Summarize',
|
||||
name: 'fieldsToSummarize',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Field',
|
||||
default: { values: [{ aggregation: 'count', field: '' }] },
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: '',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Aggregation',
|
||||
name: 'aggregation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Append',
|
||||
value: 'append',
|
||||
},
|
||||
{
|
||||
name: 'Average',
|
||||
value: 'average',
|
||||
},
|
||||
{
|
||||
name: 'Concatenate',
|
||||
value: 'concatenate',
|
||||
},
|
||||
{
|
||||
name: 'Count',
|
||||
value: 'count',
|
||||
},
|
||||
{
|
||||
name: 'Count Unique',
|
||||
value: 'countUnique',
|
||||
},
|
||||
{
|
||||
name: 'Max',
|
||||
value: 'max',
|
||||
},
|
||||
{
|
||||
name: 'Min',
|
||||
value: 'min',
|
||||
},
|
||||
{
|
||||
name: 'Sum',
|
||||
value: 'sum',
|
||||
},
|
||||
],
|
||||
default: 'count',
|
||||
description: 'How to combine the values of the field you want to summarize',
|
||||
},
|
||||
//field repeated to have different descriptions for different aggregations --------------------------------
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The name of an input field that you want to summarize',
|
||||
placeholder: 'e.g. cost',
|
||||
hint: ' Enter the field name as text',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
aggregation: [...NUMERICAL_AGGREGATIONS, 'countUnique', 'count'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'The name of an input field that you want to summarize. The field should contain numerical values; null, undefined, empty strings would be ignored.',
|
||||
placeholder: 'e.g. cost',
|
||||
hint: ' Enter the field name as text',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: NUMERICAL_AGGREGATIONS,
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'The name of an input field that you want to summarize; null, undefined, empty strings would be ignored',
|
||||
placeholder: 'e.g. cost',
|
||||
hint: ' Enter the field name as text',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['countUnique', 'count'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
displayName: 'Include Empty Values',
|
||||
name: 'includeEmpty',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['append', 'concatenate'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Separator',
|
||||
name: 'separateBy',
|
||||
type: 'options',
|
||||
default: ',',
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
|
||||
options: [
|
||||
{
|
||||
name: 'Comma',
|
||||
value: ',',
|
||||
},
|
||||
{
|
||||
name: 'Comma and Space',
|
||||
value: ', ',
|
||||
},
|
||||
{
|
||||
name: 'New Line',
|
||||
value: '\n',
|
||||
},
|
||||
{
|
||||
name: 'None',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
name: 'Space',
|
||||
value: ' ',
|
||||
},
|
||||
{
|
||||
name: 'Other',
|
||||
value: 'other',
|
||||
},
|
||||
],
|
||||
hint: 'What to insert between values',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['concatenate'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Separator',
|
||||
name: 'customSeparator',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['concatenate'],
|
||||
separateBy: ['other'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['summarize'],
|
||||
},
|
||||
},
|
||||
},
|
||||
// fieldsToSplitBy repeated to have different displayName for singleItem and separateItems -----------------------------
|
||||
{
|
||||
displayName: 'Fields to Split By',
|
||||
name: 'fieldsToSplitBy',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. country, city',
|
||||
default: '',
|
||||
description: 'The name of the input fields that you want to split the summary by',
|
||||
hint: 'Enter the name of the fields as text (separated by commas)',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['summarize'],
|
||||
},
|
||||
hide: {
|
||||
'/options.outputFormat': ['singleItem'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'multiple',
|
||||
},
|
||||
{
|
||||
displayName: 'Fields to Group By',
|
||||
name: 'fieldsToSplitBy',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. country, city',
|
||||
default: '',
|
||||
description: 'The name of the input fields that you want to split the summary by',
|
||||
hint: 'Enter the name of the fields as text (separated by commas)',
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['summarize'],
|
||||
'/options.outputFormat': ['singleItem'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'multiple',
|
||||
},
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['summarize'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
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: 'Output Format',
|
||||
name: 'outputFormat',
|
||||
type: 'options',
|
||||
default: 'separateItems',
|
||||
options: [
|
||||
{
|
||||
name: 'Each Split in a Separate Item',
|
||||
value: 'separateItems',
|
||||
},
|
||||
{
|
||||
name: 'All Splits in a Single Item',
|
||||
value: 'singleItem',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
displayName: 'Ignore items without valid fields to group by',
|
||||
name: 'skipEmptySplitFields',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function isEmpty<T>(value: T) {
|
||||
return value === undefined || value === null || value === '';
|
||||
}
|
||||
|
||||
function parseReturnData(returnData: IDataObject) {
|
||||
const regexBrackets = /[\]\["]/g;
|
||||
const regexSpaces = /[ .]/g;
|
||||
for (const key of Object.keys(returnData)) {
|
||||
if (key.match(regexBrackets)) {
|
||||
const newKey = key.replace(regexBrackets, '');
|
||||
returnData[newKey] = returnData[key];
|
||||
delete returnData[key];
|
||||
}
|
||||
}
|
||||
for (const key of Object.keys(returnData)) {
|
||||
if (key.match(regexSpaces)) {
|
||||
const newKey = key.replace(regexSpaces, '_');
|
||||
returnData[newKey] = returnData[key];
|
||||
delete returnData[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseFieldName(fieldName: string[]) {
|
||||
const regexBrackets = /[\]\["]/g;
|
||||
const regexSpaces = /[ .]/g;
|
||||
fieldName = fieldName.map((field) => {
|
||||
field = field.replace(regexBrackets, '');
|
||||
field = field.replace(regexSpaces, '_');
|
||||
return field;
|
||||
});
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
const fieldValueGetter = (disableDotNotation?: boolean) => {
|
||||
if (disableDotNotation) {
|
||||
return (item: IDataObject, field: string) => item[field];
|
||||
} else {
|
||||
return (item: IDataObject, field: string) => get(item, field);
|
||||
}
|
||||
};
|
||||
|
||||
function checkIfFieldExists(
|
||||
this: IExecuteFunctions,
|
||||
items: IDataObject[],
|
||||
aggregations: Aggregations,
|
||||
getValue: ValueGetterFn,
|
||||
) {
|
||||
for (const aggregation of aggregations) {
|
||||
if (aggregation.field === '') {
|
||||
continue;
|
||||
}
|
||||
const exist = items.some((item) => getValue(item, aggregation.field) !== undefined);
|
||||
if (!exist) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The field '${aggregation.field}' does not exist in any items`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function aggregate(items: IDataObject[], entry: Aggregation, getValue: ValueGetterFn) {
|
||||
const { aggregation, field } = entry;
|
||||
let data = [...items];
|
||||
|
||||
if (NUMERICAL_AGGREGATIONS.includes(aggregation)) {
|
||||
data = data.filter(
|
||||
(item) => typeof getValue(item, field) === 'number' && !isEmpty(getValue(item, field)),
|
||||
);
|
||||
}
|
||||
|
||||
switch (aggregation) {
|
||||
//combine operations
|
||||
case 'append':
|
||||
if (!entry.includeEmpty) {
|
||||
data = data.filter((item) => !isEmpty(getValue(item, field)));
|
||||
}
|
||||
return data.map((item) => getValue(item, field));
|
||||
case 'concatenate':
|
||||
const separateBy = entry.separateBy === 'other' ? entry.customSeparator : entry.separateBy;
|
||||
if (!entry.includeEmpty) {
|
||||
data = data.filter((item) => !isEmpty(getValue(item, field)));
|
||||
}
|
||||
return data
|
||||
.map((item) => {
|
||||
let value = getValue(item, field);
|
||||
if (typeof value === 'object') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
if (typeof value === 'undefined') {
|
||||
value = 'undefined';
|
||||
}
|
||||
|
||||
return value;
|
||||
})
|
||||
.join(separateBy);
|
||||
|
||||
//numerical operations
|
||||
case 'average':
|
||||
return (
|
||||
data.reduce((acc, item) => {
|
||||
return acc + (getValue(item, field) as number);
|
||||
}, 0) / data.length
|
||||
);
|
||||
case 'sum':
|
||||
return data.reduce((acc, item) => {
|
||||
return acc + (getValue(item, field) as number);
|
||||
}, 0);
|
||||
case 'min':
|
||||
return Math.min(
|
||||
...(data.map((item) => {
|
||||
return getValue(item, field);
|
||||
}) as number[]),
|
||||
);
|
||||
case 'max':
|
||||
return Math.max(
|
||||
...(data.map((item) => {
|
||||
return getValue(item, field);
|
||||
}) as number[]),
|
||||
);
|
||||
|
||||
//count operations
|
||||
case 'countUnique':
|
||||
return new Set(data.map((item) => getValue(item, field)).filter((item) => !isEmpty(item)))
|
||||
.size;
|
||||
default:
|
||||
//count by default
|
||||
return data.filter((item) => !isEmpty(getValue(item, field))).length;
|
||||
}
|
||||
}
|
||||
|
||||
function aggregateData(
|
||||
data: IDataObject[],
|
||||
fieldsToSummarize: Aggregations,
|
||||
options: SummarizeOptions,
|
||||
getValue: ValueGetterFn,
|
||||
) {
|
||||
const returnData = fieldsToSummarize.reduce((acc, aggregation) => {
|
||||
acc[`${AggregationDisplayNames[aggregation.aggregation]}${aggregation.field}`] = aggregate(
|
||||
data,
|
||||
aggregation,
|
||||
getValue,
|
||||
);
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
parseReturnData(returnData);
|
||||
if (options.outputFormat === 'singleItem') {
|
||||
parseReturnData(returnData);
|
||||
return returnData;
|
||||
} else {
|
||||
return { ...returnData, pairedItems: data.map((item) => item._itemIndex as number) };
|
||||
}
|
||||
}
|
||||
|
||||
function splitData(
|
||||
splitKeys: string[],
|
||||
data: IDataObject[],
|
||||
fieldsToSummarize: Aggregations,
|
||||
options: SummarizeOptions,
|
||||
getValue: ValueGetterFn,
|
||||
) {
|
||||
if (!splitKeys || splitKeys.length === 0) {
|
||||
return aggregateData(data, fieldsToSummarize, options, getValue);
|
||||
}
|
||||
|
||||
const [firstSplitKey, ...restSplitKeys] = splitKeys;
|
||||
|
||||
const groupedData = data.reduce((acc, item) => {
|
||||
let keyValuee = getValue(item, firstSplitKey) as string;
|
||||
|
||||
if (typeof keyValuee === 'object') {
|
||||
keyValuee = JSON.stringify(keyValuee);
|
||||
}
|
||||
|
||||
if (options.skipEmptySplitFields && typeof keyValuee !== 'number' && !keyValuee) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (acc[keyValuee] === undefined) {
|
||||
acc[keyValuee] = [item];
|
||||
} else {
|
||||
(acc[keyValuee] as IDataObject[]).push(item);
|
||||
}
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
|
||||
return Object.keys(groupedData).reduce((acc, key) => {
|
||||
const value = groupedData[key] as IDataObject[];
|
||||
acc[key] = splitData(restSplitKeys, value, fieldsToSummarize, options, getValue);
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
}
|
||||
|
||||
function aggregationToArray(
|
||||
aggregationResult: IDataObject,
|
||||
fieldsToSplitBy: string[],
|
||||
previousStage: IDataObject = {},
|
||||
) {
|
||||
const returnData: IDataObject[] = [];
|
||||
fieldsToSplitBy = parseFieldName(fieldsToSplitBy);
|
||||
const splitFieldName = fieldsToSplitBy[0];
|
||||
const isNext = fieldsToSplitBy[1];
|
||||
|
||||
if (isNext === undefined) {
|
||||
for (const fieldName of Object.keys(aggregationResult)) {
|
||||
returnData.push({
|
||||
...previousStage,
|
||||
[splitFieldName]: fieldName,
|
||||
...(aggregationResult[fieldName] as IDataObject),
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
} else {
|
||||
for (const key of Object.keys(aggregationResult)) {
|
||||
returnData.push(
|
||||
...aggregationToArray(aggregationResult[key] as IDataObject, fieldsToSplitBy.slice(1), {
|
||||
...previousStage,
|
||||
[splitFieldName]: key,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
}
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
): Promise<INodeExecutionData[][]> {
|
||||
const newItems = items.map(({ json }, i) => ({ ...json, _itemIndex: i }));
|
||||
|
||||
const options = this.getNodeParameter('options', 0, {}) as SummarizeOptions;
|
||||
|
||||
const fieldsToSplitBy = (this.getNodeParameter('fieldsToSplitBy', 0, '') as string)
|
||||
.split(',')
|
||||
.map((field) => field.trim())
|
||||
.filter((field) => field);
|
||||
|
||||
const fieldsToSummarize = this.getNodeParameter(
|
||||
'fieldsToSummarize.values',
|
||||
0,
|
||||
[],
|
||||
) as Aggregations;
|
||||
|
||||
if (fieldsToSummarize.filter((aggregation) => aggregation.field !== '').length === 0) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
"You need to add at least one aggregation to 'Fields to Summarize' with non empty 'Field'",
|
||||
);
|
||||
}
|
||||
|
||||
const getValue = fieldValueGetter(options.disableDotNotation);
|
||||
|
||||
const nodeVersion = this.getNode().typeVersion;
|
||||
|
||||
if (nodeVersion < 2.1) {
|
||||
checkIfFieldExists.call(this, newItems, fieldsToSummarize, getValue);
|
||||
}
|
||||
|
||||
const aggregationResult = splitData(
|
||||
fieldsToSplitBy,
|
||||
newItems,
|
||||
fieldsToSummarize,
|
||||
options,
|
||||
getValue,
|
||||
);
|
||||
|
||||
if (options.outputFormat === 'singleItem') {
|
||||
const executionData: INodeExecutionData = {
|
||||
json: aggregationResult,
|
||||
pairedItem: newItems.map((_v, index) => ({
|
||||
item: index,
|
||||
})),
|
||||
};
|
||||
return [[executionData]];
|
||||
} else {
|
||||
if (!fieldsToSplitBy.length) {
|
||||
const { pairedItems, ...json } = aggregationResult;
|
||||
const executionData: INodeExecutionData = {
|
||||
json,
|
||||
pairedItem: ((pairedItems as number[]) || []).map((index: number) => ({
|
||||
item: index,
|
||||
})),
|
||||
};
|
||||
return [[executionData]];
|
||||
}
|
||||
const returnData = aggregationToArray(aggregationResult, fieldsToSplitBy);
|
||||
const executionData = returnData.map((item) => {
|
||||
const { pairedItems, ...json } = item;
|
||||
return {
|
||||
json,
|
||||
pairedItem: ((pairedItems as number[]) || []).map((index: number) => ({
|
||||
item: index,
|
||||
})),
|
||||
};
|
||||
});
|
||||
return [executionData];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
INodeType,
|
||||
INodeTypeBaseDescription,
|
||||
INodeTypeDescription,
|
||||
} from 'n8n-workflow';
|
||||
|
||||
import { router } from './actions/router';
|
||||
import { versionDescription } from './actions/versionDescription';
|
||||
|
||||
export class ItemListsV3 implements INodeType {
|
||||
description: INodeTypeDescription;
|
||||
|
||||
constructor(baseDescription: INodeTypeBaseDescription) {
|
||||
this.description = {
|
||||
...baseDescription,
|
||||
...versionDescription,
|
||||
};
|
||||
}
|
||||
|
||||
async execute(this: IExecuteFunctions) {
|
||||
return await router.call(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
export const disableDotNotationBoolean: INodeProperties = {
|
||||
displayName: 'Disable Dot Notation',
|
||||
name: 'disableDotNotation',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to disallow referencing child fields using `parent.child` in the field name',
|
||||
};
|
||||
+406
@@ -0,0 +1,406 @@
|
||||
import get from 'lodash/get';
|
||||
import isEmpty from 'lodash/isEmpty';
|
||||
import set from 'lodash/set';
|
||||
import type {
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
IPairedItemData,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { addBinariesToItem, prepareFieldsArray } from '../../helpers/utils';
|
||||
import { disableDotNotationBoolean } from '../common.descriptions';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Aggregate',
|
||||
name: 'aggregate',
|
||||
type: 'options',
|
||||
default: 'aggregateIndividualFields',
|
||||
options: [
|
||||
{
|
||||
name: 'Individual Fields',
|
||||
value: 'aggregateIndividualFields',
|
||||
},
|
||||
{
|
||||
name: 'All Item Data (Into a Single List)',
|
||||
value: 'aggregateAllItemData',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Fields To Aggregate',
|
||||
name: 'fieldsToAggregate',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
placeholder: 'Add Field To Aggregate',
|
||||
default: { fieldToAggregate: [{ fieldToAggregate: '', renameField: false }] },
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregate: ['aggregateIndividualFields'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: '',
|
||||
name: 'fieldToAggregate',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Input Field Name',
|
||||
name: 'fieldToAggregate',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The name of a field in the input items to aggregate together',
|
||||
// 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: 'Rename Field',
|
||||
name: 'renameField',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to give the field a different name in the output',
|
||||
},
|
||||
{
|
||||
displayName: 'Output Field Name',
|
||||
name: 'outputFieldName',
|
||||
displayOptions: {
|
||||
show: {
|
||||
renameField: [true],
|
||||
},
|
||||
},
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'The name of the field to put the aggregated data in. Leave blank to use the input field name.',
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
displayName: 'Put Output in Field',
|
||||
name: 'destinationFieldName',
|
||||
type: 'string',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregate: ['aggregateAllItemData'],
|
||||
},
|
||||
},
|
||||
default: 'data',
|
||||
description: 'The name of the output field to put the data in',
|
||||
},
|
||||
{
|
||||
displayName: 'Include',
|
||||
name: 'include',
|
||||
type: 'options',
|
||||
default: 'allFields',
|
||||
options: [
|
||||
{
|
||||
name: 'All Fields',
|
||||
value: 'allFields',
|
||||
},
|
||||
{
|
||||
name: 'Specified Fields',
|
||||
value: 'specifiedFields',
|
||||
},
|
||||
{
|
||||
name: 'All Fields Except',
|
||||
value: 'allFieldsExcept',
|
||||
},
|
||||
],
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregate: ['aggregateAllItemData'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Fields To Exclude',
|
||||
name: 'fieldsToExclude',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. email, name',
|
||||
default: '',
|
||||
requiresDataPath: 'multiple',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregate: ['aggregateAllItemData'],
|
||||
include: ['allFieldsExcept'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Fields To Include',
|
||||
name: 'fieldsToInclude',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. email, name',
|
||||
default: '',
|
||||
requiresDataPath: 'multiple',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregate: ['aggregateAllItemData'],
|
||||
include: ['specifiedFields'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
{
|
||||
...disableDotNotationBoolean,
|
||||
displayOptions: {
|
||||
hide: {
|
||||
'/aggregate': ['aggregateAllItemData'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Merge Lists',
|
||||
name: 'mergeLists',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to merge the output into a single flat list (rather than a list of lists), if the field to aggregate is a list',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
'/aggregate': ['aggregateAllItemData'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Include Binaries',
|
||||
name: 'includeBinaries',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to include the binary data in the new item',
|
||||
},
|
||||
{
|
||||
displayName: 'Keep Only Unique Binaries',
|
||||
name: 'keepOnlyUnique',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to keep only unique binaries by comparing mime types, file types, file sizes and file extensions',
|
||||
displayOptions: {
|
||||
show: {
|
||||
includeBinaries: [true],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Keep Missing And Null Values',
|
||||
name: 'keepMissing',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to add a null entry to the aggregated list when there is a missing or null value',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
'/aggregate': ['aggregateAllItemData'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['concatenateItems'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
): Promise<INodeExecutionData[]> {
|
||||
let returnData: INodeExecutionData = { json: {}, pairedItem: [] };
|
||||
|
||||
const aggregate = this.getNodeParameter('aggregate', 0, '') as string;
|
||||
|
||||
if (aggregate === 'aggregateIndividualFields') {
|
||||
const disableDotNotation = this.getNodeParameter(
|
||||
'options.disableDotNotation',
|
||||
0,
|
||||
false,
|
||||
) as boolean;
|
||||
const mergeLists = this.getNodeParameter('options.mergeLists', 0, false) as boolean;
|
||||
const fieldsToAggregate = this.getNodeParameter(
|
||||
'fieldsToAggregate.fieldToAggregate',
|
||||
0,
|
||||
[],
|
||||
) as [{ fieldToAggregate: string; renameField: boolean; outputFieldName: string }];
|
||||
const keepMissing = this.getNodeParameter('options.keepMissing', 0, false) as boolean;
|
||||
|
||||
if (!fieldsToAggregate.length) {
|
||||
throw new NodeOperationError(this.getNode(), 'No fields specified', {
|
||||
description: 'Please add a field to aggregate',
|
||||
});
|
||||
}
|
||||
|
||||
const newItem: INodeExecutionData = {
|
||||
json: {},
|
||||
pairedItem: Array.from({ length: items.length }, (_, i) => i).map((index) => {
|
||||
return {
|
||||
item: index,
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
const values: { [key: string]: any } = {};
|
||||
const outputFields: string[] = [];
|
||||
|
||||
for (const { fieldToAggregate, outputFieldName, renameField } of fieldsToAggregate) {
|
||||
const field = renameField ? outputFieldName : fieldToAggregate;
|
||||
|
||||
if (outputFields.includes(field)) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The '${field}' output field is used more than once`,
|
||||
{ description: 'Please make sure each output field name is unique' },
|
||||
);
|
||||
} else {
|
||||
outputFields.push(field);
|
||||
}
|
||||
|
||||
const getFieldToAggregate = () =>
|
||||
!disableDotNotation && fieldToAggregate.includes('.')
|
||||
? fieldToAggregate.split('.').pop()
|
||||
: fieldToAggregate;
|
||||
|
||||
const _outputFieldName = outputFieldName
|
||||
? outputFieldName
|
||||
: (getFieldToAggregate() as string);
|
||||
|
||||
if (fieldToAggregate !== '') {
|
||||
values[_outputFieldName] = [];
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
if (!disableDotNotation) {
|
||||
let value = get(items[i].json, fieldToAggregate);
|
||||
|
||||
if (!keepMissing) {
|
||||
if (Array.isArray(value)) {
|
||||
value = value.filter((entry) => entry !== null);
|
||||
} else if (value === null || value === undefined) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(value) && mergeLists) {
|
||||
values[_outputFieldName].push(...value);
|
||||
} else {
|
||||
values[_outputFieldName].push(value);
|
||||
}
|
||||
} else {
|
||||
let value = items[i].json[fieldToAggregate];
|
||||
|
||||
if (!keepMissing) {
|
||||
if (Array.isArray(value)) {
|
||||
value = value.filter((entry) => entry !== null);
|
||||
} else if (value === null || value === undefined) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (Array.isArray(value) && mergeLists) {
|
||||
values[_outputFieldName].push(...value);
|
||||
} else {
|
||||
values[_outputFieldName].push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const key of Object.keys(values)) {
|
||||
if (!disableDotNotation) {
|
||||
set(newItem.json, key, values[key]);
|
||||
} else {
|
||||
newItem.json[key] = values[key];
|
||||
}
|
||||
}
|
||||
|
||||
returnData = newItem;
|
||||
} else {
|
||||
let newItems: IDataObject[] = items.map((item) => item.json);
|
||||
let pairedItem: IPairedItemData[] = [];
|
||||
const destinationFieldName = this.getNodeParameter('destinationFieldName', 0) as string;
|
||||
|
||||
const fieldsToExclude = prepareFieldsArray(
|
||||
this.getNodeParameter('fieldsToExclude', 0, '') as string,
|
||||
'Fields To Exclude',
|
||||
);
|
||||
|
||||
const fieldsToInclude = prepareFieldsArray(
|
||||
this.getNodeParameter('fieldsToInclude', 0, '') as string,
|
||||
'Fields To Include',
|
||||
);
|
||||
|
||||
if (fieldsToExclude.length || fieldsToInclude.length) {
|
||||
newItems = newItems.reduce((acc, item, index) => {
|
||||
const newItem: IDataObject = {};
|
||||
let outputFields = Object.keys(item);
|
||||
|
||||
if (fieldsToExclude.length) {
|
||||
outputFields = outputFields.filter((key) => !fieldsToExclude.includes(key));
|
||||
}
|
||||
if (fieldsToInclude.length) {
|
||||
outputFields = outputFields.filter((key) =>
|
||||
fieldsToInclude.length ? fieldsToInclude.includes(key) : true,
|
||||
);
|
||||
}
|
||||
|
||||
outputFields.forEach((key) => {
|
||||
newItem[key] = item[key];
|
||||
});
|
||||
|
||||
if (isEmpty(newItem)) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
pairedItem.push({ item: index });
|
||||
return acc.concat([newItem]);
|
||||
}, [] as IDataObject[]);
|
||||
} else {
|
||||
pairedItem = Array.from({ length: newItems.length }, (_, item) => ({
|
||||
item,
|
||||
}));
|
||||
}
|
||||
|
||||
const output: INodeExecutionData = { json: { [destinationFieldName]: newItems }, pairedItem };
|
||||
|
||||
returnData = output;
|
||||
}
|
||||
|
||||
const includeBinaries = this.getNodeParameter('options.includeBinaries', 0, false) as boolean;
|
||||
|
||||
if (includeBinaries) {
|
||||
const pairedItems = (returnData.pairedItem || []) as IPairedItemData[];
|
||||
|
||||
const aggregatedItems = pairedItems.map((item) => {
|
||||
return items[item.item];
|
||||
});
|
||||
|
||||
const keepOnlyUnique = this.getNodeParameter('options.keepOnlyUnique', 0, false) as boolean;
|
||||
|
||||
addBinariesToItem(returnData, aggregatedItems, keepOnlyUnique);
|
||||
}
|
||||
|
||||
return [returnData];
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import type { INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import * as concatenateItems from './concatenateItems.operation';
|
||||
import * as limit from './limit.operation';
|
||||
import * as removeDuplicates from './removeDuplicates.operation';
|
||||
import * as sort from './sort.operation';
|
||||
import * as splitOutItems from './splitOutItems.operation';
|
||||
import * as summarize from './summarize.operation';
|
||||
|
||||
export { concatenateItems, limit, removeDuplicates, sort, splitOutItems, summarize };
|
||||
|
||||
export const description: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Operation',
|
||||
name: 'operation',
|
||||
type: 'options',
|
||||
noDataExpression: true,
|
||||
displayOptions: {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
{
|
||||
name: 'Concatenate Items',
|
||||
value: 'concatenateItems',
|
||||
description: 'Combine fields into a list in a single new item',
|
||||
action: 'Concatenate Items',
|
||||
},
|
||||
{
|
||||
name: 'Limit',
|
||||
value: 'limit',
|
||||
description: 'Remove items if there are too many',
|
||||
action: 'Limit',
|
||||
},
|
||||
{
|
||||
name: 'Remove Duplicates',
|
||||
value: 'removeDuplicates',
|
||||
description: 'Remove extra items that are similar',
|
||||
action: 'Remove Duplicates',
|
||||
},
|
||||
{
|
||||
name: 'Sort',
|
||||
value: 'sort',
|
||||
description: 'Change the item order',
|
||||
action: 'Sort',
|
||||
},
|
||||
{
|
||||
name: 'Split Out Items',
|
||||
value: 'splitOutItems',
|
||||
description:
|
||||
"Turn a list or values of object's properties inside item(s) into separate items",
|
||||
action: 'Split Out Items',
|
||||
},
|
||||
{
|
||||
name: 'Summarize',
|
||||
value: 'summarize',
|
||||
description: 'Aggregate items together (pivot table)',
|
||||
action: 'Summarize',
|
||||
},
|
||||
],
|
||||
default: 'splitOutItems',
|
||||
},
|
||||
...concatenateItems.description,
|
||||
...limit.description,
|
||||
...removeDuplicates.description,
|
||||
...sort.description,
|
||||
...splitOutItems.description,
|
||||
...summarize.description,
|
||||
];
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Max Items',
|
||||
name: 'maxItems',
|
||||
type: 'number',
|
||||
typeOptions: {
|
||||
minValue: 1,
|
||||
},
|
||||
default: 1,
|
||||
description: 'If there are more items than this number, some are removed',
|
||||
},
|
||||
{
|
||||
displayName: 'Keep',
|
||||
name: 'keep',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'First Items',
|
||||
value: 'firstItems',
|
||||
},
|
||||
{
|
||||
name: 'Last Items',
|
||||
value: 'lastItems',
|
||||
},
|
||||
],
|
||||
default: 'firstItems',
|
||||
description: 'When removing items, whether to keep the ones at the start or the ending',
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['limit'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
): Promise<INodeExecutionData[]> {
|
||||
let returnData = items;
|
||||
const maxItems = this.getNodeParameter('maxItems', 0) as number;
|
||||
const keep = this.getNodeParameter('keep', 0) as string;
|
||||
|
||||
if (maxItems > items.length) {
|
||||
return returnData;
|
||||
}
|
||||
|
||||
if (keep === 'firstItems') {
|
||||
returnData = items.slice(0, maxItems);
|
||||
} else {
|
||||
returnData = items.slice(items.length - maxItems, items.length);
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
+249
@@ -0,0 +1,249 @@
|
||||
import get from 'lodash/get';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import lt from 'lodash/lt';
|
||||
import pick from 'lodash/pick';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
import type { IExecuteFunctions, INodeExecutionData, INodeProperties } from 'n8n-workflow';
|
||||
|
||||
import { compareItems, flattenKeys, updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { prepareFieldsArray, typeToNumber } from '../../helpers/utils';
|
||||
import { disableDotNotationBoolean } from '../common.descriptions';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Compare',
|
||||
name: 'compare',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'All Fields',
|
||||
value: 'allFields',
|
||||
},
|
||||
{
|
||||
name: 'All Fields Except',
|
||||
value: 'allFieldsExcept',
|
||||
},
|
||||
{
|
||||
name: 'Selected Fields',
|
||||
value: 'selectedFields',
|
||||
},
|
||||
],
|
||||
default: 'allFields',
|
||||
description: 'The fields of the input items to compare to see if they are the same',
|
||||
},
|
||||
{
|
||||
displayName: 'Fields To Exclude',
|
||||
name: 'fieldsToExclude',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. email, name',
|
||||
requiresDataPath: 'multiple',
|
||||
description: 'Fields in the input to exclude from the comparison',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
compare: ['allFieldsExcept'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Fields To Compare',
|
||||
name: 'fieldsToCompare',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. email, name',
|
||||
requiresDataPath: 'multiple',
|
||||
description: 'Fields in the input to add to the comparison',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
compare: ['selectedFields'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
compare: ['allFieldsExcept', 'selectedFields'],
|
||||
},
|
||||
},
|
||||
options: [
|
||||
disableDotNotationBoolean,
|
||||
{
|
||||
displayName: 'Remove Other Fields',
|
||||
name: 'removeOtherFields',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description:
|
||||
'Whether to remove any fields that are not being compared. If disabled, will keep the values from the first of the duplicates.',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['removeDuplicates'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const compare = this.getNodeParameter('compare', 0) as string;
|
||||
const disableDotNotation = this.getNodeParameter(
|
||||
'options.disableDotNotation',
|
||||
0,
|
||||
false,
|
||||
) as boolean;
|
||||
const removeOtherFields = this.getNodeParameter('options.removeOtherFields', 0, false) as boolean;
|
||||
const nodeVersion = this.getNode().typeVersion;
|
||||
|
||||
let keys = disableDotNotation
|
||||
? Object.keys(items[0].json)
|
||||
: Object.keys(flattenKeys(items[0].json));
|
||||
|
||||
for (const item of items) {
|
||||
for (const key of disableDotNotation
|
||||
? Object.keys(item.json)
|
||||
: Object.keys(flattenKeys(item.json))) {
|
||||
if (!keys.includes(key)) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (compare === 'allFieldsExcept') {
|
||||
const fieldsToExclude = prepareFieldsArray(
|
||||
this.getNodeParameter('fieldsToExclude', 0, '') as string,
|
||||
'Fields To Exclude',
|
||||
);
|
||||
|
||||
if (!fieldsToExclude.length) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'No fields specified. Please add a field to exclude from comparison',
|
||||
);
|
||||
}
|
||||
if (!disableDotNotation) {
|
||||
keys = Object.keys(flattenKeys(items[0].json));
|
||||
}
|
||||
keys = keys.filter((key) => !fieldsToExclude.includes(key));
|
||||
}
|
||||
if (compare === 'selectedFields') {
|
||||
const fieldsToCompare = prepareFieldsArray(
|
||||
this.getNodeParameter('fieldsToCompare', 0, '') as string,
|
||||
'Fields To Compare',
|
||||
);
|
||||
if (!fieldsToCompare.length) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'No fields specified. Please add a field to compare on',
|
||||
);
|
||||
}
|
||||
if (!disableDotNotation) {
|
||||
keys = Object.keys(flattenKeys(items[0].json));
|
||||
}
|
||||
keys = fieldsToCompare.map((key) => key.trim());
|
||||
}
|
||||
|
||||
// This solution is O(nlogn)
|
||||
// add original index to the items
|
||||
const newItems = items.map(
|
||||
(item, index) =>
|
||||
({
|
||||
json: { ...item.json, __INDEX: index },
|
||||
pairedItem: { item: index },
|
||||
}) as INodeExecutionData,
|
||||
);
|
||||
|
||||
//sort items using the compare keys
|
||||
newItems.sort((a, b) => {
|
||||
let result = 0;
|
||||
|
||||
for (const key of keys) {
|
||||
const a_value = disableDotNotation ? a.json[key] : get(a.json, key);
|
||||
const b_value = disableDotNotation ? b.json[key] : get(b.json, key);
|
||||
|
||||
if (nodeVersion >= 3.1) {
|
||||
const a_value_tnum = typeToNumber(a_value);
|
||||
const b_value_tnum = typeToNumber(b_value);
|
||||
if (a_value_tnum !== b_value_tnum) {
|
||||
result = a_value_tnum - b_value_tnum;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const equal = isEqual(a_value, b_value);
|
||||
|
||||
if (!equal) {
|
||||
const lessThan = lt(a_value, b_value);
|
||||
result = lessThan ? -1 : 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
for (const key of keys) {
|
||||
let type: any = undefined;
|
||||
for (const item of newItems) {
|
||||
if (key === '') {
|
||||
throw new NodeOperationError(this.getNode(), 'Name of field to compare is blank');
|
||||
}
|
||||
const value = !disableDotNotation ? get(item.json, key) : item.json[key];
|
||||
if (value === undefined && disableDotNotation && key.includes('.')) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`'${key}' field is missing from some input items`,
|
||||
{
|
||||
description:
|
||||
"If you're trying to use a nested field, make sure you turn off 'disable dot notation' in the node options",
|
||||
},
|
||||
);
|
||||
} else if (value === undefined) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`'${key}' field is missing from some input items`,
|
||||
);
|
||||
}
|
||||
if (nodeVersion < 3.1 && type !== undefined && value !== undefined && type !== typeof value) {
|
||||
throw new NodeOperationError(this.getNode(), `'${key}' isn't always the same type`, {
|
||||
description: 'The type of this field varies between items',
|
||||
});
|
||||
} else {
|
||||
type = typeof value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// collect the original indexes of items to be removed
|
||||
const removedIndexes: number[] = [];
|
||||
let temp = newItems[0];
|
||||
for (let index = 1; index < newItems.length; index++) {
|
||||
if (compareItems(newItems[index], temp, keys, disableDotNotation)) {
|
||||
removedIndexes.push(newItems[index].json.__INDEX as unknown as number);
|
||||
} else {
|
||||
temp = newItems[index];
|
||||
}
|
||||
}
|
||||
|
||||
let returnData = items.filter((_, index) => !removedIndexes.includes(index));
|
||||
|
||||
if (removeOtherFields) {
|
||||
returnData = returnData.map((item, index) => ({
|
||||
json: pick(item.json, ...keys),
|
||||
pairedItem: { item: index },
|
||||
}));
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
import get from 'lodash/get';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import lt from 'lodash/lt';
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { shuffleArray, updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { sortByCode } from '../../helpers/utils';
|
||||
import { disableDotNotationBoolean } from '../common.descriptions';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Type',
|
||||
name: 'type',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Simple',
|
||||
value: 'simple',
|
||||
},
|
||||
{
|
||||
name: 'Random',
|
||||
value: 'random',
|
||||
},
|
||||
{
|
||||
name: 'Code',
|
||||
value: 'code',
|
||||
},
|
||||
],
|
||||
default: 'simple',
|
||||
description: 'The fields of the input items to compare to see if they are the same',
|
||||
},
|
||||
{
|
||||
displayName: 'Fields To Sort By',
|
||||
name: 'sortFieldsUi',
|
||||
type: 'fixedCollection',
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
placeholder: 'Add Field To Sort By',
|
||||
options: [
|
||||
{
|
||||
displayName: '',
|
||||
name: 'sortField',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Field Name',
|
||||
name: 'fieldName',
|
||||
type: 'string',
|
||||
required: true,
|
||||
default: '',
|
||||
description: 'The field to sort by',
|
||||
// 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: 'Order',
|
||||
name: 'order',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Ascending',
|
||||
value: 'ascending',
|
||||
},
|
||||
{
|
||||
name: 'Descending',
|
||||
value: 'descending',
|
||||
},
|
||||
],
|
||||
default: 'ascending',
|
||||
description: 'The order to sort by',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
default: {},
|
||||
description: 'The fields of the input items to compare to see if they are the same',
|
||||
displayOptions: {
|
||||
show: {
|
||||
type: ['simple'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Code',
|
||||
name: 'code',
|
||||
type: 'string',
|
||||
typeOptions: {
|
||||
alwaysOpenEditWindow: true,
|
||||
editor: 'jsEditor',
|
||||
rows: 10,
|
||||
},
|
||||
default: `// The two items to compare are in the variables a and b
|
||||
// Access the fields in a.json and b.json
|
||||
// Return -1 if a should go before b
|
||||
// Return 1 if b should go before a
|
||||
// Return 0 if there's no difference
|
||||
|
||||
fieldName = 'myField';
|
||||
|
||||
if (a.json[fieldName] < b.json[fieldName]) {
|
||||
return -1;
|
||||
}
|
||||
if (a.json[fieldName] > b.json[fieldName]) {
|
||||
return 1;
|
||||
}
|
||||
return 0;`,
|
||||
description: 'Javascript code to determine the order of any two items',
|
||||
displayOptions: {
|
||||
show: {
|
||||
type: ['code'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
displayOptions: {
|
||||
show: {
|
||||
type: ['simple'],
|
||||
},
|
||||
},
|
||||
options: [disableDotNotationBoolean],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['sort'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
): Promise<INodeExecutionData[]> {
|
||||
let returnData = [...items];
|
||||
const type = this.getNodeParameter('type', 0) as string;
|
||||
const disableDotNotation = this.getNodeParameter(
|
||||
'options.disableDotNotation',
|
||||
0,
|
||||
false,
|
||||
) as boolean;
|
||||
|
||||
if (type === 'random') {
|
||||
shuffleArray(returnData);
|
||||
return returnData;
|
||||
}
|
||||
|
||||
if (type === 'simple') {
|
||||
const sortFieldsUi = this.getNodeParameter('sortFieldsUi', 0) as IDataObject;
|
||||
const sortFields = sortFieldsUi.sortField as Array<{
|
||||
fieldName: string;
|
||||
order: 'ascending' | 'descending';
|
||||
}>;
|
||||
|
||||
if (!sortFields?.length) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'No sorting specified. Please add a field to sort by',
|
||||
);
|
||||
}
|
||||
|
||||
for (const { fieldName } of sortFields) {
|
||||
let found = false;
|
||||
for (const item of items) {
|
||||
if (!disableDotNotation) {
|
||||
if (get(item.json, fieldName) !== undefined) {
|
||||
found = true;
|
||||
}
|
||||
} else if (item.json.hasOwnProperty(fieldName)) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found && disableDotNotation && fieldName.includes('.')) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`Couldn't find the field '${fieldName}' in the input data`,
|
||||
{
|
||||
description:
|
||||
"If you're trying to use a nested field, make sure you turn off 'disable dot notation' in the node options",
|
||||
},
|
||||
);
|
||||
} else if (!found) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`Couldn't find the field '${fieldName}' in the input data`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const sortFieldsWithDirection = sortFields.map((field) => ({
|
||||
name: field.fieldName,
|
||||
dir: field.order === 'ascending' ? 1 : -1,
|
||||
}));
|
||||
|
||||
returnData.sort((a, b) => {
|
||||
let result = 0;
|
||||
for (const field of sortFieldsWithDirection) {
|
||||
let equal;
|
||||
if (!disableDotNotation) {
|
||||
const _a =
|
||||
typeof get(a.json, field.name) === 'string'
|
||||
? (get(a.json, field.name) as string).toLowerCase()
|
||||
: get(a.json, field.name);
|
||||
const _b =
|
||||
typeof get(b.json, field.name) === 'string'
|
||||
? (get(b.json, field.name) as string).toLowerCase()
|
||||
: get(b.json, field.name);
|
||||
equal = isEqual(_a, _b);
|
||||
} else {
|
||||
const _a =
|
||||
typeof a.json[field.name] === 'string'
|
||||
? (a.json[field.name] as string).toLowerCase()
|
||||
: a.json[field.name];
|
||||
const _b =
|
||||
typeof b.json[field.name] === 'string'
|
||||
? (b.json[field.name] as string).toLowerCase()
|
||||
: b.json[field.name];
|
||||
equal = isEqual(_a, _b);
|
||||
}
|
||||
|
||||
if (!equal) {
|
||||
let lessThan;
|
||||
if (!disableDotNotation) {
|
||||
const _a =
|
||||
typeof get(a.json, field.name) === 'string'
|
||||
? (get(a.json, field.name) as string).toLowerCase()
|
||||
: get(a.json, field.name);
|
||||
const _b =
|
||||
typeof get(b.json, field.name) === 'string'
|
||||
? (get(b.json, field.name) as string).toLowerCase()
|
||||
: get(b.json, field.name);
|
||||
lessThan = lt(_a, _b);
|
||||
} else {
|
||||
const _a =
|
||||
typeof a.json[field.name] === 'string'
|
||||
? (a.json[field.name] as string).toLowerCase()
|
||||
: a.json[field.name];
|
||||
const _b =
|
||||
typeof b.json[field.name] === 'string'
|
||||
? (b.json[field.name] as string).toLowerCase()
|
||||
: b.json[field.name];
|
||||
lessThan = lt(_a, _b);
|
||||
}
|
||||
if (lessThan) {
|
||||
result = -1 * field.dir;
|
||||
} else {
|
||||
result = 1 * field.dir;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
} else {
|
||||
returnData = await sortByCode.call(this, returnData);
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
import get from 'lodash/get';
|
||||
import unset from 'lodash/unset';
|
||||
import type {
|
||||
IBinaryData,
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { deepCopy, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { prepareFieldsArray } from '../../helpers/utils';
|
||||
import { disableDotNotationBoolean } from '../common.descriptions';
|
||||
|
||||
const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Fields To Split Out',
|
||||
name: 'fieldToSplitOut',
|
||||
type: 'string',
|
||||
default: '',
|
||||
required: true,
|
||||
placeholder: 'Drag fields from the left or type their names',
|
||||
description:
|
||||
'The name of the input fields to break out into separate items. Separate multiple field names by commas. For binary data, use $binary.',
|
||||
requiresDataPath: 'multiple',
|
||||
},
|
||||
{
|
||||
displayName: 'Include',
|
||||
name: 'include',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'No Other Fields',
|
||||
value: 'noOtherFields',
|
||||
},
|
||||
{
|
||||
name: 'All Other Fields',
|
||||
value: 'allOtherFields',
|
||||
},
|
||||
{
|
||||
name: 'Selected Other Fields',
|
||||
value: 'selectedOtherFields',
|
||||
},
|
||||
],
|
||||
default: 'noOtherFields',
|
||||
description: 'Whether to copy any other fields into the new items',
|
||||
},
|
||||
{
|
||||
displayName: 'Fields To Include',
|
||||
name: 'fieldsToInclude',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. email, name',
|
||||
requiresDataPath: 'multiple',
|
||||
description: 'Fields in the input items to aggregate together',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
include: ['selectedOtherFields'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add Field',
|
||||
default: {},
|
||||
options: [
|
||||
disableDotNotationBoolean,
|
||||
{
|
||||
displayName: 'Destination Field Name',
|
||||
name: 'destinationFieldName',
|
||||
type: 'string',
|
||||
requiresDataPath: 'multiple',
|
||||
default: '',
|
||||
description: 'The field in the output under which to put the split field contents',
|
||||
},
|
||||
{
|
||||
displayName: 'Include Binary',
|
||||
name: 'includeBinary',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
description: 'Whether to include the binary data in the new items',
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['splitOutItems'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const returnData: INodeExecutionData[] = [];
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const fieldsToSplitOut = (this.getNodeParameter('fieldToSplitOut', i) as string)
|
||||
.split(',')
|
||||
.map((field) => field.trim().replace(/^\$json\./, ''));
|
||||
|
||||
const options = this.getNodeParameter('options', i, {});
|
||||
|
||||
const disableDotNotation = options.disableDotNotation as boolean;
|
||||
|
||||
const destinationFields = ((options.destinationFieldName as string) || '')
|
||||
.split(',')
|
||||
.filter((field) => field.trim() !== '')
|
||||
.map((field) => field.trim());
|
||||
|
||||
if (destinationFields.length && destinationFields.length !== fieldsToSplitOut.length) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
'If multiple fields to split out are given, the same number of destination fields must be given',
|
||||
);
|
||||
}
|
||||
|
||||
const include = this.getNodeParameter('include', i) as
|
||||
| 'selectedOtherFields'
|
||||
| 'allOtherFields'
|
||||
| 'noOtherFields';
|
||||
|
||||
const multiSplit = fieldsToSplitOut.length > 1;
|
||||
|
||||
const item = { ...items[i].json };
|
||||
const splited: INodeExecutionData[] = [];
|
||||
for (const [entryIndex, fieldToSplitOut] of fieldsToSplitOut.entries()) {
|
||||
const destinationFieldName = destinationFields[entryIndex] || '';
|
||||
|
||||
let entityToSplit: IDataObject[] = [];
|
||||
|
||||
if (fieldToSplitOut === '$binary') {
|
||||
entityToSplit = Object.entries(items[i].binary || {}).map(([key, value]) => ({
|
||||
[key]: value,
|
||||
}));
|
||||
} else {
|
||||
if (!disableDotNotation) {
|
||||
entityToSplit = get(item, fieldToSplitOut) as IDataObject[];
|
||||
} else {
|
||||
entityToSplit = item[fieldToSplitOut] as IDataObject[];
|
||||
}
|
||||
|
||||
if (entityToSplit === undefined) {
|
||||
entityToSplit = [];
|
||||
}
|
||||
|
||||
if (typeof entityToSplit !== 'object' || entityToSplit === null) {
|
||||
entityToSplit = [entityToSplit] as unknown as IDataObject[];
|
||||
}
|
||||
|
||||
if (!Array.isArray(entityToSplit)) {
|
||||
entityToSplit = Object.values(entityToSplit);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [elementIndex, element] of entityToSplit.entries()) {
|
||||
if (splited[elementIndex] === undefined) {
|
||||
splited[elementIndex] = { json: {}, pairedItem: { item: i } };
|
||||
}
|
||||
|
||||
const fieldName = destinationFieldName || fieldToSplitOut;
|
||||
|
||||
if (fieldToSplitOut === '$binary') {
|
||||
if (splited[elementIndex].binary === undefined) {
|
||||
splited[elementIndex].binary = {};
|
||||
}
|
||||
splited[elementIndex].binary[Object.keys(element)[0]] = Object.values(
|
||||
element,
|
||||
)[0] as IBinaryData;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof element === 'object' && element !== null && include === 'noOtherFields') {
|
||||
if (destinationFieldName === '' && !multiSplit) {
|
||||
splited[elementIndex] = {
|
||||
json: { ...splited[elementIndex].json, ...element },
|
||||
pairedItem: { item: i },
|
||||
};
|
||||
} else {
|
||||
splited[elementIndex].json[fieldName] = element;
|
||||
}
|
||||
} else {
|
||||
splited[elementIndex].json[fieldName] = element;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const splitEntry of splited) {
|
||||
let newItem: INodeExecutionData = splitEntry;
|
||||
|
||||
if (include === 'allOtherFields') {
|
||||
const itemCopy = deepCopy(item);
|
||||
for (const fieldToSplitOut of fieldsToSplitOut) {
|
||||
if (!disableDotNotation) {
|
||||
unset(itemCopy, fieldToSplitOut);
|
||||
} else {
|
||||
delete itemCopy[fieldToSplitOut];
|
||||
}
|
||||
}
|
||||
newItem.json = { ...itemCopy, ...splitEntry.json };
|
||||
}
|
||||
|
||||
if (include === 'selectedOtherFields') {
|
||||
const fieldsToInclude = prepareFieldsArray(
|
||||
this.getNodeParameter('fieldsToInclude', i, '') as string,
|
||||
'Fields To Include',
|
||||
);
|
||||
|
||||
if (!fieldsToInclude.length) {
|
||||
throw new NodeOperationError(this.getNode(), 'No fields specified', {
|
||||
description: 'Please add a field to include',
|
||||
});
|
||||
}
|
||||
|
||||
for (const field of fieldsToInclude) {
|
||||
if (!disableDotNotation) {
|
||||
splitEntry.json[field] = get(item, field);
|
||||
} else {
|
||||
splitEntry.json[field] = item[field];
|
||||
}
|
||||
}
|
||||
|
||||
newItem = splitEntry;
|
||||
}
|
||||
|
||||
const includeBinary = options.includeBinary as boolean;
|
||||
|
||||
if (includeBinary) {
|
||||
if (items[i].binary && !newItem.binary) {
|
||||
newItem.binary = items[i].binary;
|
||||
}
|
||||
}
|
||||
|
||||
returnData.push(newItem);
|
||||
}
|
||||
}
|
||||
|
||||
return returnData;
|
||||
}
|
||||
@@ -0,0 +1,616 @@
|
||||
import get from 'lodash/get';
|
||||
import type {
|
||||
GenericValue,
|
||||
IDataObject,
|
||||
IExecuteFunctions,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { updateDisplayOptions } from '@utils/utilities';
|
||||
|
||||
import { disableDotNotationBoolean } from '../common.descriptions';
|
||||
|
||||
type AggregationType =
|
||||
| 'append'
|
||||
| 'average'
|
||||
| 'concatenate'
|
||||
| 'count'
|
||||
| 'countUnique'
|
||||
| 'max'
|
||||
| 'min'
|
||||
| 'sum';
|
||||
|
||||
type Aggregation = {
|
||||
aggregation: AggregationType;
|
||||
field: string;
|
||||
includeEmpty?: boolean;
|
||||
separateBy?: string;
|
||||
customSeparator?: string;
|
||||
};
|
||||
|
||||
type Aggregations = Aggregation[];
|
||||
|
||||
const AggregationDisplayNames = {
|
||||
append: 'appended_',
|
||||
average: 'average_',
|
||||
concatenate: 'concatenated_',
|
||||
count: 'count_',
|
||||
countUnique: 'unique_count_',
|
||||
max: 'max_',
|
||||
min: 'min_',
|
||||
sum: 'sum_',
|
||||
};
|
||||
|
||||
const NUMERICAL_AGGREGATIONS = ['average', 'sum'];
|
||||
|
||||
type SummarizeOptions = {
|
||||
disableDotNotation?: boolean;
|
||||
outputFormat?: 'separateItems' | 'singleItem';
|
||||
skipEmptySplitFields?: boolean;
|
||||
};
|
||||
|
||||
type ValueGetterFn = (
|
||||
item: IDataObject,
|
||||
field: string,
|
||||
) => IDataObject | IDataObject[] | GenericValue | GenericValue[];
|
||||
|
||||
export const properties: INodeProperties[] = [
|
||||
{
|
||||
displayName: 'Fields to Summarize',
|
||||
name: 'fieldsToSummarize',
|
||||
type: 'fixedCollection',
|
||||
placeholder: 'Add Field',
|
||||
default: { values: [{ aggregation: 'count', field: '' }] },
|
||||
typeOptions: {
|
||||
multipleValues: true,
|
||||
},
|
||||
options: [
|
||||
{
|
||||
displayName: '',
|
||||
name: 'values',
|
||||
values: [
|
||||
{
|
||||
displayName: 'Aggregation',
|
||||
name: 'aggregation',
|
||||
type: 'options',
|
||||
options: [
|
||||
{
|
||||
name: 'Append',
|
||||
value: 'append',
|
||||
},
|
||||
{
|
||||
name: 'Average',
|
||||
value: 'average',
|
||||
},
|
||||
{
|
||||
name: 'Concatenate',
|
||||
value: 'concatenate',
|
||||
},
|
||||
{
|
||||
name: 'Count',
|
||||
value: 'count',
|
||||
},
|
||||
{
|
||||
name: 'Count Unique',
|
||||
value: 'countUnique',
|
||||
},
|
||||
{
|
||||
name: 'Max',
|
||||
value: 'max',
|
||||
},
|
||||
{
|
||||
name: 'Min',
|
||||
value: 'min',
|
||||
},
|
||||
{
|
||||
name: 'Sum',
|
||||
value: 'sum',
|
||||
},
|
||||
],
|
||||
default: 'count',
|
||||
description: 'How to combine the values of the field you want to summarize',
|
||||
},
|
||||
//field repeated to have different descriptions for different aggregations --------------------------------
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description: 'The name of an input field that you want to summarize',
|
||||
placeholder: 'e.g. cost',
|
||||
hint: ' Enter the field name as text',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
aggregation: [...NUMERICAL_AGGREGATIONS, 'countUnique', 'count', 'max', 'min'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'The name of an input field that you want to summarize. The field should contain numerical values; null, undefined, empty strings would be ignored.',
|
||||
placeholder: 'e.g. cost',
|
||||
hint: ' Enter the field name as text',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: NUMERICAL_AGGREGATIONS,
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
{
|
||||
displayName: 'Field',
|
||||
name: 'field',
|
||||
type: 'string',
|
||||
default: '',
|
||||
description:
|
||||
'The name of an input field that you want to summarize; null, undefined, empty strings would be ignored',
|
||||
placeholder: 'e.g. cost',
|
||||
hint: ' Enter the field name as text',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['countUnique', 'count', 'max', 'min'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'single',
|
||||
},
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
displayName: 'Include Empty Values',
|
||||
name: 'includeEmpty',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['append', 'concatenate'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Separator',
|
||||
name: 'separateBy',
|
||||
type: 'options',
|
||||
default: ',',
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-options-type-unsorted-items
|
||||
options: [
|
||||
{
|
||||
name: 'Comma',
|
||||
value: ',',
|
||||
},
|
||||
{
|
||||
name: 'Comma and Space',
|
||||
value: ', ',
|
||||
},
|
||||
{
|
||||
name: 'New Line',
|
||||
value: '\n',
|
||||
},
|
||||
{
|
||||
name: 'None',
|
||||
value: '',
|
||||
},
|
||||
{
|
||||
name: 'Space',
|
||||
value: ' ',
|
||||
},
|
||||
{
|
||||
name: 'Other',
|
||||
value: 'other',
|
||||
},
|
||||
],
|
||||
hint: 'What to insert between values',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['concatenate'],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Custom Separator',
|
||||
name: 'customSeparator',
|
||||
type: 'string',
|
||||
default: '',
|
||||
displayOptions: {
|
||||
show: {
|
||||
aggregation: ['concatenate'],
|
||||
separateBy: ['other'],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// fieldsToSplitBy repeated to have different displayName for singleItem and separateItems -----------------------------
|
||||
{
|
||||
displayName: 'Fields to Split By',
|
||||
name: 'fieldsToSplitBy',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. country, city',
|
||||
default: '',
|
||||
description: 'The name of the input fields that you want to split the summary by',
|
||||
hint: 'Enter the name of the fields as text (separated by commas)',
|
||||
displayOptions: {
|
||||
hide: {
|
||||
'/options.outputFormat': ['singleItem'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'multiple',
|
||||
},
|
||||
{
|
||||
displayName: 'Fields to Group By',
|
||||
name: 'fieldsToSplitBy',
|
||||
type: 'string',
|
||||
placeholder: 'e.g. country, city',
|
||||
default: '',
|
||||
description: 'The name of the input fields that you want to split the summary by',
|
||||
hint: 'Enter the name of the fields as text (separated by commas)',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'/options.outputFormat': ['singleItem'],
|
||||
},
|
||||
},
|
||||
requiresDataPath: 'multiple',
|
||||
},
|
||||
// ----------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
type: 'collection',
|
||||
placeholder: 'Add option',
|
||||
default: {},
|
||||
options: [
|
||||
disableDotNotationBoolean,
|
||||
{
|
||||
displayName: 'Output Format',
|
||||
name: 'outputFormat',
|
||||
type: 'options',
|
||||
default: 'separateItems',
|
||||
options: [
|
||||
{
|
||||
name: 'Each Split in a Separate Item',
|
||||
value: 'separateItems',
|
||||
},
|
||||
{
|
||||
name: 'All Splits in a Single Item',
|
||||
value: 'singleItem',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
|
||||
displayName: 'Ignore items without valid fields to group by',
|
||||
name: 'skipEmptySplitFields',
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const displayOptions = {
|
||||
show: {
|
||||
resource: ['itemList'],
|
||||
operation: ['summarize'],
|
||||
},
|
||||
};
|
||||
|
||||
export const description = updateDisplayOptions(displayOptions, properties);
|
||||
|
||||
function isEmpty<T>(value: T) {
|
||||
return value === undefined || value === null || value === '';
|
||||
}
|
||||
|
||||
function parseReturnData(returnData: IDataObject) {
|
||||
const regexBrackets = /[\]\["]/g;
|
||||
const regexSpaces = /[ .]/g;
|
||||
for (const key of Object.keys(returnData)) {
|
||||
if (key.match(regexBrackets)) {
|
||||
const newKey = key.replace(regexBrackets, '');
|
||||
returnData[newKey] = returnData[key];
|
||||
delete returnData[key];
|
||||
}
|
||||
}
|
||||
for (const key of Object.keys(returnData)) {
|
||||
if (key.match(regexSpaces)) {
|
||||
const newKey = key.replace(regexSpaces, '_');
|
||||
returnData[newKey] = returnData[key];
|
||||
delete returnData[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function parseFieldName(fieldName: string[]) {
|
||||
const regexBrackets = /[\]\["]/g;
|
||||
const regexSpaces = /[ .]/g;
|
||||
fieldName = fieldName.map((field) => {
|
||||
field = field.replace(regexBrackets, '');
|
||||
field = field.replace(regexSpaces, '_');
|
||||
return field;
|
||||
});
|
||||
return fieldName;
|
||||
}
|
||||
|
||||
const fieldValueGetter = (disableDotNotation?: boolean) => {
|
||||
if (disableDotNotation) {
|
||||
return (item: IDataObject, field: string) => item[field];
|
||||
} else {
|
||||
return (item: IDataObject, field: string) => get(item, field);
|
||||
}
|
||||
};
|
||||
|
||||
function checkIfFieldExists(
|
||||
this: IExecuteFunctions,
|
||||
items: IDataObject[],
|
||||
aggregations: Aggregations,
|
||||
getValue: ValueGetterFn,
|
||||
) {
|
||||
for (const aggregation of aggregations) {
|
||||
if (aggregation.field === '') {
|
||||
continue;
|
||||
}
|
||||
const exist = items.some((item) => getValue(item, aggregation.field) !== undefined);
|
||||
if (!exist) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The field '${aggregation.field}' does not exist in any items`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function aggregate(items: IDataObject[], entry: Aggregation, getValue: ValueGetterFn) {
|
||||
const { aggregation, field } = entry;
|
||||
let data = [...items];
|
||||
|
||||
if (NUMERICAL_AGGREGATIONS.includes(aggregation)) {
|
||||
data = data.filter(
|
||||
(item) => typeof getValue(item, field) === 'number' && !isEmpty(getValue(item, field)),
|
||||
);
|
||||
}
|
||||
|
||||
switch (aggregation) {
|
||||
//combine operations
|
||||
case 'append':
|
||||
if (!entry.includeEmpty) {
|
||||
data = data.filter((item) => !isEmpty(getValue(item, field)));
|
||||
}
|
||||
return data.map((item) => getValue(item, field));
|
||||
case 'concatenate':
|
||||
const separateBy = entry.separateBy === 'other' ? entry.customSeparator : entry.separateBy;
|
||||
if (!entry.includeEmpty) {
|
||||
data = data.filter((item) => !isEmpty(getValue(item, field)));
|
||||
}
|
||||
return data
|
||||
.map((item) => {
|
||||
let value = getValue(item, field);
|
||||
if (typeof value === 'object') {
|
||||
value = JSON.stringify(value);
|
||||
}
|
||||
if (typeof value === 'undefined') {
|
||||
value = 'undefined';
|
||||
}
|
||||
|
||||
return value;
|
||||
})
|
||||
.join(separateBy);
|
||||
|
||||
//numerical operations
|
||||
case 'average':
|
||||
return (
|
||||
data.reduce((acc, item) => {
|
||||
return acc + (getValue(item, field) as number);
|
||||
}, 0) / data.length
|
||||
);
|
||||
case 'sum':
|
||||
return data.reduce((acc, item) => {
|
||||
return acc + (getValue(item, field) as number);
|
||||
}, 0);
|
||||
//comparison operations
|
||||
case 'min':
|
||||
let min;
|
||||
for (const item of data) {
|
||||
const value = getValue(item, field);
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
if (min === undefined || value < min) {
|
||||
min = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return min !== undefined ? min : null;
|
||||
case 'max':
|
||||
let max;
|
||||
for (const item of data) {
|
||||
const value = getValue(item, field);
|
||||
if (value !== undefined && value !== null && value !== '') {
|
||||
if (max === undefined || value > max) {
|
||||
max = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return max !== undefined ? max : null;
|
||||
|
||||
//count operations
|
||||
case 'countUnique':
|
||||
return new Set(data.map((item) => getValue(item, field)).filter((item) => !isEmpty(item)))
|
||||
.size;
|
||||
default:
|
||||
//count by default
|
||||
return data.filter((item) => !isEmpty(getValue(item, field))).length;
|
||||
}
|
||||
}
|
||||
|
||||
function aggregateData(
|
||||
data: IDataObject[],
|
||||
fieldsToSummarize: Aggregations,
|
||||
options: SummarizeOptions,
|
||||
getValue: ValueGetterFn,
|
||||
) {
|
||||
const returnData = fieldsToSummarize.reduce((acc, aggregation) => {
|
||||
acc[`${AggregationDisplayNames[aggregation.aggregation]}${aggregation.field}`] = aggregate(
|
||||
data,
|
||||
aggregation,
|
||||
getValue,
|
||||
);
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
parseReturnData(returnData);
|
||||
if (options.outputFormat === 'singleItem') {
|
||||
parseReturnData(returnData);
|
||||
return returnData;
|
||||
} else {
|
||||
return { ...returnData, pairedItems: data.map((item) => item._itemIndex as number) };
|
||||
}
|
||||
}
|
||||
|
||||
function splitData(
|
||||
splitKeys: string[],
|
||||
data: IDataObject[],
|
||||
fieldsToSummarize: Aggregations,
|
||||
options: SummarizeOptions,
|
||||
getValue: ValueGetterFn,
|
||||
) {
|
||||
if (!splitKeys || splitKeys.length === 0) {
|
||||
return aggregateData(data, fieldsToSummarize, options, getValue);
|
||||
}
|
||||
|
||||
const [firstSplitKey, ...restSplitKeys] = splitKeys;
|
||||
|
||||
const groupedData = data.reduce((acc, item) => {
|
||||
let keyValuee = getValue(item, firstSplitKey) as string;
|
||||
|
||||
if (typeof keyValuee === 'object') {
|
||||
keyValuee = JSON.stringify(keyValuee);
|
||||
}
|
||||
|
||||
if (options.skipEmptySplitFields && typeof keyValuee !== 'number' && !keyValuee) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
if (acc[keyValuee] === undefined) {
|
||||
acc[keyValuee] = [item];
|
||||
} else {
|
||||
(acc[keyValuee] as IDataObject[]).push(item);
|
||||
}
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
|
||||
return Object.keys(groupedData).reduce((acc, key) => {
|
||||
const value = groupedData[key] as IDataObject[];
|
||||
acc[key] = splitData(restSplitKeys, value, fieldsToSummarize, options, getValue);
|
||||
return acc;
|
||||
}, {} as IDataObject);
|
||||
}
|
||||
|
||||
function aggregationToArray(
|
||||
aggregationResult: IDataObject,
|
||||
fieldsToSplitBy: string[],
|
||||
previousStage: IDataObject = {},
|
||||
) {
|
||||
const returnData: IDataObject[] = [];
|
||||
fieldsToSplitBy = parseFieldName(fieldsToSplitBy);
|
||||
const splitFieldName = fieldsToSplitBy[0];
|
||||
const isNext = fieldsToSplitBy[1];
|
||||
|
||||
if (isNext === undefined) {
|
||||
for (const fieldName of Object.keys(aggregationResult)) {
|
||||
returnData.push({
|
||||
...previousStage,
|
||||
[splitFieldName]: fieldName,
|
||||
...(aggregationResult[fieldName] as IDataObject),
|
||||
});
|
||||
}
|
||||
return returnData;
|
||||
} else {
|
||||
for (const key of Object.keys(aggregationResult)) {
|
||||
returnData.push(
|
||||
...aggregationToArray(aggregationResult[key] as IDataObject, fieldsToSplitBy.slice(1), {
|
||||
...previousStage,
|
||||
[splitFieldName]: key,
|
||||
}),
|
||||
);
|
||||
}
|
||||
return returnData;
|
||||
}
|
||||
}
|
||||
|
||||
export async function execute(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const newItems = items.map(({ json }, i) => ({ ...json, _itemIndex: i }));
|
||||
|
||||
const options = this.getNodeParameter('options', 0, {}) as SummarizeOptions;
|
||||
|
||||
const fieldsToSplitBy = (this.getNodeParameter('fieldsToSplitBy', 0, '') as string)
|
||||
.split(',')
|
||||
.map((field) => field.trim())
|
||||
.filter((field) => field);
|
||||
|
||||
const fieldsToSummarize = this.getNodeParameter(
|
||||
'fieldsToSummarize.values',
|
||||
0,
|
||||
[],
|
||||
) as Aggregations;
|
||||
|
||||
if (fieldsToSummarize.filter((aggregation) => aggregation.field !== '').length === 0) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
"You need to add at least one aggregation to 'Fields to Summarize' with non empty 'Field'",
|
||||
);
|
||||
}
|
||||
|
||||
const getValue = fieldValueGetter(options.disableDotNotation);
|
||||
|
||||
const nodeVersion = this.getNode().typeVersion;
|
||||
|
||||
if (nodeVersion < 2.1) {
|
||||
checkIfFieldExists.call(this, newItems, fieldsToSummarize, getValue);
|
||||
}
|
||||
|
||||
const aggregationResult = splitData(
|
||||
fieldsToSplitBy,
|
||||
newItems,
|
||||
fieldsToSummarize,
|
||||
options,
|
||||
getValue,
|
||||
);
|
||||
|
||||
if (options.outputFormat === 'singleItem') {
|
||||
const executionData: INodeExecutionData = {
|
||||
json: aggregationResult,
|
||||
pairedItem: newItems.map((_v, index) => ({
|
||||
item: index,
|
||||
})),
|
||||
};
|
||||
return [executionData];
|
||||
} else {
|
||||
if (!fieldsToSplitBy.length) {
|
||||
const { pairedItems, ...json } = aggregationResult;
|
||||
const executionData: INodeExecutionData = {
|
||||
json,
|
||||
pairedItem: ((pairedItems as number[]) || []).map((index: number) => ({
|
||||
item: index,
|
||||
})),
|
||||
};
|
||||
return [executionData];
|
||||
}
|
||||
const returnData = aggregationToArray(aggregationResult, fieldsToSplitBy);
|
||||
const executionData = returnData.map((item) => {
|
||||
const { pairedItems, ...json } = item;
|
||||
return {
|
||||
json,
|
||||
pairedItem: ((pairedItems as number[]) || []).map((index: number) => ({
|
||||
item: index,
|
||||
})),
|
||||
};
|
||||
});
|
||||
return executionData;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { AllEntities } from 'n8n-workflow';
|
||||
|
||||
type NodeMap = {
|
||||
itemList:
|
||||
| 'concatenateItems'
|
||||
| 'limit'
|
||||
| 'removeDuplicates'
|
||||
| 'sort'
|
||||
| 'splitOutItems'
|
||||
| 'summarize';
|
||||
};
|
||||
|
||||
export type ItemListsType = AllEntities<NodeMap>;
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { IExecuteFunctions, INodeExecutionData } from 'n8n-workflow';
|
||||
import { NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import * as itemList from './itemList';
|
||||
import type { ItemListsType } from './node.type';
|
||||
|
||||
export async function router(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
|
||||
let returnData: INodeExecutionData[] = [];
|
||||
|
||||
const items = this.getInputData();
|
||||
const resource = this.getNodeParameter<ItemListsType>('resource', 0);
|
||||
const operation = this.getNodeParameter('operation', 0);
|
||||
|
||||
const itemListsNodeData = {
|
||||
resource,
|
||||
operation,
|
||||
} as ItemListsType;
|
||||
|
||||
switch (itemListsNodeData.resource) {
|
||||
case 'itemList':
|
||||
returnData = await itemList[itemListsNodeData.operation].execute.call(this, items);
|
||||
break;
|
||||
default:
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
`The operation "${operation}" is not supported!`,
|
||||
);
|
||||
}
|
||||
|
||||
return [returnData];
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/* eslint-disable n8n-nodes-base/node-filename-against-convention */
|
||||
import { NodeConnectionTypes, type INodeTypeDescription } from 'n8n-workflow';
|
||||
|
||||
import * as itemList from './itemList';
|
||||
|
||||
export const versionDescription: INodeTypeDescription = {
|
||||
displayName: 'Item Lists',
|
||||
name: 'itemLists',
|
||||
icon: 'file:itemLists.svg',
|
||||
group: ['input'],
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Helper for working with lists of items and transforming arrays',
|
||||
version: [3, 3.1],
|
||||
defaults: {
|
||||
name: 'Item Lists',
|
||||
},
|
||||
inputs: [NodeConnectionTypes.Main],
|
||||
outputs: [NodeConnectionTypes.Main],
|
||||
credentials: [],
|
||||
properties: [
|
||||
{
|
||||
displayName: 'Resource',
|
||||
name: 'resource',
|
||||
type: 'hidden',
|
||||
options: [
|
||||
{
|
||||
name: 'Item List',
|
||||
value: 'itemList',
|
||||
},
|
||||
],
|
||||
default: 'itemList',
|
||||
},
|
||||
...itemList.description,
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,126 @@
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
IBinaryData,
|
||||
INodeExecutionData,
|
||||
GenericValue,
|
||||
} from 'n8n-workflow';
|
||||
import { ApplicationError, NodeOperationError } from 'n8n-workflow';
|
||||
|
||||
import { JsTaskRunnerSandbox } from '../../../Code/JsTaskRunnerSandbox';
|
||||
|
||||
export const prepareFieldsArray = (fields: string | string[], fieldName = 'Fields') => {
|
||||
if (typeof fields === 'string') {
|
||||
return fields
|
||||
.split(',')
|
||||
.map((entry) => entry.trim())
|
||||
.filter((entry) => entry !== '');
|
||||
}
|
||||
if (Array.isArray(fields)) {
|
||||
return fields;
|
||||
}
|
||||
throw new ApplicationError(
|
||||
`The \'${fieldName}\' parameter must be a string of fields separated by commas or an array of strings.`,
|
||||
{ level: 'warning' },
|
||||
);
|
||||
};
|
||||
|
||||
const returnRegExp = /\breturn\b/g;
|
||||
|
||||
export async function sortByCode(
|
||||
this: IExecuteFunctions,
|
||||
items: INodeExecutionData[],
|
||||
): Promise<INodeExecutionData[]> {
|
||||
const userCode = this.getNodeParameter('code', 0) as string;
|
||||
if (!returnRegExp.test(userCode)) {
|
||||
throw new NodeOperationError(
|
||||
this.getNode(),
|
||||
"Sort code doesn't return. Please add a 'return' statement to your code",
|
||||
);
|
||||
}
|
||||
|
||||
const mode = this.getMode();
|
||||
const chunkSize = undefined;
|
||||
const sortCode = `return items.sort((a, b) => { ${userCode} })`;
|
||||
const sandbox = new JsTaskRunnerSandbox(mode, this, chunkSize, { items });
|
||||
const executionResult = await sandbox.runCode<INodeExecutionData[]>(sortCode);
|
||||
|
||||
return executionResult;
|
||||
}
|
||||
|
||||
type PartialBinaryData = Omit<IBinaryData, 'data'>;
|
||||
const isBinaryUniqueSetup = () => {
|
||||
const binaries: PartialBinaryData[] = [];
|
||||
return (binary: IBinaryData) => {
|
||||
for (const existingBinary of binaries) {
|
||||
if (
|
||||
existingBinary.mimeType === binary.mimeType &&
|
||||
existingBinary.fileType === binary.fileType &&
|
||||
existingBinary.fileSize === binary.fileSize &&
|
||||
existingBinary.fileExtension === binary.fileExtension
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
binaries.push({
|
||||
mimeType: binary.mimeType,
|
||||
fileType: binary.fileType,
|
||||
fileSize: binary.fileSize,
|
||||
fileExtension: binary.fileExtension,
|
||||
});
|
||||
|
||||
return true;
|
||||
};
|
||||
};
|
||||
|
||||
export function addBinariesToItem(
|
||||
newItem: INodeExecutionData,
|
||||
items: INodeExecutionData[],
|
||||
uniqueOnly?: boolean,
|
||||
) {
|
||||
const isBinaryUnique = uniqueOnly ? isBinaryUniqueSetup() : undefined;
|
||||
|
||||
for (const item of items) {
|
||||
if (item.binary === undefined) continue;
|
||||
|
||||
for (const key of Object.keys(item.binary)) {
|
||||
if (!newItem.binary) newItem.binary = {};
|
||||
let binaryKey = key;
|
||||
const binary = item.binary[key];
|
||||
|
||||
if (isBinaryUnique && !isBinaryUnique(binary)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the binary key already exists add a suffix to it
|
||||
let i = 1;
|
||||
while (newItem.binary[binaryKey] !== undefined) {
|
||||
binaryKey = `${key}_${i}`;
|
||||
i++;
|
||||
}
|
||||
|
||||
newItem.binary[binaryKey] = binary;
|
||||
}
|
||||
}
|
||||
|
||||
return newItem;
|
||||
}
|
||||
|
||||
export function typeToNumber(value: GenericValue): number {
|
||||
if (typeof value === 'object') {
|
||||
if (Array.isArray(value)) return 9;
|
||||
if (value === null) return 10;
|
||||
if (value instanceof Date) return 11;
|
||||
}
|
||||
const types = {
|
||||
_string: 1,
|
||||
_number: 2,
|
||||
_bigint: 3,
|
||||
_boolean: 4,
|
||||
_symbol: 5,
|
||||
_undefined: 6,
|
||||
_object: 7,
|
||||
_function: 8,
|
||||
};
|
||||
return types[`_${typeof value}`];
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"row_number": {
|
||||
"type": "integer"
|
||||
}
|
||||
},
|
||||
"version": 3
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"address": {
|
||||
"type": "string"
|
||||
},
|
||||
"data_cid": {
|
||||
"type": "string"
|
||||
},
|
||||
"data_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"gps_coordinates": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"latitude": {
|
||||
"type": "number"
|
||||
},
|
||||
"longitude": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
},
|
||||
"photos_link": {
|
||||
"type": "string"
|
||||
},
|
||||
"place_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"place_id_search": {
|
||||
"type": "string"
|
||||
},
|
||||
"position": {
|
||||
"type": "integer"
|
||||
},
|
||||
"provider_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"reviews": {
|
||||
"type": "integer"
|
||||
},
|
||||
"reviews_link": {
|
||||
"type": "string"
|
||||
},
|
||||
"thumbnail": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
},
|
||||
"type_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"type_ids": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"types": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"unclaimed_listing": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"website": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"categories": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
},
|
||||
"content:encoded": {
|
||||
"type": "string"
|
||||
},
|
||||
"content:encodedSnippet": {
|
||||
"type": "string"
|
||||
},
|
||||
"contentSnippet": {
|
||||
"type": "string"
|
||||
},
|
||||
"creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"dc:creator": {
|
||||
"type": "string"
|
||||
},
|
||||
"guid": {
|
||||
"type": "string"
|
||||
},
|
||||
"isoDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"link": {
|
||||
"type": "string"
|
||||
},
|
||||
"pubDate": {
|
||||
"type": "string"
|
||||
},
|
||||
"title": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"appended_message_content": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"content": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="60" height="60" viewBox="0 0 210 210"><path d="M18.8 18.8C8.4 18.8 0 27.1 0 37.5s8.4 18.8 18.8 18.8 18.8-8.4 18.8-18.8-8.5-18.7-18.8-18.7m0 62.4C8.4 81.2 0 89.6 0 100s8.4 18.8 18.8 18.8 18.8-8.4 18.8-18.8-8.5-18.8-18.8-18.8m0 62.6C8.4 143.8 0 152.1 0 162.5s8.4 18.8 18.8 18.8 18.8-8.4 18.8-18.8-8.5-18.7-18.8-18.7m175 6.2h-125c-3.5 0-6.2 2.8-6.2 6.2v12.5c0 3.5 2.8 6.2 6.2 6.2h125c3.5 0 6.2-2.8 6.2-6.2v-12.5c0-3.4-2.8-6.2-6.2-6.2m0-125h-125c-3.5 0-6.2 2.8-6.2 6.2v12.5c0 3.5 2.8 6.2 6.2 6.2h125c3.5 0 6.2-2.8 6.2-6.2V31.2c0-3.4-2.8-6.2-6.2-6.2m0 62.5h-125c-3.5 0-6.2 2.8-6.2 6.2v12.5c0 3.5 2.8 6.2 6.2 6.2h125c3.5 0 6.2-2.8 6.2-6.2V93.8c0-3.5-2.8-6.3-6.2-6.3" style="fill:#ff6d5a"/></svg>
|
||||
|
After Width: | Height: | Size: 757 B |
@@ -0,0 +1,5 @@
|
||||
import { NodeTestHarness } from '@nodes-testing/node-test-harness';
|
||||
|
||||
describe('Test ItemLists Node', () => {
|
||||
new NodeTestHarness().setupTests();
|
||||
});
|
||||
@@ -0,0 +1,244 @@
|
||||
{
|
||||
"name": "itemLists test",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "6c90bf81-0c0e-4c5f-9f0c-297f06d9668a",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-440, 260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"char": "a"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"char": "b"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"char": "c"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"char": "d"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"char": "e"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "2e0011d5-c6a0-4a40-ab8c-9d011cde40d5",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [-180, 260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "aggregateItems",
|
||||
"fieldsToAggregate": {
|
||||
"fieldToAggregate": [
|
||||
{
|
||||
"fieldToAggregate": "id",
|
||||
"renameField": true,
|
||||
"outputFieldName": "data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "d95ca3a3-fb43-4037-846e-b87103dec1a3",
|
||||
"name": "fields aggregate and rename",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 0]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "aggregateItems",
|
||||
"aggregate": "aggregateAllItemData"
|
||||
},
|
||||
"id": "4c1bc7be-7611-418d-aad5-8642b1cc0781",
|
||||
"name": "aggregate all fields into list",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 320]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "aggregateItems",
|
||||
"aggregate": "aggregateAllItemData",
|
||||
"include": "specifiedFields",
|
||||
"fieldsToInclude": {
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "id"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "951de23c-2018-437b-961e-8ae7d7fd1a82",
|
||||
"name": "aggregate selected fields into list",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 500]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "aggregateItems",
|
||||
"aggregate": "aggregateAllItemData",
|
||||
"destinationFieldName": "output",
|
||||
"include": "allFieldsExcept",
|
||||
"fieldsToExclude": {
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "char"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "b62c02ee-5edb-473d-a755-7fb8700641fa",
|
||||
"name": "aggregate all fields except selected into list",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 700]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"fields aggregate and rename": [
|
||||
{
|
||||
"json": {
|
||||
"data": [1, 2, 3, 4, 5]
|
||||
}
|
||||
}
|
||||
],
|
||||
"aggregate all fields into list": [
|
||||
{
|
||||
"json": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1,
|
||||
"char": "a"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"char": "b"
|
||||
},
|
||||
{
|
||||
"id": 3,
|
||||
"char": "c"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"char": "d"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"char": "e"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"aggregate selected fields into list": [
|
||||
{
|
||||
"json": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"id": 3
|
||||
},
|
||||
{
|
||||
"id": 4
|
||||
},
|
||||
{
|
||||
"id": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"aggregate all fields except selected into list": [
|
||||
{
|
||||
"json": {
|
||||
"output": [
|
||||
{
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"id": 3
|
||||
},
|
||||
{
|
||||
"id": 4
|
||||
},
|
||||
{
|
||||
"id": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "fields aggregate and rename",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "aggregate all fields into list",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "aggregate selected fields into list",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "aggregate all fields except selected into list",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "9bf7c52b-b118-4dad-bfef-7db41828393b",
|
||||
"id": "105",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
{
|
||||
"name": "itemsList concatenate paired fix",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "37256a71-67fe-4643-b4c7-e670096b68fc",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
620,
|
||||
540
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"category": "randomData",
|
||||
"randomDataSeed": "n8n",
|
||||
"randomDataCount": 3
|
||||
},
|
||||
"id": "0518c1d3-2e6d-40c6-8225-d89ec28ed28d",
|
||||
"name": "DebugHelper",
|
||||
"type": "n8n-nodes-base.debugHelper",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1000,
|
||||
540
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "concatenateItems",
|
||||
"aggregate": "aggregateAllItemData"
|
||||
},
|
||||
"id": "7866056b-a7c1-41e2-b3b7-301cb21d95d4",
|
||||
"name": "Item Lists",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
1200,
|
||||
540
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fields": {
|
||||
"values": [
|
||||
{
|
||||
"name": "foo",
|
||||
"stringValue": "bar"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "191ec112-65b6-4e4a-bf11-b63ab7e96f68",
|
||||
"name": "Edit Fields",
|
||||
"type": "n8n-nodes-base.set",
|
||||
"typeVersion": 3,
|
||||
"position": [
|
||||
800,
|
||||
540
|
||||
]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"customerId": "1",
|
||||
"message": "={{ $('Edit Fields').item.json.foo }}"
|
||||
},
|
||||
"id": "4a3d6844-5044-4570-912a-af3f32efa871",
|
||||
"name": "Customer Messenger (n8n training)",
|
||||
"type": "n8n-nodes-base.n8nTrainingCustomerMessenger",
|
||||
"typeVersion": 1,
|
||||
"position": [
|
||||
1400,
|
||||
540
|
||||
]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Customer Messenger (n8n training)": [
|
||||
{
|
||||
"json": {
|
||||
"output": "Sent message to customer 1: bar"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Edit Fields",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"DebugHelper": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Customer Messenger (n8n training)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Edit Fields": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "DebugHelper",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "d17719c9-c625-4419-8b3c-d4cfaeffc312",
|
||||
"id": "vXdwDVBSRxZxMrcv",
|
||||
"meta": {
|
||||
"instanceId": "b888bd11cd1ddbb95450babf3e199556799d999b896f650de768b8370ee50363"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
{
|
||||
"name": "itemLists test",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "bd7af0bb-de39-44b4-ac11-eb1d22f5e8d7",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [260, 180]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"entry": 1
|
||||
},
|
||||
{
|
||||
"entry": 2
|
||||
},
|
||||
{
|
||||
"entry": 3
|
||||
},
|
||||
{
|
||||
"entry": 4
|
||||
},
|
||||
{
|
||||
"entry": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "21185d7a-f0c1-49a0-9c2d-f0f198ceea7e",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [520, 180]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "limit"
|
||||
},
|
||||
"id": "7cc02cc4-1f5f-489a-81e2-4c96b3bdf221",
|
||||
"name": "Item Lists limit first",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [740, 80]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "limit",
|
||||
"keep": "lastItems"
|
||||
},
|
||||
"id": "2bf79d53-7a0b-4716-aa09-55ad43d306ae",
|
||||
"name": "Item Lists limit last",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [740, 300]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Item Lists limit first": [
|
||||
{
|
||||
"json": {
|
||||
"entry": 1
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists limit last": [
|
||||
{
|
||||
"json": {
|
||||
"entry": 5
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists limit first",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists limit last",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "5036d554-1ba4-4b5f-ba9f-1de6df09e807",
|
||||
"id": "105",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
{
|
||||
"name": "itemLists test",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "c36aef04-b8ee-4f50-938b-ec64c4f78c97",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [860, 340]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"entry": 1,
|
||||
"data": "a",
|
||||
"char": "a"
|
||||
},
|
||||
{
|
||||
"entry": 1,
|
||||
"data": "b",
|
||||
"char": "a"
|
||||
},
|
||||
{
|
||||
"entry": 1,
|
||||
"data": "a",
|
||||
"char": "a"
|
||||
},
|
||||
{
|
||||
"entry": 4,
|
||||
"data": "d",
|
||||
"char": "a"
|
||||
},
|
||||
{
|
||||
"entry": 5,
|
||||
"data": "e",
|
||||
"char": "a"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "79f44614-aaf8-421a-9cad-2d92445a5dd5",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1120, 340]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeDuplicates",
|
||||
"compare": "allFieldsExcept",
|
||||
"fieldsToExclude": {
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "data"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "c1f69c55-caba-4d44-b209-445f4bef3756",
|
||||
"name": "Item Lists remove duplicates exclude",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [1320, 340]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeDuplicates",
|
||||
"compare": "selectedFields",
|
||||
"fieldsToCompare": {
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "char"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"removeOtherFields": true
|
||||
}
|
||||
},
|
||||
"id": "7b9f0a7c-25f0-4d1f-b30f-666ddcaf5d98",
|
||||
"name": "Item Lists remove duplicates selected",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [1340, 560]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeDuplicates"
|
||||
},
|
||||
"id": "9762644f-34cb-48ca-b8a3-e0aa0ca05d4a",
|
||||
"name": "Item Lists remove duplicates",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [1320, 120]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Item Lists remove duplicates selected": [
|
||||
{
|
||||
"json": {
|
||||
"char": "a"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists remove duplicates exclude": [
|
||||
{
|
||||
"json": {
|
||||
"entry": 1,
|
||||
"data": "a",
|
||||
"char": "a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"entry": 4,
|
||||
"data": "d",
|
||||
"char": "a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"entry": 5,
|
||||
"data": "e",
|
||||
"char": "a"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists remove duplicates": [
|
||||
{
|
||||
"json": {
|
||||
"entry": 1,
|
||||
"data": "a",
|
||||
"char": "a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"entry": 1,
|
||||
"data": "b",
|
||||
"char": "a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"entry": 4,
|
||||
"data": "d",
|
||||
"char": "a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"entry": 5,
|
||||
"data": "e",
|
||||
"char": "a"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists remove duplicates",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists remove duplicates exclude",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists remove duplicates selected",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "9b7b1482-ceaa-4878-8091-d6fbfbe58a79",
|
||||
"id": "105",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,320 @@
|
||||
{
|
||||
"name": "My workflow 63",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "5f174c07-00e5-49fc-854f-b1571d35c5a3",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [960, 520]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"mixed": "1",
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": 1,
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": true,
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": false,
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": {},
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": [],
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": "1",
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": 1,
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": true,
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": false,
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": {},
|
||||
"match": "foo"
|
||||
},
|
||||
{
|
||||
"mixed": [],
|
||||
"match": "foo"
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "2aa52759-f7a5-4a60-bf2e-9c1e3d2821dc",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [1160, 520]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeDuplicates"
|
||||
},
|
||||
"id": "90acc956-989f-4008-a3df-fe0162762b24",
|
||||
"name": "Remove duplicates",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3.1,
|
||||
"position": [1440, 160]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeDuplicates",
|
||||
"compare": "selectedFields",
|
||||
"fieldsToCompare": "mixed",
|
||||
"options": {}
|
||||
},
|
||||
"id": "6c39a6bb-b042-4d4c-828c-52699f178828",
|
||||
"name": "Remove duplicates by mixed",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3.1,
|
||||
"position": [1440, 340]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeDuplicates",
|
||||
"compare": "selectedFields",
|
||||
"fieldsToCompare": "match",
|
||||
"options": {}
|
||||
},
|
||||
"id": "d3783be2-6705-44d3-b481-72e7a9dba458",
|
||||
"name": "Remove duplicates by match",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3.1,
|
||||
"position": [1440, 520]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeDuplicates",
|
||||
"compare": "allFieldsExcept",
|
||||
"fieldsToExclude": "mixed",
|
||||
"options": {}
|
||||
},
|
||||
"id": "e634e39d-0ce6-477f-97cc-69eec2dd981b",
|
||||
"name": "Remove duplicates except by mixed",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3.1,
|
||||
"position": [1440, 720]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeDuplicates",
|
||||
"compare": "allFieldsExcept",
|
||||
"fieldsToExclude": "match",
|
||||
"options": {}
|
||||
},
|
||||
"id": "0a1bbc9a-3505-413c-ae86-c60dc60b5909",
|
||||
"name": "Remove duplicates except by match",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3.1,
|
||||
"position": [1440, 940]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Remove duplicates": [
|
||||
{
|
||||
"json": {
|
||||
"mixed": "1",
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": 1,
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": true,
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": false,
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": {},
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": [],
|
||||
"match": "foo"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Remove duplicates by mixed": [
|
||||
{
|
||||
"json": {
|
||||
"mixed": "1",
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": 1,
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": true,
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": false,
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": {},
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": [],
|
||||
"match": "foo"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Remove duplicates by match": [
|
||||
{
|
||||
"json": {
|
||||
"mixed": "1",
|
||||
"match": "foo"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Remove duplicates except by mixed": [
|
||||
{
|
||||
"json": {
|
||||
"mixed": "1",
|
||||
"match": "foo"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Remove duplicates except by match": [
|
||||
{
|
||||
"json": {
|
||||
"mixed": "1",
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": 1,
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": true,
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": false,
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": {},
|
||||
"match": "foo"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"mixed": [],
|
||||
"match": "foo"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Remove duplicates",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Remove duplicates by mixed",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Remove duplicates by match",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Remove duplicates except by mixed",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Remove duplicates except by match",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {
|
||||
"executionOrder": "v1"
|
||||
},
|
||||
"versionId": "c657bc6f-02bc-4e1b-b49a-d1dca8b13256",
|
||||
"id": "cHSnZsTtYIJj3gL2",
|
||||
"meta": {
|
||||
"instanceId": "104a4d08d8897b8bdeb38aaca515021075e0bd8544c983c2bb8c86e6a8e6081c"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,393 @@
|
||||
{
|
||||
"name": "itemLists test",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "6c90bf81-0c0e-4c5f-9f0c-297f06d9668a",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-400, 400]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": {
|
||||
"data": [
|
||||
{
|
||||
"id": 3,
|
||||
"char": "c"
|
||||
},
|
||||
{
|
||||
"id": 4,
|
||||
"char": "d"
|
||||
},
|
||||
{
|
||||
"id": 5,
|
||||
"char": "e"
|
||||
},
|
||||
{
|
||||
"id": 1,
|
||||
"char": "a"
|
||||
},
|
||||
{
|
||||
"id": 2,
|
||||
"char": "b"
|
||||
}
|
||||
],
|
||||
"data2": [
|
||||
{
|
||||
"text": "foo"
|
||||
}
|
||||
],
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "2e0011d5-c6a0-4a40-ab8c-9d011cde40d5",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [-180, 400]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data",
|
||||
"options": {}
|
||||
},
|
||||
"id": "e7eac465-8fe6-498c-9942-ebd47df537c1",
|
||||
"name": "Item Lists",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 160]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data",
|
||||
"include": "allOtherFields",
|
||||
"options": {}
|
||||
},
|
||||
"id": "09b7fe15-dbad-4ca6-bf1e-3093139d14e5",
|
||||
"name": "Item Lists1",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 320]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data",
|
||||
"include": "selectedOtherFields",
|
||||
"fieldsToInclude": {
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "data3"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "7ea63dc7-8141-4233-af47-9894919c7fe4",
|
||||
"name": "Item Lists2",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 480]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data",
|
||||
"options": {
|
||||
"destinationFieldName": "output"
|
||||
}
|
||||
},
|
||||
"id": "89c3c1b4-9577-480a-931f-3b34450b23cb",
|
||||
"name": "Item Lists3",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [80, 660]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Item Lists": [
|
||||
{
|
||||
"json": {
|
||||
"id": 3,
|
||||
"char": "c"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4,
|
||||
"char": "d"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 5,
|
||||
"char": "e"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 1,
|
||||
"char": "a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2,
|
||||
"char": "b"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists1": [
|
||||
{
|
||||
"json": {
|
||||
"data2": [
|
||||
{
|
||||
"text": "foo"
|
||||
}
|
||||
],
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 3,
|
||||
"char": "c"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data2": [
|
||||
{
|
||||
"text": "foo"
|
||||
}
|
||||
],
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 4,
|
||||
"char": "d"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data2": [
|
||||
{
|
||||
"text": "foo"
|
||||
}
|
||||
],
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 5,
|
||||
"char": "e"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data2": [
|
||||
{
|
||||
"text": "foo"
|
||||
}
|
||||
],
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 1,
|
||||
"char": "a"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data2": [
|
||||
{
|
||||
"text": "foo"
|
||||
}
|
||||
],
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 2,
|
||||
"char": "b"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists2": [
|
||||
{
|
||||
"json": {
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 3,
|
||||
"char": "c"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 4,
|
||||
"char": "d"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 5,
|
||||
"char": "e"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 1,
|
||||
"char": "a"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data3": [
|
||||
{
|
||||
"text": "bar"
|
||||
}
|
||||
],
|
||||
"data": {
|
||||
"id": 2,
|
||||
"char": "b"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists3": [
|
||||
{
|
||||
"json": {
|
||||
"output": {
|
||||
"id": 3,
|
||||
"char": "c"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"output": {
|
||||
"id": 4,
|
||||
"char": "d"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"output": {
|
||||
"id": 5,
|
||||
"char": "e"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"output": {
|
||||
"id": 1,
|
||||
"char": "a"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"output": {
|
||||
"id": 2,
|
||||
"char": "b"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "9230f580-6f41-47c9-9949-bf258fc3fa47",
|
||||
"id": "105",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
+414
@@ -0,0 +1,414 @@
|
||||
{
|
||||
"name": "itemList split Object",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "ade46a75-ab57-48c6-886b-0c118f5ef1c6",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [520, 800]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data",
|
||||
"include": "selectedOtherFields",
|
||||
"fieldsToInclude": {
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "tag"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "45e1d7a3-d6e8-4b69-a68a-1038db13be4c",
|
||||
"name": "Item Lists1",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [1120, 340]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": {
|
||||
"data": {
|
||||
"entry1": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"entry2": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"entry3": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
},
|
||||
"data2": ["a", "b", "c"],
|
||||
"data3": {
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3
|
||||
},
|
||||
"data4": null,
|
||||
"tag": "bar"
|
||||
}
|
||||
},
|
||||
"id": "faa78fac-468d-42b8-96e9-0fb62c312da3",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [760, 800]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "5baaf321-7e89-473d-a313-7cb90b3f13b3",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1380, 340]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data3",
|
||||
"include": "allOtherFields",
|
||||
"options": {
|
||||
"destinationFieldName": "extracted"
|
||||
}
|
||||
},
|
||||
"id": "a786bea9-eb29-4c6d-aea6-a22aee622bc6",
|
||||
"name": "Item Lists",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [1120, 720]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "0521a24b-c74a-48fa-ae50-48a242b97806",
|
||||
"name": "No Operation, do nothing1",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1380, 720]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data3",
|
||||
"options": {}
|
||||
},
|
||||
"id": "0c1c8827-72ab-4738-918c-d529e66505c6",
|
||||
"name": "Item Lists2",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [1120, 540]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "4c0dca36-c2ae-4d40-8952-0e728ac93fa3",
|
||||
"name": "No Operation, do nothing2",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1380, 540]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data2",
|
||||
"options": {}
|
||||
},
|
||||
"id": "b2031380-b2a8-426d-8f7a-ab072d23b979",
|
||||
"name": "Item Lists3",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [1120, 920]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "617f7259-beee-42f1-bba2-4e75a83fe369",
|
||||
"name": "No Operation, do nothing3",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1380, 920]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data4",
|
||||
"options": {}
|
||||
},
|
||||
"id": "8909b8eb-e5a9-4436-8e62-09d8c9670ac1",
|
||||
"name": "Item Lists4",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [1120, 1140],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "a9278f90-8ad9-42dc-85b6-28bf1b6764b7",
|
||||
"name": "No Operation, do nothing4",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1380, 1140]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"tag": "bar",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"tag": "bar",
|
||||
"data": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"tag": "bar",
|
||||
"data": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing2": [
|
||||
{
|
||||
"json": {
|
||||
"data3": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data3": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data3": 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing1": [
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"entry1": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"entry2": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"entry3": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
},
|
||||
"data2": ["a", "b", "c"],
|
||||
"data4": null,
|
||||
"tag": "bar",
|
||||
"extracted": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"entry1": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"entry2": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"entry3": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
},
|
||||
"data2": ["a", "b", "c"],
|
||||
"data4": null,
|
||||
"tag": "bar",
|
||||
"extracted": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"entry1": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"entry2": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"entry3": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
},
|
||||
"data2": ["a", "b", "c"],
|
||||
"data4": null,
|
||||
"tag": "bar",
|
||||
"extracted": 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing3": [
|
||||
{
|
||||
"json": {
|
||||
"data2": "a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data2": "b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data2": "c"
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing4": [
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"entry1": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"entry2": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"entry3": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
},
|
||||
"data2": ["a", "b", "c"],
|
||||
"data3": {
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3
|
||||
},
|
||||
"data4": null,
|
||||
"tag": "bar"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists3": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists4": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false
|
||||
}
|
||||
@@ -0,0 +1,342 @@
|
||||
{
|
||||
"name": "itemLists test",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "6c90bf81-0c0e-4c5f-9f0c-297f06d9668a",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-400, 420]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"category": "red",
|
||||
"text": "foo",
|
||||
"char": "a",
|
||||
"value": 1
|
||||
},
|
||||
{
|
||||
"category": "blue",
|
||||
"text": "spam",
|
||||
"char": "b",
|
||||
"value": 2
|
||||
},
|
||||
{
|
||||
"category": "green",
|
||||
"text": "bar",
|
||||
"char": "c",
|
||||
"value": 3
|
||||
},
|
||||
{
|
||||
"category": "red",
|
||||
"text": "foo",
|
||||
"char": "a",
|
||||
"value": 4
|
||||
},
|
||||
{
|
||||
"category": "red",
|
||||
"text": "bar",
|
||||
"char": "a",
|
||||
"value": 5
|
||||
},
|
||||
{
|
||||
"category": "blue",
|
||||
"text": "foo",
|
||||
"char": "a",
|
||||
"value": 6
|
||||
},
|
||||
{
|
||||
"category": "blue",
|
||||
"text": "foo",
|
||||
"char": "a",
|
||||
"value": 7
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "2e0011d5-c6a0-4a40-ab8c-9d011cde40d5",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [-180, 420]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "summarize",
|
||||
"fieldsToSummarize": {
|
||||
"values": [
|
||||
{
|
||||
"aggregation": "append",
|
||||
"field": "char"
|
||||
},
|
||||
{
|
||||
"field": "char"
|
||||
},
|
||||
{
|
||||
"aggregation": "countUnique",
|
||||
"field": "char"
|
||||
},
|
||||
{
|
||||
"aggregation": "concatenate",
|
||||
"field": "char",
|
||||
"separateBy": ", "
|
||||
}
|
||||
]
|
||||
},
|
||||
"fieldsToSplitBy": "category, text",
|
||||
"options": {}
|
||||
},
|
||||
"id": "1dedf668-b766-4283-9efd-90db28404f0b",
|
||||
"name": "Item Lists",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [40, 220]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "summarize",
|
||||
"fieldsToSummarize": {
|
||||
"values": [
|
||||
{
|
||||
"aggregation": "append",
|
||||
"field": "char"
|
||||
},
|
||||
{
|
||||
"field": "char"
|
||||
},
|
||||
{
|
||||
"aggregation": "countUnique",
|
||||
"field": "char"
|
||||
},
|
||||
{
|
||||
"aggregation": "concatenate",
|
||||
"field": "char",
|
||||
"separateBy": ", "
|
||||
}
|
||||
]
|
||||
},
|
||||
"fieldsToSplitBy": "category, text",
|
||||
"options": {
|
||||
"outputFormat": "singleItem"
|
||||
}
|
||||
},
|
||||
"id": "8fd0f819-226c-4b29-87c7-b724dd72605c",
|
||||
"name": "Item Lists1",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [40, 420]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "summarize",
|
||||
"fieldsToSummarize": {
|
||||
"values": [
|
||||
{
|
||||
"aggregation": "average",
|
||||
"field": "value"
|
||||
},
|
||||
{
|
||||
"aggregation": "max",
|
||||
"field": "value"
|
||||
},
|
||||
{
|
||||
"aggregation": "min",
|
||||
"field": "value"
|
||||
},
|
||||
{
|
||||
"aggregation": "max",
|
||||
"field": "value"
|
||||
},
|
||||
{
|
||||
"aggregation": "sum",
|
||||
"field": "value"
|
||||
},
|
||||
{
|
||||
"aggregation": "append",
|
||||
"field": "value"
|
||||
}
|
||||
]
|
||||
},
|
||||
"fieldsToSplitBy": "category",
|
||||
"options": {}
|
||||
},
|
||||
"id": "33e0367d-42d9-4f82-8fc8-8e2019aa3734",
|
||||
"name": "Item Lists2",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 1,
|
||||
"position": [40, 620]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Item Lists": [
|
||||
{
|
||||
"json": {
|
||||
"category": "red",
|
||||
"text": "foo",
|
||||
"appended_char": ["a", "a"],
|
||||
"count_char": 2,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "a, a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"category": "red",
|
||||
"text": "bar",
|
||||
"appended_char": ["a"],
|
||||
"count_char": 1,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"category": "blue",
|
||||
"text": "spam",
|
||||
"appended_char": ["b"],
|
||||
"count_char": 1,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "b"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"category": "blue",
|
||||
"text": "foo",
|
||||
"appended_char": ["a", "a"],
|
||||
"count_char": 2,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "a, a"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"category": "green",
|
||||
"text": "bar",
|
||||
"appended_char": ["c"],
|
||||
"count_char": 1,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "c"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists1": [
|
||||
{
|
||||
"json": {
|
||||
"red": {
|
||||
"foo": {
|
||||
"appended_char": ["a", "a"],
|
||||
"count_char": 2,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "a, a"
|
||||
},
|
||||
"bar": {
|
||||
"appended_char": ["a"],
|
||||
"count_char": 1,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "a"
|
||||
}
|
||||
},
|
||||
"blue": {
|
||||
"spam": {
|
||||
"appended_char": ["b"],
|
||||
"count_char": 1,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "b"
|
||||
},
|
||||
"foo": {
|
||||
"appended_char": ["a", "a"],
|
||||
"count_char": 2,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "a, a"
|
||||
}
|
||||
},
|
||||
"green": {
|
||||
"bar": {
|
||||
"appended_char": ["c"],
|
||||
"count_char": 1,
|
||||
"unique_count_char": 1,
|
||||
"concatenated_char": "c"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists2": [
|
||||
{
|
||||
"json": {
|
||||
"category": "red",
|
||||
"average_value": 3.3333333333333335,
|
||||
"max_value": 5,
|
||||
"min_value": 1,
|
||||
"sum_value": 10,
|
||||
"appended_value": [1, 4, 5]
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"category": "blue",
|
||||
"average_value": 5,
|
||||
"max_value": 7,
|
||||
"min_value": 2,
|
||||
"sum_value": 15,
|
||||
"appended_value": [2, 6, 7]
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"category": "green",
|
||||
"average_value": 3,
|
||||
"max_value": 3,
|
||||
"min_value": 3,
|
||||
"sum_value": 3,
|
||||
"appended_value": [3]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "bee0d911-844d-4fe6-bd52-a1716dd74dd8",
|
||||
"id": "105",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,723 @@
|
||||
{
|
||||
"name": "My workflow 4",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "037f477b-6775-47e9-b735-71c1d984ceb6",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [440, 1300]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "dataa",
|
||||
"options": {}
|
||||
},
|
||||
"id": "b9f156e1-ffb0-4121-abf3-8813b8cc738e",
|
||||
"name": "Item Lists4",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [920, 260],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "dataa",
|
||||
"options": {}
|
||||
},
|
||||
"id": "c8ed5ebc-18f2-4d94-8f19-c019278e5d0d",
|
||||
"name": "Item Lists5",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.1,
|
||||
"position": [920, 420],
|
||||
"alwaysOutputData": true
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e2c1fc7c-3333-4849-a084-dac2c6edc1a7",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 240]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "c8c9727d-232d-4ae2-a7cc-3dcf00b32474",
|
||||
"name": "No Operation, do nothing1",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 420]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "aggregateItems",
|
||||
"fieldsToAggregate": {
|
||||
"fieldToAggregate": [
|
||||
{
|
||||
"fieldToAggregate": "idd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "88c02d0a-0680-4564-8b2e-48ebfabe4864",
|
||||
"name": "Item Lists",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [920, 1080],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "aggregateItems",
|
||||
"fieldsToAggregate": {
|
||||
"fieldToAggregate": [
|
||||
{
|
||||
"fieldToAggregate": "idd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "327ace17-571f-4aea-98bf-3cad71449f56",
|
||||
"name": "Item Lists6",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.1,
|
||||
"position": [920, 1240]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "38a5b83a-0f71-4788-983b-9f7719d59190",
|
||||
"name": "No Operation, do nothing2",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 1060]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "aa76c9fb-5a15-4239-903f-570aeca80453",
|
||||
"name": "No Operation, do nothing3",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 1240]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "summarize",
|
||||
"fieldsToSummarize": {
|
||||
"values": [
|
||||
{
|
||||
"field": "idd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "246e42b2-62ab-4ef4-acf1-031ac6236052",
|
||||
"name": "Item Lists7",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [920, 1880],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "summarize",
|
||||
"fieldsToSummarize": {
|
||||
"values": [
|
||||
{
|
||||
"field": "idd"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "3145a0dd-035a-477b-8d5a-98a2106b46c8",
|
||||
"name": "Item Lists8",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.1,
|
||||
"position": [920, 2040]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "71bc9c78-cf27-4628-9f64-19a9bef353c3",
|
||||
"name": "No Operation, do nothing4",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 1860]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "1ee8497c-e721-4741-b059-ff4b85cb0e73",
|
||||
"name": "No Operation, do nothing5",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 2040]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"id": 3
|
||||
},
|
||||
{
|
||||
"id": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"id": "b97e2dd3-8934-4f61-a217-e4251c3c018f",
|
||||
"name": "Code2",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [600, 500]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"id": 3
|
||||
},
|
||||
{
|
||||
"id": 4
|
||||
}
|
||||
]
|
||||
},
|
||||
"id": "cc63d7e0-0ecb-4aa8-ae15-69c6b32ce6d9",
|
||||
"name": "Code",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [660, 1260]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "summarize",
|
||||
"fieldsToSummarize": {
|
||||
"values": [
|
||||
{
|
||||
"field": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "50df7038-52d2-4b07-a729-a683ebbd769d",
|
||||
"name": "Item Lists9",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [920, 2260],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "summarize",
|
||||
"fieldsToSummarize": {
|
||||
"values": [
|
||||
{
|
||||
"field": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "15347d3e-0e4a-4c48-ad24-80730f7015c8",
|
||||
"name": "Item Lists10",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.1,
|
||||
"position": [920, 2420]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "aa2bfbc8-bdfd-41fd-83f8-181bcd2fa9be",
|
||||
"name": "No Operation, do nothing6",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 2240]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "9ca712dc-dd5a-474e-808b-224bf4149f85",
|
||||
"name": "No Operation, do nothing7",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 2420]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "aggregateItems",
|
||||
"fieldsToAggregate": {
|
||||
"fieldToAggregate": [
|
||||
{
|
||||
"fieldToAggregate": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "1cf43271-c87b-4523-9d8d-ed72b4ad49fa",
|
||||
"name": "Item Lists1",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [920, 1460],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "aggregateItems",
|
||||
"fieldsToAggregate": {
|
||||
"fieldToAggregate": [
|
||||
{
|
||||
"fieldToAggregate": "id"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "d1153bd8-a546-4ae2-a7ba-6f3ed4c41ba5",
|
||||
"name": "Item Lists11",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.1,
|
||||
"position": [920, 1620]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "32fa0e78-35ef-4a84-bc09-b94141db2bab",
|
||||
"name": "No Operation, do nothing8",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 1440]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "d3c43cd0-7544-4cb9-8fad-6e8e8c8676bc",
|
||||
"name": "No Operation, do nothing9",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 1620]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data",
|
||||
"options": {}
|
||||
},
|
||||
"id": "94c419a1-a941-472b-8142-39ca1c428390",
|
||||
"name": "Item Lists12",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2,
|
||||
"position": [920, 660],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "dataa",
|
||||
"options": {}
|
||||
},
|
||||
"id": "482d5523-a4d1-4bee-8717-c5949674a246",
|
||||
"name": "Item Lists13",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.1,
|
||||
"position": [920, 820],
|
||||
"alwaysOutputData": true
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "9dd44dc5-3525-4a22-b1dd-5d369db28759",
|
||||
"name": "No Operation, do nothing10",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 640]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "f23f7fed-e7e9-4e9d-abdd-056973fc0bbc",
|
||||
"name": "No Operation, do nothing11",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1140, 820]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing1": [
|
||||
{
|
||||
"json": {}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"data": [
|
||||
{
|
||||
"id": 1
|
||||
},
|
||||
{
|
||||
"id": 2
|
||||
},
|
||||
{
|
||||
"id": 3
|
||||
},
|
||||
{
|
||||
"id": 4
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing10": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing11": [
|
||||
{
|
||||
"json": {}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing2": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing3": [
|
||||
{
|
||||
"json": {
|
||||
"idd": []
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing8": [
|
||||
{
|
||||
"json": {
|
||||
"id": [1, 2, 3, 4]
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing9": [
|
||||
{
|
||||
"json": {
|
||||
"id": [1, 2, 3, 4]
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing4": [
|
||||
{
|
||||
"json": {
|
||||
"id": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 3
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing5": [
|
||||
{
|
||||
"json": {
|
||||
"count_idd": 0
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing6": [
|
||||
{
|
||||
"json": {
|
||||
"count_id": 4
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing7": [
|
||||
{
|
||||
"json": {
|
||||
"count_id": 4
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"Item Lists4": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists5": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Code",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists6": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists7": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists8": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists12",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists13",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists6",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists7",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists8",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists9",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists10",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists11",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists9": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing6",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists10": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing7",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing8",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists11": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing9",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists12": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing10",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists13": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing11",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "5d3c6a6b-df81-42e6-ae4c-2b297024a298",
|
||||
"id": "9",
|
||||
"meta": {
|
||||
"instanceId": "6ebec4953fe56f1c009e7c3b107578b375137523af057073c0b5da17350651bd"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
{
|
||||
"name": "item_list 2.2",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "f2d01806-a457-4a3a-8bd9-aeb005aecb99",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [-80, 820]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data, data2",
|
||||
"include": "selectedOtherFields",
|
||||
"fieldsToInclude": {
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "tag"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "b0dbb504-d111-49ee-a904-1ece920a2e7a",
|
||||
"name": "Item Lists1",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.2,
|
||||
"position": [520, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"data": {
|
||||
"data": {
|
||||
"entry1": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"entry2": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"entry3": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
},
|
||||
"data2": ["a", "b", "c"],
|
||||
"data3": {
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
"c": 3
|
||||
},
|
||||
"data4": null,
|
||||
"tag": "bar"
|
||||
}
|
||||
},
|
||||
"id": "64fa7b5c-c85c-4dd8-8863-11d6e0ee8426",
|
||||
"name": "Code1",
|
||||
"type": "n8n-nodes-testing.testData",
|
||||
"typeVersion": 1,
|
||||
"position": [160, 820]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data2, tag",
|
||||
"include": "selectedOtherFields",
|
||||
"fieldsToInclude": {
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "data4"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"destinationFieldName": "fromArray, singleField"
|
||||
}
|
||||
},
|
||||
"id": "80b46f60-19c2-4aec-a232-901b54bdb7c0",
|
||||
"name": "Item Lists",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.2,
|
||||
"position": [520, 780]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data3, data2, data",
|
||||
"options": {}
|
||||
},
|
||||
"id": "c2bbf08c-4571-4d28-a95d-82b0dd22edd9",
|
||||
"name": "Item Lists2",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.2,
|
||||
"position": [520, 560]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data2, data3",
|
||||
"include": "allOtherFields",
|
||||
"options": {}
|
||||
},
|
||||
"id": "ef10bb23-43e1-4e48-b8b7-c634e4c41b56",
|
||||
"name": "Item Lists3",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.2,
|
||||
"position": [520, 1180]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": " tag, data4",
|
||||
"options": {
|
||||
"destinationFieldName": "entry1, entry2"
|
||||
}
|
||||
},
|
||||
"id": "cd083969-5c27-47f6-9eb2-aa52fc2f5cb2",
|
||||
"name": "Item Lists4",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.2,
|
||||
"position": [520, 1400],
|
||||
"continueOnFail": true
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data2",
|
||||
"include": "selectedOtherFields",
|
||||
"fieldsToInclude": {
|
||||
"fields": [
|
||||
{
|
||||
"fieldName": "data4"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {
|
||||
"destinationFieldName": "fromArray"
|
||||
}
|
||||
},
|
||||
"id": "866154c7-2967-44d7-a278-1600752749d3",
|
||||
"name": "Item Lists5",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 2.2,
|
||||
"position": [520, 960]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"Item Lists1": [
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"data2": "a",
|
||||
"tag": "bar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"data2": "b",
|
||||
"tag": "bar"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
},
|
||||
"data2": "c",
|
||||
"tag": "bar"
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists2": [
|
||||
{
|
||||
"json": {
|
||||
"data3": 1,
|
||||
"data2": "a",
|
||||
"data": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data3": 2,
|
||||
"data2": "b",
|
||||
"data": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data3": 3,
|
||||
"data2": "c",
|
||||
"data": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists": [
|
||||
{
|
||||
"json": {
|
||||
"fromArray": "a",
|
||||
"singleField": "bar",
|
||||
"data4": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"fromArray": "b",
|
||||
"data4": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"fromArray": "c",
|
||||
"data4": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists5": [
|
||||
{
|
||||
"json": {
|
||||
"fromArray": "a",
|
||||
"data4": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"fromArray": "b",
|
||||
"data4": null
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"fromArray": "c",
|
||||
"data4": null
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists3": [
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"entry1": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"entry2": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"entry3": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
},
|
||||
"data4": null,
|
||||
"tag": "bar",
|
||||
"data2": "a",
|
||||
"data3": 1
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"entry1": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"entry2": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"entry3": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
},
|
||||
"data4": null,
|
||||
"tag": "bar",
|
||||
"data2": "b",
|
||||
"data3": 2
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"data": {
|
||||
"entry1": {
|
||||
"id": 1,
|
||||
"info": "some info 1"
|
||||
},
|
||||
"entry2": {
|
||||
"id": 2,
|
||||
"info": "some info 2"
|
||||
},
|
||||
"entry3": {
|
||||
"id": 3,
|
||||
"info": "some info 3"
|
||||
}
|
||||
},
|
||||
"data4": null,
|
||||
"tag": "bar",
|
||||
"data2": "c",
|
||||
"data3": 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"Item Lists4": [
|
||||
{
|
||||
"json": {
|
||||
"entry1": "bar",
|
||||
"entry2": null
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Code1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Code1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "1f311937-f825-4bda-a39e-e27c6ffe5906",
|
||||
"id": "170",
|
||||
"meta": {
|
||||
"instanceId": "36203ea1ce3cef713fa25999bd9874ae26b9e4c2c3a90a365f2882a154d031d0"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
@@ -0,0 +1,606 @@
|
||||
{
|
||||
"name": "itemList refactor",
|
||||
"nodes": [
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e7ecaa9c-e35d-4095-a85b-85b83f807c2a",
|
||||
"name": "When clicking \"Execute Workflow\"",
|
||||
"type": "n8n-nodes-base.manualTrigger",
|
||||
"typeVersion": 1,
|
||||
"position": [420, 400]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "getAllPeople",
|
||||
"returnAll": true
|
||||
},
|
||||
"id": "7d925077-afaa-46d5-ba2f-0c19d93afecc",
|
||||
"name": "Customer Datastore (n8n training)",
|
||||
"type": "n8n-nodes-base.n8nTrainingCustomerDatastore",
|
||||
"typeVersion": 1,
|
||||
"position": [640, 400]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "concatenateItems",
|
||||
"fieldsToAggregate": {
|
||||
"fieldToAggregate": [
|
||||
{
|
||||
"fieldToAggregate": "email"
|
||||
},
|
||||
{
|
||||
"fieldToAggregate": "notes"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "f80182d8-54f6-4a26-82b3-27d67e4ca39b",
|
||||
"name": "Item Lists1",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1120, -120]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "concatenateItems",
|
||||
"aggregate": "aggregateAllItemData",
|
||||
"destinationFieldName": "data2"
|
||||
},
|
||||
"id": "23eefe2c-6394-4b53-a791-852dcd671ea0",
|
||||
"name": "Item Lists",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1120, 40]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "limit",
|
||||
"maxItems": 2
|
||||
},
|
||||
"id": "676dc72f-9766-43c0-aab7-2f8a93ed46e5",
|
||||
"name": "Item Lists2",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1120, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "limit",
|
||||
"keep": "lastItems"
|
||||
},
|
||||
"id": "9615299b-5acc-4459-a7d3-2cb23c3224ab",
|
||||
"name": "Item Lists3",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1120, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "sort",
|
||||
"sortFieldsUi": {
|
||||
"sortField": [
|
||||
{
|
||||
"fieldName": "country"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "fd2f190a-b161-48ff-93e1-0f30efce051a",
|
||||
"name": "Item Lists4",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1120, 540]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "limit",
|
||||
"maxItems": 4
|
||||
},
|
||||
"id": "14759521-76ad-46d8-8add-1d7361788fe1",
|
||||
"name": "Item Lists5",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1360, 540]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "removeDuplicates",
|
||||
"compare": "selectedFields",
|
||||
"fieldsToCompare": "country",
|
||||
"options": {}
|
||||
},
|
||||
"id": "c0eba4d0-f975-4987-8bb2-853e8a98665e",
|
||||
"name": "Item Lists6",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1560, 540]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "concatenateItems",
|
||||
"aggregate": "aggregateAllItemData",
|
||||
"include": "specifiedFields",
|
||||
"fieldsToInclude": "country, notes, name, created"
|
||||
},
|
||||
"id": "b5962f40-a891-4b02-8fec-c2d76f85375f",
|
||||
"name": "Item Lists7",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1120, 740]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"fieldToSplitOut": "data",
|
||||
"include": "allOtherFields",
|
||||
"options": {
|
||||
"destinationFieldName": "newData"
|
||||
}
|
||||
},
|
||||
"id": "15c6fc86-7e38-4d76-836a-8f8426ca05e3",
|
||||
"name": "Item Lists8",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1380, 740]
|
||||
},
|
||||
{
|
||||
"parameters": {
|
||||
"operation": "summarize",
|
||||
"fieldsToSummarize": {
|
||||
"values": [
|
||||
{
|
||||
"aggregation": "append",
|
||||
"field": "newData.notes"
|
||||
},
|
||||
{
|
||||
"aggregation": "max",
|
||||
"field": "newData.created"
|
||||
},
|
||||
{
|
||||
"aggregation": "min",
|
||||
"field": "newData.created"
|
||||
}
|
||||
]
|
||||
},
|
||||
"options": {}
|
||||
},
|
||||
"id": "8c51ae57-487e-472a-b268-6e8ad347edbb",
|
||||
"name": "Item Lists9",
|
||||
"type": "n8n-nodes-base.itemLists",
|
||||
"typeVersion": 3,
|
||||
"position": [1560, 940]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e859b082-284c-4bb3-96b6-39a86152d8f6",
|
||||
"name": "No Operation, do nothing",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1760, -120]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "9da56c21-739d-4f2f-adf7-953cf0550d97",
|
||||
"name": "No Operation, do nothing1",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1760, 40]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "f85ac031-24de-4701-bb3d-76c684924002",
|
||||
"name": "No Operation, do nothing2",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1760, 200]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "e7faff14-55d6-4d78-983d-027fd56bcd5a",
|
||||
"name": "No Operation, do nothing3",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1760, 360]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "dc8b7bbc-b1a8-4ba8-b214-d73341cb9f85",
|
||||
"name": "No Operation, do nothing4",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1760, 740]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "09bdeca1-6a9e-4668-b9d1-14ed6139e047",
|
||||
"name": "No Operation, do nothing5",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1760, 540]
|
||||
},
|
||||
{
|
||||
"parameters": {},
|
||||
"id": "7d508889-d94e-4818-abc8-b669a0fe64ea",
|
||||
"name": "No Operation, do nothing6",
|
||||
"type": "n8n-nodes-base.noOp",
|
||||
"typeVersion": 1,
|
||||
"position": [1760, 940]
|
||||
}
|
||||
],
|
||||
"pinData": {
|
||||
"No Operation, do nothing": [
|
||||
{
|
||||
"json": {
|
||||
"email": [
|
||||
"gatsby@west-egg.com",
|
||||
"jab@macondo.co",
|
||||
"info@in-and-out-of-weeks.org",
|
||||
"captain@heartofgold.com",
|
||||
"edmund@narnia.gov"
|
||||
],
|
||||
"notes": [
|
||||
"Keeps asking about a green light??",
|
||||
"Lots of people named after him. Very confusing",
|
||||
"Keeps rolling his terrible eyes",
|
||||
"Felt like I was talking to more than one person",
|
||||
"Passionate sailor"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing1": [
|
||||
{
|
||||
"json": {
|
||||
"data2": [
|
||||
{
|
||||
"id": "23423532",
|
||||
"name": "Jay Gatsby",
|
||||
"email": "gatsby@west-egg.com",
|
||||
"notes": "Keeps asking about a green light??",
|
||||
"country": "US",
|
||||
"created": "1925-04-10"
|
||||
},
|
||||
{
|
||||
"id": "23423533",
|
||||
"name": "José Arcadio Buendía",
|
||||
"email": "jab@macondo.co",
|
||||
"notes": "Lots of people named after him. Very confusing",
|
||||
"country": "CO",
|
||||
"created": "1967-05-05"
|
||||
},
|
||||
{
|
||||
"id": "23423534",
|
||||
"name": "Max Sendak",
|
||||
"email": "info@in-and-out-of-weeks.org",
|
||||
"notes": "Keeps rolling his terrible eyes",
|
||||
"country": "US",
|
||||
"created": "1963-04-09"
|
||||
},
|
||||
{
|
||||
"id": "23423535",
|
||||
"name": "Zaphod Beeblebrox",
|
||||
"email": "captain@heartofgold.com",
|
||||
"notes": "Felt like I was talking to more than one person",
|
||||
"country": null,
|
||||
"created": "1979-10-12"
|
||||
},
|
||||
{
|
||||
"id": "23423536",
|
||||
"name": "Edmund Pevensie",
|
||||
"email": "edmund@narnia.gov",
|
||||
"notes": "Passionate sailor",
|
||||
"country": "UK",
|
||||
"created": "1950-10-16"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing2": [
|
||||
{
|
||||
"json": {
|
||||
"id": "23423532",
|
||||
"name": "Jay Gatsby",
|
||||
"email": "gatsby@west-egg.com",
|
||||
"notes": "Keeps asking about a green light??",
|
||||
"country": "US",
|
||||
"created": "1925-04-10"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "23423533",
|
||||
"name": "José Arcadio Buendía",
|
||||
"email": "jab@macondo.co",
|
||||
"notes": "Lots of people named after him. Very confusing",
|
||||
"country": "CO",
|
||||
"created": "1967-05-05"
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing3": [
|
||||
{
|
||||
"json": {
|
||||
"id": "23423536",
|
||||
"name": "Edmund Pevensie",
|
||||
"email": "edmund@narnia.gov",
|
||||
"notes": "Passionate sailor",
|
||||
"country": "UK",
|
||||
"created": "1950-10-16"
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing5": [
|
||||
{
|
||||
"json": {
|
||||
"id": "23423533",
|
||||
"name": "José Arcadio Buendía",
|
||||
"email": "jab@macondo.co",
|
||||
"notes": "Lots of people named after him. Very confusing",
|
||||
"country": "CO",
|
||||
"created": "1967-05-05"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "23423536",
|
||||
"name": "Edmund Pevensie",
|
||||
"email": "edmund@narnia.gov",
|
||||
"notes": "Passionate sailor",
|
||||
"country": "UK",
|
||||
"created": "1950-10-16"
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"id": "23423532",
|
||||
"name": "Jay Gatsby",
|
||||
"email": "gatsby@west-egg.com",
|
||||
"notes": "Keeps asking about a green light??",
|
||||
"country": "US",
|
||||
"created": "1925-04-10"
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing4": [
|
||||
{
|
||||
"json": {
|
||||
"newData": {
|
||||
"name": "Jay Gatsby",
|
||||
"notes": "Keeps asking about a green light??",
|
||||
"country": "US",
|
||||
"created": "1925-04-10"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"newData": {
|
||||
"name": "José Arcadio Buendía",
|
||||
"notes": "Lots of people named after him. Very confusing",
|
||||
"country": "CO",
|
||||
"created": "1967-05-05"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"newData": {
|
||||
"name": "Max Sendak",
|
||||
"notes": "Keeps rolling his terrible eyes",
|
||||
"country": "US",
|
||||
"created": "1963-04-09"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"newData": {
|
||||
"name": "Zaphod Beeblebrox",
|
||||
"notes": "Felt like I was talking to more than one person",
|
||||
"country": null,
|
||||
"created": "1979-10-12"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"json": {
|
||||
"newData": {
|
||||
"name": "Edmund Pevensie",
|
||||
"notes": "Passionate sailor",
|
||||
"country": "UK",
|
||||
"created": "1950-10-16"
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"No Operation, do nothing6": [
|
||||
{
|
||||
"json": {
|
||||
"appended_newData_notes": [
|
||||
"Keeps asking about a green light??",
|
||||
"Lots of people named after him. Very confusing",
|
||||
"Keeps rolling his terrible eyes",
|
||||
"Felt like I was talking to more than one person",
|
||||
"Passionate sailor"
|
||||
],
|
||||
"max_newData_created": "1979-10-12",
|
||||
"min_newData_created": "1925-04-10"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"connections": {
|
||||
"When clicking \"Execute Workflow\"": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Customer Datastore (n8n training)",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Customer Datastore (n8n training)": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "Item Lists7",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists1": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists4": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists5": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists6",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists7": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists8",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists8": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "Item Lists9",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
},
|
||||
{
|
||||
"node": "No Operation, do nothing4",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing1",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists2": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing2",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists3": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing3",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists6": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing5",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"Item Lists9": {
|
||||
"main": [
|
||||
[
|
||||
{
|
||||
"node": "No Operation, do nothing6",
|
||||
"type": "main",
|
||||
"index": 0
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
},
|
||||
"active": false,
|
||||
"settings": {},
|
||||
"versionId": "ce3e0124-aa56-497c-a2e1-24158837c7f9",
|
||||
"id": "m7QDuxo599dkZ0Ex",
|
||||
"meta": {
|
||||
"instanceId": "e34acda144ba98351e38adb4db781751ca8cd64a8248aef8b65608fc9a49008c"
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
Reference in New Issue
Block a user