Skip to content

Commit

Permalink
feat(perf): huge filtering execution speed improvements (#694)
Browse files Browse the repository at this point in the history
* feat(perf): huge filtering execution speed improvements
The problem was that previous code was parsing the search values at the same time as processing the filtering check and that is totally unnecessary, the search value once provided will never change when comparing to each cell. This PR is rewriting the Filter Service to process the search values only once (before looping through all cells). Parsing Dates are the biggest performance killer because we take dates, that could be in any format, and we parse (convert) them to MomentJS object and that is expensive on 50,000 or more rows (see below for perf logs before/after).
  • Loading branch information
ghiscoding committed Feb 12, 2021
1 parent 844e167 commit f93a24d
Show file tree
Hide file tree
Showing 42 changed files with 1,677 additions and 896 deletions.
Original file line number Diff line number Diff line change
@@ -1,55 +1,63 @@
import { FieldType, FilterConditionOption } from '../../models/index';
import { booleanFilterCondition } from '../booleanFilterCondition';
import { executeMappedCondition } from '../executeMappedCondition';
import { executeBooleanFilterCondition, getFilterParsedBoolean } from '../booleanFilterCondition';
import { executeFilterConditionTest } from '../filterConditionProcesses';

/** will return True in all cases with only 1 exception when the only searchTerm is inversed to the cell value */

describe('booleanFilterCondition method', () => {
describe('executeBooleanFilterCondition method', () => {
it('should return True when no cell value is provided, neither search terms', () => {
const searchTerms = undefined;
const options = { dataKey: '', operator: 'EQ', cellValue: '', fieldType: FieldType.boolean } as FilterConditionOption;
const output = booleanFilterCondition(options);
const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms));
expect(output).toBe(true);
});

it('should return True when any cell value is provided', () => {
const searchTerms = undefined;
const options = { dataKey: '', operator: 'EQ', cellValue: 'foo', fieldType: FieldType.boolean } as FilterConditionOption;
const output = booleanFilterCondition(options);
const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms));
expect(output).toBe(true);
});

it('should return True when boolean value True is provided as cell value and called from the "executeMappedCondition"', () => {
const options = { dataKey: '', operator: 'EQ', cellValue: 'true', fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption;
const output = executeMappedCondition(options);
it('should return True when boolean value True is provided as cell value and called from the "executeFilterConditionTest"', () => {
const searchTerms = ['true'];
const options = { dataKey: '', operator: 'EQ', cellValue: 'true', fieldType: FieldType.boolean, searchTerms } as FilterConditionOption;
const output = executeFilterConditionTest(options, getFilterParsedBoolean(searchTerms));
expect(output).toBe(true);
});

it('should return True when boolean value True is provided as cell value', () => {
const options = { dataKey: '', operator: 'EQ', cellValue: 'true', fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption;
const output = booleanFilterCondition(options);
const searchTerms = ['true'];
const options = { dataKey: '', operator: 'EQ', cellValue: 'true', fieldType: FieldType.boolean, searchTerms } as FilterConditionOption;
const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms));
expect(output).toBe(true);
});

it('should return True when boolean value provided is equal to the searchTerms even when it is a string type', () => {
const options = { dataKey: '', operator: 'EQ', cellValue: true, fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption;
const output = booleanFilterCondition(options);
const searchTerms = ['true'];
const options = { dataKey: '', operator: 'EQ', cellValue: true, fieldType: FieldType.boolean, searchTerms } as FilterConditionOption;
const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms));
expect(output).toBe(true);
});

it('should return True when the cell value is equal to at least 1 of the searchTerms', () => {
const options = { dataKey: '', operator: 'EQ', cellValue: true, fieldType: FieldType.boolean, searchTerms: ['true', 'false'] } as FilterConditionOption;
const output = booleanFilterCondition(options);
const searchTerms = ['true', 'false'];
const options = { dataKey: '', operator: 'EQ', cellValue: true, fieldType: FieldType.boolean, searchTerms } as FilterConditionOption;
const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms));
expect(output).toBe(true);
});

it('should return False when cell value is inversed to the searchTerm', () => {
const options = { dataKey: '', operator: 'EQ', cellValue: false, fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption;
const output = booleanFilterCondition(options);
const searchTerms = ['true'];
const options = { dataKey: '', operator: 'EQ', cellValue: false, fieldType: FieldType.boolean, searchTerms } as FilterConditionOption;
const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms));
expect(output).toBe(false);
});

it('should return False even when Operator is Not Equal because condition is always a strict equal check', () => {
const options = { dataKey: '', operator: 'NE', cellValue: false, fieldType: FieldType.boolean, searchTerms: ['true'] } as FilterConditionOption;
const output = booleanFilterCondition(options);
const searchTerms = ['true'];
const options = { dataKey: '', operator: 'NE', cellValue: false, fieldType: FieldType.boolean, searchTerms } as FilterConditionOption;
const output = executeBooleanFilterCondition(options, getFilterParsedBoolean(searchTerms));
expect(output).toBe(false);
});
});
Original file line number Diff line number Diff line change
@@ -1,84 +1,84 @@
import { FieldType, FilterConditionOption } from '../../models/index';
import { collectionSearchFilterCondition } from '../collectionSearchFilterCondition';
import { executeMappedCondition } from '../executeMappedCondition';
import { executeCollectionSearchFilterCondition } from '../collectionSearchFilterCondition';
import { executeFilterConditionTest } from '../filterConditionProcesses';

describe('collectionSearchFilterCondition method', () => {
describe('executeCollectionSearchFilterCondition method', () => {
it('should return False when searchTerms is empty', () => {
const options = { dataKey: '', operator: 'IN', cellValue: 3, fieldType: FieldType.string } as FilterConditionOption;
const output = collectionSearchFilterCondition(options);
const output = executeCollectionSearchFilterCondition(options);
expect(output).toBe(false);
});

it('should return True when input value is in the searchTerms', () => {
const options = { dataKey: '', operator: 'IN', cellValue: 3, fieldType: FieldType.string, searchTerms: ['3'] } as FilterConditionOption;
const output = collectionSearchFilterCondition(options);
const output = executeCollectionSearchFilterCondition(options);
expect(output).toBe(true);
});

it('should return True when input value provided is equal to the searchTerms', () => {
const options = { dataKey: '', operator: 'IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption;
const output = collectionSearchFilterCondition(options);
const output = executeCollectionSearchFilterCondition(options);
expect(output).toBe(true);
});

it('should return True when input value provided is equal to the searchTerms even though there are no Operator provided (it will use EQ as default)', () => {
const options = { dataKey: '', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption;
const output = collectionSearchFilterCondition(options);
const output = executeCollectionSearchFilterCondition(options);
expect(output).toBe(true);
});

it('should return True when the cell value is equal to at least 1 of the searchTerms', () => {
const options = { dataKey: '', operator: 'IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['bar', 'foo', 'John'] } as FilterConditionOption;
const output = collectionSearchFilterCondition(options);
const output = executeCollectionSearchFilterCondition(options);
expect(output).toBe(true);
});

it('should return True when the cell value is equal to at least 1 of the searchTerms and called by the "executeMappedCondition"', () => {
it('should return True when the cell value is equal to at least 1 of the searchTerms and called by the "executeFilterConditionTest"', () => {
const options = { dataKey: '', operator: 'IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['bar', 'foo', 'John'] } as FilterConditionOption;
const output = executeMappedCondition(options);
const output = executeFilterConditionTest(options);
expect(output).toBe(true);
});

it('should return False when cell value is not within the searchTerms', () => {
const options = { dataKey: '', operator: 'IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['bar'] } as FilterConditionOption;
const output = collectionSearchFilterCondition(options);
const output = executeCollectionSearchFilterCondition(options);
expect(output).toBe(false);
});

it('should return False even when Operator is Not IN because condition is always a strict equal check', () => {
const options = { dataKey: '', operator: 'NOT_IN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption;
const output = collectionSearchFilterCondition(options);
const output = executeCollectionSearchFilterCondition(options);
expect(output).toBe(false);
});

it('should return False even when Operator is NIN because condition is always a strict equal check', () => {
const options = { dataKey: '', operator: 'NIN', cellValue: 'foo', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption;
const output = collectionSearchFilterCondition(options);
const output = executeCollectionSearchFilterCondition(options);
expect(output).toBe(false);
});

it('should return True even when Operator is "Not IN" and the cell value is not in search terms', () => {
const options1 = { dataKey: '', operator: 'NIN', cellValue: 'bar', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption;
const options2 = { dataKey: '', operator: 'NOT_IN', cellValue: 'bar', fieldType: FieldType.string, searchTerms: ['foo'] } as FilterConditionOption;

const output1 = collectionSearchFilterCondition(options1);
const output2 = collectionSearchFilterCondition(options2);
const output1 = executeCollectionSearchFilterCondition(options1);
const output2 = executeCollectionSearchFilterCondition(options2);

expect(output1).toBe(true);
expect(output2).toBe(true);
});

it('should return True when input value contains searchTerms content', () => {
const options = { dataKey: '', operator: 'IN_CONTAINS', cellValue: 'Task2,Task3', fieldType: FieldType.string, searchTerms: ['Task2', 'Task3'] } as FilterConditionOption;
const output = collectionSearchFilterCondition(options);
const output = executeCollectionSearchFilterCondition(options);
expect(output).toBe(true);
});

it('should return True when input value is not found when using "not in contains" searchTerms content', () => {
const options1 = { dataKey: '', operator: 'NIN_CONTAINS', cellValue: 'Task11,Task22,Task33', fieldType: FieldType.string, searchTerms: ['Task1', 'Task2', 'Task3'] } as FilterConditionOption;
const options2 = { dataKey: '', operator: 'NOT_IN_CONTAINS', cellValue: 'Task11,Task22,Task33', fieldType: FieldType.string, searchTerms: ['Task1', 'Task2', 'Task3'] } as FilterConditionOption;
const output1 = collectionSearchFilterCondition(options1);
const output2 = collectionSearchFilterCondition(options2);
const output1 = executeCollectionSearchFilterCondition(options1);
const output2 = executeCollectionSearchFilterCondition(options2);

expect(output1).toBe(true);
expect(output2).toBe(true);
Expand All @@ -87,8 +87,8 @@ describe('collectionSearchFilterCondition method', () => {
it('should return False when input value not in contains searchTerms content', () => {
const options1 = { dataKey: '', operator: 'NIN_CONTAINS', cellValue: 'Task1,Task3', fieldType: FieldType.string, searchTerms: ['Task1', 'Task2', 'Task3'] } as FilterConditionOption;
const options2 = { dataKey: '', operator: 'NOT_IN_CONTAINS', cellValue: 'Task1,Task3', fieldType: FieldType.string, searchTerms: ['Task1', 'Task2', 'Task3'] } as FilterConditionOption;
const output1 = collectionSearchFilterCondition(options1);
const output2 = collectionSearchFilterCondition(options2);
const output1 = executeCollectionSearchFilterCondition(options1);
const output2 = executeCollectionSearchFilterCondition(options2);

expect(output1).toBe(false);
expect(output2).toBe(false);
Expand Down

0 comments on commit f93a24d

Please sign in to comment.