From abca7343d2af7f7a50dbe4d36751f2badd85f3d7 Mon Sep 17 00:00:00 2001 From: Gajus Kuizinas Date: Thu, 11 Nov 2021 14:18:11 -0600 Subject: [PATCH] feat: pre-calculate path --- src/grammar.ne | 2 +- src/grammar.ts | 2 +- src/internalFilter.ts | 4 ++-- src/types.ts | 1 + test/benchmark.ts | 14 ++++++++++++++ 5 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/grammar.ne b/src/grammar.ne index 07b403d..cc0e8af 100644 --- a/src/grammar.ne +++ b/src/grammar.ne @@ -83,7 +83,7 @@ post_boolean_primary -> | __ boolean_primary {% d => d[1] %} side -> - field ":" _ query {% d => ({field: d[0], ...d[3]}) %} + field ":" _ query {% d => ({field: d[0], fieldPath: d[0].split('.').filter(Boolean), ...d[3]}) %} | query {% d => ({field: '', ...d[0]}) %} field -> diff --git a/src/grammar.ts b/src/grammar.ts index 5fcf83b..e972db3 100644 --- a/src/grammar.ts +++ b/src/grammar.ts @@ -216,7 +216,7 @@ const grammar: Grammar = { {"name": "boolean_primary", "symbols": ["side"], "postprocess": id}, {"name": "post_boolean_primary", "symbols": [{"literal":"("}, "_", "boolean_primary", "_", {"literal":")"}], "postprocess": d => d[2]}, {"name": "post_boolean_primary", "symbols": ["__", "boolean_primary"], "postprocess": d => d[1]}, - {"name": "side", "symbols": ["field", {"literal":":"}, "_", "query"], "postprocess": d => ({field: d[0], ...d[3]})}, + {"name": "side", "symbols": ["field", {"literal":":"}, "_", "query"], "postprocess": d => ({field: d[0], fieldPath: d[0].split('.').filter(Boolean), ...d[3]})}, {"name": "side", "symbols": ["query"], "postprocess": d => ({field: '', ...d[0]})}, {"name": "field$ebnf$1", "symbols": []}, {"name": "field$ebnf$1", "symbols": ["field$ebnf$1", /[a-zA-Z\d_$.]/], "postprocess": (d) => d[0].concat([d[1]])}, diff --git a/src/internalFilter.ts b/src/internalFilter.ts index e84a052..e7e0065 100644 --- a/src/internalFilter.ts +++ b/src/internalFilter.ts @@ -195,10 +195,10 @@ const testField = ( path, highlights, ); - } else if (ast.field.includes('.')) { + } else if (ast.fieldPath) { let value = row; - for (const key of ast.field.split('.')) { + for (const key of ast.fieldPath) { if (typeof value !== 'object' || value === null) { return false; } else if (key in value) { diff --git a/src/types.ts b/src/types.ts index 9267c19..1b35e99 100644 --- a/src/types.ts +++ b/src/types.ts @@ -9,6 +9,7 @@ export type RelationalOperator = '<' | '<=' | '=' | '>' | '>='; export type Ast = { field: string, + fieldPath: readonly string[], left?: Ast, operand?: Ast, operator?: 'AND' | 'NOT' | 'OR', diff --git a/test/benchmark.ts b/test/benchmark.ts index 1693989..318537e 100644 --- a/test/benchmark.ts +++ b/test/benchmark.ts @@ -29,6 +29,11 @@ let size = 10_000; while (size--) { persons.push({ email: faker.internet.email(), + foo: { + bar: { + baz: faker.name.findName(), + }, + }, height: randomInRange(160, 220), name: faker.name.findName(), }); @@ -92,6 +97,15 @@ void suite( filter(query, persons); }; }), + + add('filters list by the "foo.bar.baz" field using simple strict equality check', () => { + const query = parse('foo.bar.baz:"Gajus"'); + + return () => { + filter(query, persons); + }; + }), + cycle(), complete(), );