From ab48002b82b2bb85988fd8dc15721061fa0bfec9 Mon Sep 17 00:00:00 2001 From: Harry Brundage Date: Mon, 4 Nov 2024 18:11:52 +0000 Subject: [PATCH] feat: add support for explicit chain filters --- src/filter.ts | 42 ++++++++++++++++++++++++++++++++++++++++++ test/filter.ts | 22 ++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/filter.ts b/src/filter.ts index aa2883a8c..09972a0ac 100644 --- a/src/filter.ts +++ b/src/filter.ts @@ -609,6 +609,48 @@ export class Filter { }); } + /** + * This filter applies a series of filters, in order, to each output row. A chain filter is like using a logical AND. + * + * @param {object} filters Each filter in the chain sees only the output of the previous filter. For example, if you chain two filters, and the first filter removes half of the cells from the output row, the second filter does not have access to the cells that were removed. + * + * @example + * ``` + * //- + * // In the following example, we're creating a filter that will retrieve + * // results for entries that were created between December 17th, 2015 + * // and March 22nd, 2016 and entries that have data for `follows:tjefferson`. + * //- + * const filter = [ + * { + * chain: [ + * [ + * { + * time: { + * start: new Date('December 17, 2015'), + * end: new Date('March 22, 2016') + * } + * } + * ], + * [ + * { + * family: 'follows' + * }, + * { + * column: 'tjefferson' + * } + * ] + * ] + * } + * ]; + * ``` + */ + chain(filters: RawFilter[]): void { + this.set('chain', { + filters: filters.map(Filter.parse), + }); + } + /** * Applies the given label to all cells in the output row. This allows * the client to determine which results were produced from which part of diff --git a/test/filter.ts b/test/filter.ts index e3041523c..4b8270074 100644 --- a/test/filter.ts +++ b/test/filter.ts @@ -376,6 +376,28 @@ describe('Bigtable/Filter', () => { }); }); + describe('chain', () => { + it('should create a chain filter', done => { + const fakeFilters = [{}, {}, {}]; + + const spy = sandbox.stub(Filter, 'parse').returnsArg(0); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + filter.set = (filterName, value: any) => { + assert.strictEqual(filterName, 'chain'); + assert.strictEqual(value.filters[0], fakeFilters[0]); + assert.strictEqual(value.filters[1], fakeFilters[1]); + assert.strictEqual(value.filters[2], fakeFilters[2]); + assert.strictEqual(spy.getCall(0).args[0], fakeFilters[0]); + assert.strictEqual(spy.getCall(1).args[0], fakeFilters[1]); + assert.strictEqual(spy.getCall(2).args[0], fakeFilters[2]); + spy.restore(); + done(); + }; + + filter.chain(fakeFilters); + }); + }); + describe('label', () => { it('should apply the label transformer', done => { const label = 'label';