mirror of
https://github.com/n8n-io/n8n.git
synced 2025-03-05 20:50:17 -08:00
feat(Google Sheets Node): Option how to combine filters when reading rows (#8652)
This commit is contained in:
parent
ad82f0c0c8
commit
a5e522e536
|
@ -11,7 +11,7 @@ export class GoogleSheets extends VersionedNodeType {
|
|||
name: 'googleSheets',
|
||||
icon: 'file:googleSheets.svg',
|
||||
group: ['input', 'output'],
|
||||
defaultVersion: 4.2,
|
||||
defaultVersion: 4.3,
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Read, update and write data to Google Sheets',
|
||||
};
|
||||
|
@ -23,6 +23,7 @@ export class GoogleSheets extends VersionedNodeType {
|
|||
4: new GoogleSheetsV2(baseDescription),
|
||||
4.1: new GoogleSheetsV2(baseDescription),
|
||||
4.2: new GoogleSheetsV2(baseDescription),
|
||||
4.3: new GoogleSheetsV2(baseDescription),
|
||||
};
|
||||
|
||||
super(nodeVersions, baseDescription);
|
||||
|
|
|
@ -282,3 +282,121 @@ describe('Test Google Sheets, autoMapInputData', () => {
|
|||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Test Google Sheets, lookupValues', () => {
|
||||
const inputData = [
|
||||
['row_number', 'id', 'num', 'text'],
|
||||
[2, 1, '111', 'bar'],
|
||||
[3, 3, 1, 'bar'],
|
||||
[4, 4, 1, 'baz'],
|
||||
[5, 5, 1, 'baz'],
|
||||
[6, 6, 66, 'foo'],
|
||||
[7, 7, 77, 'foo'],
|
||||
] as string[][];
|
||||
|
||||
it('should return rows by combining filters by OR', async () => {
|
||||
const fakeExecuteFunction = {
|
||||
getNode() {
|
||||
return {};
|
||||
},
|
||||
} as unknown as IExecuteFunctions;
|
||||
|
||||
const googleSheet = new GoogleSheet('spreadsheetId', fakeExecuteFunction);
|
||||
|
||||
const result = await googleSheet.lookupValues(
|
||||
inputData,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
lookupColumn: 'num',
|
||||
lookupValue: '1',
|
||||
},
|
||||
{
|
||||
lookupColumn: 'text',
|
||||
lookupValue: 'foo',
|
||||
},
|
||||
],
|
||||
true,
|
||||
'OR',
|
||||
);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toEqual([
|
||||
{
|
||||
row_number: 3,
|
||||
id: 3,
|
||||
num: 1,
|
||||
text: 'bar',
|
||||
},
|
||||
{
|
||||
row_number: 4,
|
||||
id: 4,
|
||||
num: 1,
|
||||
text: 'baz',
|
||||
},
|
||||
{
|
||||
row_number: 5,
|
||||
id: 5,
|
||||
num: 1,
|
||||
text: 'baz',
|
||||
},
|
||||
{
|
||||
row_number: 6,
|
||||
id: 6,
|
||||
num: 66,
|
||||
text: 'foo',
|
||||
},
|
||||
{
|
||||
row_number: 7,
|
||||
id: 7,
|
||||
num: 77,
|
||||
text: 'foo',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return rows by combining filters by AND', async () => {
|
||||
const fakeExecuteFunction = {
|
||||
getNode() {
|
||||
return {};
|
||||
},
|
||||
} as unknown as IExecuteFunctions;
|
||||
|
||||
const googleSheet = new GoogleSheet('spreadsheetId', fakeExecuteFunction);
|
||||
|
||||
const result = await googleSheet.lookupValues(
|
||||
inputData,
|
||||
0,
|
||||
1,
|
||||
[
|
||||
{
|
||||
lookupColumn: 'num',
|
||||
lookupValue: '1',
|
||||
},
|
||||
{
|
||||
lookupColumn: 'text',
|
||||
lookupValue: 'baz',
|
||||
},
|
||||
],
|
||||
true,
|
||||
'AND',
|
||||
);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result).toEqual([
|
||||
{
|
||||
row_number: 4,
|
||||
id: 4,
|
||||
num: 1,
|
||||
text: 'baz',
|
||||
},
|
||||
{
|
||||
row_number: 5,
|
||||
id: 5,
|
||||
num: 1,
|
||||
text: 'baz',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -136,7 +136,7 @@ export const description: SheetProperties = [
|
|||
show: {
|
||||
resource: ['sheet'],
|
||||
operation: ['append'],
|
||||
'@version': [4, 4.1, 4.2],
|
||||
'@version': [{ _cnd: { gte: 4 } }],
|
||||
},
|
||||
hide: {
|
||||
...untilSheetSelected,
|
||||
|
|
|
@ -172,7 +172,7 @@ export const description: SheetProperties = [
|
|||
show: {
|
||||
resource: ['sheet'],
|
||||
operation: ['appendOrUpdate'],
|
||||
'@version': [4, 4.1, 4.2],
|
||||
'@version': [{ _cnd: { gte: 4 } }],
|
||||
},
|
||||
hide: {
|
||||
...untilSheetSelected,
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import type { IExecuteFunctions, IDataObject, INodeExecutionData } from 'n8n-workflow';
|
||||
import type {
|
||||
IExecuteFunctions,
|
||||
IDataObject,
|
||||
INodeExecutionData,
|
||||
INodeProperties,
|
||||
} from 'n8n-workflow';
|
||||
import type { GoogleSheet } from '../../helpers/GoogleSheet';
|
||||
import {
|
||||
getRangeString,
|
||||
|
@ -15,6 +20,27 @@ import type {
|
|||
|
||||
import { dataLocationOnSheet, outputFormatting } from './commonDescription';
|
||||
|
||||
const combineFiltersOptions: INodeProperties = {
|
||||
displayName: 'Combine Filters',
|
||||
name: 'combineFilters',
|
||||
type: 'options',
|
||||
description:
|
||||
'How to combine the conditions defined in "Filters": AND requires all conditions to be true, OR requires at least one condition to be true',
|
||||
options: [
|
||||
{
|
||||
name: 'AND',
|
||||
value: 'AND',
|
||||
description: 'Only rows that meet all the conditions are selected',
|
||||
},
|
||||
{
|
||||
name: 'OR',
|
||||
value: 'OR',
|
||||
description: 'Rows that meet at least one condition are selected',
|
||||
},
|
||||
],
|
||||
default: 'AND',
|
||||
};
|
||||
|
||||
export const description: SheetProperties = [
|
||||
{
|
||||
displayName: 'Filters',
|
||||
|
@ -64,6 +90,33 @@ export const description: SheetProperties = [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
...combineFiltersOptions,
|
||||
default: 'OR',
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [{ _cnd: { lt: 4.3 } }],
|
||||
resource: ['sheet'],
|
||||
operation: ['read'],
|
||||
},
|
||||
hide: {
|
||||
...untilSheetSelected,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
...combineFiltersOptions,
|
||||
displayOptions: {
|
||||
show: {
|
||||
'@version': [{ _cnd: { gte: 4.3 } }],
|
||||
resource: ['sheet'],
|
||||
operation: ['read'],
|
||||
},
|
||||
hide: {
|
||||
...untilSheetSelected,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
displayName: 'Options',
|
||||
name: 'options',
|
||||
|
@ -178,19 +231,24 @@ export async function execute(
|
|||
}
|
||||
}
|
||||
|
||||
const combineFilters = this.getNodeParameter('combineFilters', itemIndex, 'OR') as
|
||||
| 'AND'
|
||||
| 'OR';
|
||||
|
||||
responseData = await sheet.lookupValues(
|
||||
data as string[][],
|
||||
headerRow,
|
||||
firstDataRow,
|
||||
lookupValues,
|
||||
returnAllMatches,
|
||||
combineFilters,
|
||||
);
|
||||
} else {
|
||||
responseData = sheet.structureArrayDataByColumn(data as string[][], headerRow, firstDataRow);
|
||||
}
|
||||
|
||||
returnData.push(
|
||||
...responseData.map((item, index) => {
|
||||
...responseData.map((item) => {
|
||||
return {
|
||||
json: item,
|
||||
pairedItem: { item: itemIndex },
|
||||
|
|
|
@ -172,7 +172,7 @@ export const description: SheetProperties = [
|
|||
show: {
|
||||
resource: ['sheet'],
|
||||
operation: ['update'],
|
||||
'@version': [4, 4.1, 4.2],
|
||||
'@version': [{ _cnd: { gte: 4 } }],
|
||||
},
|
||||
hide: {
|
||||
...untilSheetSelected,
|
||||
|
|
|
@ -9,7 +9,7 @@ export const versionDescription: INodeTypeDescription = {
|
|||
name: 'googleSheets',
|
||||
icon: 'file:googleSheets.svg',
|
||||
group: ['input', 'output'],
|
||||
version: [3, 4, 4.1, 4.2],
|
||||
version: [3, 4, 4.1, 4.2, 4.3],
|
||||
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
||||
description: 'Read, update and write data to Google Sheets',
|
||||
defaults: {
|
||||
|
|
|
@ -629,6 +629,7 @@ export class GoogleSheet {
|
|||
dataStartRowIndex: number,
|
||||
lookupValues: ILookupValues[],
|
||||
returnAllMatches?: boolean,
|
||||
combineFilters: 'AND' | 'OR' = 'OR',
|
||||
): Promise<IDataObject[]> {
|
||||
const keys: string[] = [];
|
||||
|
||||
|
@ -665,28 +666,65 @@ export class GoogleSheet {
|
|||
// const returnData = [inputData[keyRowIndex]];
|
||||
const returnData = [keys];
|
||||
|
||||
lookupLoop: for (const lookupValue of lookupValues) {
|
||||
returnColumnIndex = keys.indexOf(lookupValue.lookupColumn);
|
||||
if (combineFilters === 'OR') {
|
||||
lookupLoop: for (const lookupValue of lookupValues) {
|
||||
returnColumnIndex = keys.indexOf(lookupValue.lookupColumn);
|
||||
|
||||
if (returnColumnIndex === -1) {
|
||||
throw new NodeOperationError(
|
||||
this.executeFunctions.getNode(),
|
||||
`The column "${lookupValue.lookupColumn}" could not be found`,
|
||||
);
|
||||
if (returnColumnIndex === -1) {
|
||||
throw new NodeOperationError(
|
||||
this.executeFunctions.getNode(),
|
||||
`The column "${lookupValue.lookupColumn}" could not be found`,
|
||||
);
|
||||
}
|
||||
|
||||
// Loop over all the items and find the one with the matching value
|
||||
for (rowIndex = dataStartRowIndex; rowIndex < inputData.length; rowIndex++) {
|
||||
if (
|
||||
inputData[rowIndex][returnColumnIndex]?.toString() ===
|
||||
lookupValue.lookupValue.toString()
|
||||
) {
|
||||
if (addedRows.indexOf(rowIndex) === -1) {
|
||||
returnData.push(inputData[rowIndex]);
|
||||
addedRows.push(rowIndex);
|
||||
}
|
||||
|
||||
if (returnAllMatches !== true) {
|
||||
continue lookupLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lookupLoop: for (rowIndex = dataStartRowIndex; rowIndex < inputData.length; rowIndex++) {
|
||||
let allMatch = true;
|
||||
|
||||
// Loop over all the items and find the one with the matching value
|
||||
for (rowIndex = dataStartRowIndex; rowIndex < inputData.length; rowIndex++) {
|
||||
if (
|
||||
inputData[rowIndex][returnColumnIndex]?.toString() === lookupValue.lookupValue.toString()
|
||||
) {
|
||||
for (const lookupValue of lookupValues) {
|
||||
returnColumnIndex = keys.indexOf(lookupValue.lookupColumn);
|
||||
|
||||
if (returnColumnIndex === -1) {
|
||||
throw new NodeOperationError(
|
||||
this.executeFunctions.getNode(),
|
||||
`The column "${lookupValue.lookupColumn}" could not be found`,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
inputData[rowIndex][returnColumnIndex]?.toString() !==
|
||||
lookupValue.lookupValue.toString()
|
||||
) {
|
||||
allMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allMatch) {
|
||||
if (addedRows.indexOf(rowIndex) === -1) {
|
||||
returnData.push(inputData[rowIndex]);
|
||||
addedRows.push(rowIndex);
|
||||
}
|
||||
|
||||
if (returnAllMatches !== true) {
|
||||
continue lookupLoop;
|
||||
break lookupLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2175,7 +2175,7 @@ export type PropertiesOf<M extends { resource: string; operation: string }> = Ar
|
|||
[key in 'show' | 'hide']?: {
|
||||
resource?: Array<M['resource']>;
|
||||
operation?: Array<M['operation']>;
|
||||
[otherKey: string]: NodeParameterValue[] | undefined;
|
||||
[otherKey: string]: Array<NodeParameterValue | DisplayCondition> | undefined;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue