catalog-backend: fix facets endpoint performance regression

Route the EXISTS-based filters through final_entities (one row per
entity) instead of correlating against the search table directly.
This avoids the pathological case where correlated subqueries scan
the much larger search table for every row in the outer facets query.

Signed-off-by: Fredrik Adelöw <freben@spotify.com>
Made-with: Cursor
This commit is contained in:
Fredrik Adelöw
2026-04-20 14:02:02 +02:00
parent 7192e84cad
commit cf195de72c
2 changed files with 18 additions and 2 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend': patch
---
Fixed a performance regression in the `/entity-facets` endpoint when filters or permission conditions are applied, by routing the EXISTS-based filter through `final_entities` instead of correlating against the much larger `search` table.
@@ -690,13 +690,24 @@ export class DefaultEntitiesCatalog implements EntitiesCatalog {
.groupBy(['search.key', 'search.original_value']);
if (request.filter || request.query) {
// Build a subquery that finds matching entity IDs via
// final_entities, so that the EXISTS-based filters correlate
// against one-row-per-entity rather than the much larger search
// table. This keeps the facets aggregation fast even with many
// filter clauses or permission conditions.
const entityIdSubquery = this.database('final_entities')
.select('final_entities.entity_id')
.whereNotNull('final_entities.final_entity');
applyEntityFilterToQuery({
filter: request.filter,
query: request.query,
targetQuery: query,
onEntityIdField: 'search.entity_id',
targetQuery: entityIdSubquery,
onEntityIdField: 'final_entities.entity_id',
knex: this.database,
});
query.whereIn('search.entity_id', entityIdSubquery);
}
const rows = await query;