diff --git a/packages/nodes-base/nodes/GoogleSheets/GoogleSheet.ts b/packages/nodes-base/nodes/GoogleSheets/GoogleSheet.ts index 57b1958b3..1f61b5888 100644 --- a/packages/nodes-base/nodes/GoogleSheets/GoogleSheet.ts +++ b/packages/nodes-base/nodes/GoogleSheets/GoogleSheet.ts @@ -18,6 +18,10 @@ export interface ISheetUpdateData { values: string[][]; } +export interface ILookupValues { + lookupColumn: string; + lookupValue: string; +} export class GoogleSheet { id: string; @@ -146,7 +150,7 @@ export class GoogleSheet { /** * Returns the given sheet data in a strucutred way */ - structureData(inputData: string[][], startRow: number, keys: string[]): IDataObject[] { + structureData(inputData: string[][], startRow: number, keys: string[], addEmpty?: boolean): IDataObject[] { const returnData = []; let tempEntry: IDataObject, rowIndex: number, columnIndex: number, key: string; @@ -160,7 +164,7 @@ export class GoogleSheet { tempEntry[key] = inputData[rowIndex][columnIndex]; } } - if (Object.keys(tempEntry).length) { + if (Object.keys(tempEntry).length || addEmpty === true) { // Only add the entry if data got found to not have empty ones returnData.push(tempEntry); } @@ -312,6 +316,61 @@ export class GoogleSheet { } + /** + * Looks for a specific value in a column and if it gets found it returns the whole row + * + * @param {string[][]} inputData Data to to check for lookup value in + * @param {number} keyRowIndex Index of the row which contains the keys + * @param {number} dataStartRowIndex Index of the first row which contains data + * @param {ILookupValues[]} lookupValues The lookup values which decide what data to return + * @returns {Promise} + * @memberof GoogleSheet + */ + async lookupValues(inputData: string[][], keyRowIndex: number, dataStartRowIndex: number, lookupValues: ILookupValues[]): Promise { + const keys: string[] = []; + + if (keyRowIndex < 0 || dataStartRowIndex < keyRowIndex || keyRowIndex >= inputData.length) { + // The key row does not exist so it is not possible to look up the data + throw new Error(`The key row does not exist!`); + } + + // Create the keys array + for (let columnIndex = 0; columnIndex < inputData[keyRowIndex].length; columnIndex++) { + keys.push(inputData[keyRowIndex][columnIndex]); + } + + const returnData = [ + inputData[keyRowIndex], + ]; + + // Loop over all the lookup values and try to find a row to return + let rowIndex: number; + let returnColumnIndex: number; + lookupLoop: + for (const lookupValue of lookupValues) { + returnColumnIndex = keys.indexOf(lookupValue.lookupColumn); + + if (returnColumnIndex === -1) { + throw new Error(`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()) { + returnData.push(inputData[rowIndex]); + continue lookupLoop; + } + } + + // If value could not be found add an empty one that the order of + // the returned items stays the same + returnData.push([]); + } + + return this.structureData(returnData, 1, keys, true); + } + + async convertStructuredDataToArray(inputData: IDataObject[], range: string, keyRowIndex: number): Promise { let startColumn, endColumn; let sheet: string | undefined = undefined; diff --git a/packages/nodes-base/nodes/GoogleSheets/GoogleSheets.node.ts b/packages/nodes-base/nodes/GoogleSheets/GoogleSheets.node.ts index 051271cce..fe08e0fb8 100644 --- a/packages/nodes-base/nodes/GoogleSheets/GoogleSheets.node.ts +++ b/packages/nodes-base/nodes/GoogleSheets/GoogleSheets.node.ts @@ -9,6 +9,7 @@ import { import { GoogleSheet, IGoogleAuthCredentials, + ILookupValues, ISheetUpdateData, } from './GoogleSheet'; @@ -44,6 +45,11 @@ export class GoogleSheets implements INodeType { value: 'append', description: 'Appends the data to a Sheet', }, + { + name: 'Lookup', + value: 'lookup', + description: 'Looks for a specific column value and then returns the matching row' + }, { name: 'Read', value: 'read', @@ -150,7 +156,7 @@ export class GoogleSheets implements INodeType { }, // ---------------------------------- - // Read & Update + // Read & Update & lookupColumn // ---------------------------------- { displayName: 'Data Start Row', @@ -194,6 +200,43 @@ export class GoogleSheets implements INodeType { description: 'Index of the row which contains the key. Starts with 0.', }, + + // ---------------------------------- + // lookup + // ---------------------------------- + { + displayName: 'Lookup Column', + name: 'lookupColumn', + type: 'string', + default: '', + placeholder: 'Email', + required: true, + displayOptions: { + show: { + operation: [ + 'lookup' + ], + }, + }, + description: 'The name of the column in which to look for value.', + }, + { + displayName: 'Lookup Value', + name: 'lookupValue', + type: 'string', + default: '', + placeholder: 'frank@example.com', + required: true, + displayOptions: { + show: { + operation: [ + 'lookup' + ], + }, + }, + description: 'The value to look for in column.', + }, + // ---------------------------------- // Update // ---------------------------------- @@ -259,6 +302,33 @@ export class GoogleSheets implements INodeType { // TODO: Should have something like add metadata which does not get passed through return this.prepareOutputData(items); + } else if (operation === 'lookup') { + // ---------------------------------- + // lookup + // ---------------------------------- + + const sheetData = await sheet.getData(range); + + if (sheetData === undefined) { + return []; + } + + const dataStartRow = this.getNodeParameter('dataStartRow', 0) as number; + const keyRow = this.getNodeParameter('keyRow', 0) as number; + + const items = this.getInputData(); + + const lookupValues: ILookupValues[] = []; + for (let i = 0; i < items.length; i++) { + lookupValues.push({ + lookupColumn: this.getNodeParameter('lookupColumn', i) as string, + lookupValue: this.getNodeParameter('lookupValue', i) as string, + }); + } + + const returnData = await sheet.lookupValues(sheetData, keyRow, dataStartRow, lookupValues); + + return [this.helpers.returnJsonArray(returnData)]; } else if (operation === 'read') { // ---------------------------------- // read @@ -312,7 +382,6 @@ export class GoogleSheets implements INodeType { const keyRow = this.getNodeParameter('keyRow', 0) as number; const dataStartRow = this.getNodeParameter('dataStartRow', 0) as number; - const setData: IDataObject[] = []; items.forEach((item) => { setData.push(item.json);