diff --git a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts index 47784b22a5..c29ae24cf8 100644 --- a/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/Google/Sheet/GoogleSheets.node.ts @@ -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); diff --git a/packages/nodes-base/nodes/Google/Sheet/test/v2/utils/utils.test.ts b/packages/nodes-base/nodes/Google/Sheet/test/v2/utils/utils.test.ts index 69894eea4b..474c878ded 100644 --- a/packages/nodes-base/nodes/Google/Sheet/test/v2/utils/utils.test.ts +++ b/packages/nodes-base/nodes/Google/Sheet/test/v2/utils/utils.test.ts @@ -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', + }, + ]); + }); +}); diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/append.operation.ts b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/append.operation.ts index afb463c225..5e1d0a810d 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/append.operation.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/append.operation.ts @@ -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, diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts index 6d51a32b88..46521fb245 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/appendOrUpdate.operation.ts @@ -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, diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts index f9c777e6e3..9650e73a4a 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/read.operation.ts @@ -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 }, diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts index 89b3503cf1..658d8ffcdd 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/actions/sheet/update.operation.ts @@ -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, diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/actions/versionDescription.ts b/packages/nodes-base/nodes/Google/Sheet/v2/actions/versionDescription.ts index 8936062383..8bb6064f49 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/actions/versionDescription.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/actions/versionDescription.ts @@ -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: { diff --git a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts index 5203dee6e0..a7d532b114 100644 --- a/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts +++ b/packages/nodes-base/nodes/Google/Sheet/v2/helpers/GoogleSheet.ts @@ -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; } } } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index 04dd0c0b7b..e3e512f7dd 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -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; }; }; }