diff --git a/src/bigquery.ts b/src/bigquery.ts index 21b5434d..805ab1ce 100644 --- a/src/bigquery.ts +++ b/src/bigquery.ts @@ -376,12 +376,33 @@ export class BigQuery extends common.Service { * * @param {object} schema * @param {array} rows + * @param {array} selectedFields List of fields to return. + * If unspecified, all fields are returned. * @returns {array} Fields using their matching names from the table's schema. */ static mergeSchemaWithRows_( schema: TableSchema | TableField, - rows: TableRow[] + rows: TableRow[], + selectedFields?: string[] ) { + if (selectedFields && selectedFields!.length > 0) { + const selectedFieldsArray = selectedFields!.map(c => { + return c.split('.'); + }); + + const currentFields = selectedFieldsArray.map(c => c.shift()); + //filter schema fields based on selected fields. + schema.fields = schema.fields?.filter( + field => + currentFields + .map(c => c!.toLowerCase()) + .indexOf(field.name!.toLowerCase()) >= 0 + ); + selectedFields = selectedFieldsArray + .filter(c => c.length > 0) + .map(c => c.join('.')); + } + return arrify(rows) .map(mergeSchema) .map(flattenRows); @@ -391,10 +412,10 @@ export class BigQuery extends common.Service { let value = field.v; if (schemaField.mode === 'REPEATED') { value = (value as TableRowField[]).map(val => { - return convert(schemaField, val.v); + return convert(schemaField, val.v, selectedFields); }); } else { - value = convert(schemaField, value); + value = convert(schemaField, value, selectedFields); } // eslint-disable-next-line @typescript-eslint/no-explicit-any const fieldObject: any = {}; @@ -403,8 +424,12 @@ export class BigQuery extends common.Service { }); } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - function convert(schemaField: TableField, value: any) { + function convert( + schemaField: TableField, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + value: any, + selectedFields?: string[] + ) { if (is.null(value)) { return value; } @@ -434,7 +459,11 @@ export class BigQuery extends common.Service { break; } case 'RECORD': { - value = BigQuery.mergeSchemaWithRows_(schemaField, value).pop(); + value = BigQuery.mergeSchemaWithRows_( + schemaField, + value, + selectedFields + ).pop(); break; } case 'DATE': { diff --git a/src/table.ts b/src/table.ts index 99eaf3e8..076e7b06 100644 --- a/src/table.ts +++ b/src/table.ts @@ -1678,7 +1678,11 @@ class Table extends common.ServiceObject { callback!(err, null, null, resp); return; } - rows = BigQuery.mergeSchemaWithRows_(this.metadata.schema, rows || []); + rows = BigQuery.mergeSchemaWithRows_( + this.metadata.schema, + rows || [], + options.selectedFields ? options.selectedFields!.split(',') : [] + ); callback!(null, rows, nextQuery, resp); }; diff --git a/test/table.ts b/test/table.ts index d5588a54..abac343b 100644 --- a/test/table.ts +++ b/test/table.ts @@ -2025,6 +2025,112 @@ describe('BigQuery/Table', () => { done(); }); }); + + it('should return selected fields', done => { + const selectedFields = 'age'; + const rows = [{f: [{v: 40}]}]; + const schema = { + fields: [ + {name: 'name', type: 'string'}, + {name: 'age', type: 'INTEGER'}, + ], + }; + const result = [{age: 40}]; + + table.metadata = {schema}; + + sandbox.restore(); + + table.request = (reqOpts: DecorateRequestOptions, callback: Function) => { + callback(null, {rows}); + }; + + table.getRows({selectedFields}, (err: Error, rows: {}) => { + assert.ifError(err); + assert.deepStrictEqual(rows, result); + done(); + }); + }); + + it('should return selected fields from nested objects', done => { + const selectedFields = 'objects.nested_object.nested_property_1'; + const rows = [ + { + f: [ + { + v: [ + { + v: { + f: [ + { + v: { + f: [ + { + v: 'nested_property_1_value', + }, + ], + }, + }, + ], + }, + }, + ], + }, + ], + }, + ]; + const schema = { + fields: [ + {name: 'name', type: 'string'}, + { + name: 'objects', + type: 'RECORD', + mode: 'REPEATED', + fields: [ + { + name: 'nested_object', + type: 'RECORD', + fields: [ + { + name: 'nested_property', + type: 'STRING', + }, + { + name: 'nested_property_1', + type: 'STRING', + }, + ], + }, + ], + }, + ], + }; + const result = [ + { + objects: [ + { + nested_object: { + nested_property_1: 'nested_property_1_value', + }, + }, + ], + }, + ]; + + table.metadata = {schema}; + + sandbox.restore(); + + table.request = (reqOpts: DecorateRequestOptions, callback: Function) => { + callback(null, {rows}); + }; + + table.getRows({selectedFields}, (err: Error, rows: {}) => { + assert.ifError(err); + assert.deepStrictEqual(rows, result); + done(); + }); + }); }); describe('insert', () => {