From a7dfddaa6ce39798a2770c1de6f5b0e34946476b Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 23 Feb 2026 13:16:31 -0700 Subject: [PATCH 1/5] Perf: use Set lookups in isFilterSupported for O(1) filter checks - Add ALLOWED_TYPE_FILTERS_SET built once at module load from ALLOWED_TYPE_FILTERS - Add REPORT_FIELD_FILTER_KEY and REPORT_FIELD_GLOBAL_PREFIX constants - Replace array.some() with Set.has() and single report-field prefix check - Keeps behavior identical; improves performance when called in loops (SearchFiltersBar, buildQueryStringFromFilterFormValues) Co-authored-by: Cursor --- src/libs/SearchQueryUtils.ts | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index 9e143fbef6aae..13e925d39b615 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -89,6 +89,18 @@ const createKeyToUserFriendlyMap = () => { // Create the maps once at module initialization for performance const keyToUserFriendlyMap = createKeyToUserFriendlyMap(); +// Precompute Sets of allowed filters per type for O(1) isFilterSupported lookups +const ALLOWED_TYPE_FILTERS_SET: Partial>> = (() => { + const map: Partial>> = {}; + for (const [dataType, filters] of Object.entries(ALLOWED_TYPE_FILTERS)) { + map[dataType as SearchDataTypes] = new Set(filters as string[]); + } + return map; +})(); + +const REPORT_FIELD_FILTER_KEY = CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_FIELD; +const REPORT_FIELD_GLOBAL_PREFIX = CONST.SEARCH.REPORT_FIELD.GLOBAL_PREFIX; + /** * Lookup a key in the keyToUserFriendlyMap and return the user-friendly key. * @@ -403,10 +415,17 @@ function isSearchDatePreset(date: string | undefined): date is SearchDatePreset * Returns whether a given search filter is supported in a given search data type */ function isFilterSupported(filter: SearchAdvancedFiltersKey, type: SearchDataTypes) { - return ALLOWED_TYPE_FILTERS[type].some((supportedFilter) => { - const isReportFieldSupported = supportedFilter === CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_FIELD && filter.startsWith(CONST.SEARCH.REPORT_FIELD.GLOBAL_PREFIX); - return supportedFilter === filter || isReportFieldSupported; - }); + const allowed = ALLOWED_TYPE_FILTERS_SET[type]; + if (!allowed) { + return false; + } + if (allowed.has(filter)) { + return true; + } + if (filter.startsWith(REPORT_FIELD_GLOBAL_PREFIX)) { + return allowed.has(REPORT_FIELD_FILTER_KEY); + } + return false; } /** From 40a5e6bbe42bf45fb03054b784c6fb9128c3609a Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 23 Feb 2026 13:19:19 -0700 Subject: [PATCH 2/5] Define ALLOWED_TYPE_FILTERS as Sets in SearchAdvancedFiltersForm - Change each type's value from array to new Set([...]) - Add Record> type for O(1) lookups at source Co-authored-by: Cursor --- src/types/form/SearchAdvancedFiltersForm.ts | 26 ++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/types/form/SearchAdvancedFiltersForm.ts b/src/types/form/SearchAdvancedFiltersForm.ts index 54c33c46ab913..d6eed3641dde7 100644 --- a/src/types/form/SearchAdvancedFiltersForm.ts +++ b/src/types/form/SearchAdvancedFiltersForm.ts @@ -172,8 +172,8 @@ const FILTER_KEYS = { LIMIT: 'limit', } as const; -const ALLOWED_TYPE_FILTERS = { - [CONST.SEARCH.DATA_TYPES.EXPENSE]: [ +const ALLOWED_TYPE_FILTERS: Record> = { + [CONST.SEARCH.DATA_TYPES.EXPENSE]: new Set([ FILTER_KEYS.TYPE, FILTER_KEYS.STATUS, FILTER_KEYS.FROM, @@ -270,8 +270,8 @@ const ALLOWED_TYPE_FILTERS = { FILTER_KEYS.ATTENDEE_NOT, FILTER_KEYS.COLUMNS, FILTER_KEYS.LIMIT, - ], - [CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT]: [ + ]), + [CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT]: new Set([ FILTER_KEYS.TYPE, FILTER_KEYS.STATUS, FILTER_KEYS.FROM, @@ -326,8 +326,8 @@ const ALLOWED_TYPE_FILTERS = { FILTER_KEYS.TITLE_NOT, FILTER_KEYS.REPORT_FIELD, FILTER_KEYS.COLUMNS, - ], - [CONST.SEARCH.DATA_TYPES.INVOICE]: [ + ]), + [CONST.SEARCH.DATA_TYPES.INVOICE]: new Set([ FILTER_KEYS.TYPE, FILTER_KEYS.STATUS, FILTER_KEYS.FROM, @@ -410,9 +410,9 @@ const ALLOWED_TYPE_FILTERS = { FILTER_KEYS.REPORT_FIELD, FILTER_KEYS.TITLE_NOT, FILTER_KEYS.COLUMNS, - ], + ]), - [CONST.SEARCH.DATA_TYPES.TRIP]: [ + [CONST.SEARCH.DATA_TYPES.TRIP]: new Set([ FILTER_KEYS.TYPE, FILTER_KEYS.STATUS, FILTER_KEYS.FROM, @@ -488,9 +488,9 @@ const ALLOWED_TYPE_FILTERS = { FILTER_KEYS.TITLE_NOT, FILTER_KEYS.REPORT_FIELD, FILTER_KEYS.COLUMNS, - ], + ]), - [CONST.SEARCH.DATA_TYPES.CHAT]: [ + [CONST.SEARCH.DATA_TYPES.CHAT]: new Set([ FILTER_KEYS.TYPE, FILTER_KEYS.FROM, FILTER_KEYS.FROM_NOT, @@ -508,9 +508,9 @@ const ALLOWED_TYPE_FILTERS = { FILTER_KEYS.IS_NOT, FILTER_KEYS.HAS, FILTER_KEYS.HAS_NOT, - ], + ]), - [CONST.SEARCH.DATA_TYPES.TASK]: [ + [CONST.SEARCH.DATA_TYPES.TASK]: new Set([ FILTER_KEYS.TYPE, FILTER_KEYS.STATUS, FILTER_KEYS.TITLE, @@ -527,7 +527,7 @@ const ALLOWED_TYPE_FILTERS = { FILTER_KEYS.DATE_NOT, FILTER_KEYS.DATE_AFTER, FILTER_KEYS.DATE_BEFORE, - ], + ]), }; type SearchAdvancedFiltersKey = ValueOf | ReportFieldKey; From b87cde23cd48127c17b94a2eb55a33650859cbda Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 23 Feb 2026 13:19:20 -0700 Subject: [PATCH 3/5] Use ALLOWED_TYPE_FILTERS directly in isFilterSupported - Remove ALLOWED_TYPE_FILTERS_SET and module-level loop - Use imported ALLOWED_TYPE_FILTERS[type] (now Sets) for .has() lookups Co-authored-by: Cursor --- src/libs/SearchQueryUtils.ts | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index 13e925d39b615..284a4beb2305d 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -89,15 +89,6 @@ const createKeyToUserFriendlyMap = () => { // Create the maps once at module initialization for performance const keyToUserFriendlyMap = createKeyToUserFriendlyMap(); -// Precompute Sets of allowed filters per type for O(1) isFilterSupported lookups -const ALLOWED_TYPE_FILTERS_SET: Partial>> = (() => { - const map: Partial>> = {}; - for (const [dataType, filters] of Object.entries(ALLOWED_TYPE_FILTERS)) { - map[dataType as SearchDataTypes] = new Set(filters as string[]); - } - return map; -})(); - const REPORT_FIELD_FILTER_KEY = CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_FIELD; const REPORT_FIELD_GLOBAL_PREFIX = CONST.SEARCH.REPORT_FIELD.GLOBAL_PREFIX; @@ -415,7 +406,7 @@ function isSearchDatePreset(date: string | undefined): date is SearchDatePreset * Returns whether a given search filter is supported in a given search data type */ function isFilterSupported(filter: SearchAdvancedFiltersKey, type: SearchDataTypes) { - const allowed = ALLOWED_TYPE_FILTERS_SET[type]; + const allowed = ALLOWED_TYPE_FILTERS[type]; if (!allowed) { return false; } From c3ecc7994ebf2cc50c3aefe08a01c7a816b0a1cb Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 23 Feb 2026 13:20:29 -0700 Subject: [PATCH 4/5] rename var --- src/libs/SearchQueryUtils.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index 284a4beb2305d..36b367c6dc697 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -406,15 +406,15 @@ function isSearchDatePreset(date: string | undefined): date is SearchDatePreset * Returns whether a given search filter is supported in a given search data type */ function isFilterSupported(filter: SearchAdvancedFiltersKey, type: SearchDataTypes) { - const allowed = ALLOWED_TYPE_FILTERS[type]; - if (!allowed) { + const supportedTypeFilters = ALLOWED_TYPE_FILTERS[type]; + if (!supportedTypeFilters) { return false; } - if (allowed.has(filter)) { + if (supportedTypeFilters.has(filter)) { return true; } if (filter.startsWith(REPORT_FIELD_GLOBAL_PREFIX)) { - return allowed.has(REPORT_FIELD_FILTER_KEY); + return supportedTypeFilters.has(REPORT_FIELD_FILTER_KEY); } return false; } From 3fbecf4a21c19d30f7e0f3887950ecf779815386 Mon Sep 17 00:00:00 2001 From: Carlos Martins Date: Mon, 23 Feb 2026 13:54:49 -0700 Subject: [PATCH 5/5] rm consts --- src/libs/SearchQueryUtils.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/libs/SearchQueryUtils.ts b/src/libs/SearchQueryUtils.ts index 36b367c6dc697..271418f19eddb 100644 --- a/src/libs/SearchQueryUtils.ts +++ b/src/libs/SearchQueryUtils.ts @@ -89,9 +89,6 @@ const createKeyToUserFriendlyMap = () => { // Create the maps once at module initialization for performance const keyToUserFriendlyMap = createKeyToUserFriendlyMap(); -const REPORT_FIELD_FILTER_KEY = CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_FIELD; -const REPORT_FIELD_GLOBAL_PREFIX = CONST.SEARCH.REPORT_FIELD.GLOBAL_PREFIX; - /** * Lookup a key in the keyToUserFriendlyMap and return the user-friendly key. * @@ -413,8 +410,8 @@ function isFilterSupported(filter: SearchAdvancedFiltersKey, type: SearchDataTyp if (supportedTypeFilters.has(filter)) { return true; } - if (filter.startsWith(REPORT_FIELD_GLOBAL_PREFIX)) { - return supportedTypeFilters.has(REPORT_FIELD_FILTER_KEY); + if (filter.startsWith(CONST.SEARCH.REPORT_FIELD.GLOBAL_PREFIX)) { + return supportedTypeFilters.has(CONST.SEARCH.SYNTAX_FILTER_KEYS.REPORT_FIELD); } return false; }