@backstage/plugin-catalog-backend 3.7.0 → 3.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @backstage/plugin-catalog-backend
|
|
2
2
|
|
|
3
|
+
## 3.7.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 88da433: Split the `queryEntities` list and count into separate queries instead of a multi-reference CTE. When the `filtered` CTE was referenced twice (once for the count, once for the data), PostgreSQL refused to inline it, forcing full materialization of the filtered set before applying `LIMIT`. By running the count as a standalone query, the list CTE is only referenced once, allowing the planner to short-circuit on `LIMIT` and return the first page in milliseconds instead of waiting for the full filtered set to materialize.
|
|
8
|
+
|
|
9
|
+
The standalone count query also fixes a pre-existing bug where `totalItems` was inflated for entities with multi-valued sort fields (e.g. tags). The old CTE-based count counted search rows, so an entity with 3 tags would be counted 3 times. The new count uses `EXISTS` to count distinct entities, aligning `totalItems` with the number of entities actually reachable through cursor pagination.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies
|
|
12
|
+
- @backstage/catalog-client@1.15.2
|
|
13
|
+
|
|
3
14
|
## 3.7.0
|
|
4
15
|
|
|
5
16
|
### Minor Changes
|
|
@@ -247,6 +247,40 @@ class DefaultEntitiesCatalog {
|
|
|
247
247
|
this.logger.warn(`Only one sort field is supported, ignoring the rest`);
|
|
248
248
|
}
|
|
249
249
|
const sortField = cursor.orderFields.at(0);
|
|
250
|
+
const normalizedFullTextFilterTerm = cursor.fullTextFilter?.term?.trim();
|
|
251
|
+
const textFilterFields = cursor.fullTextFilter?.fields ?? [
|
|
252
|
+
sortField?.field || "metadata.uid"
|
|
253
|
+
];
|
|
254
|
+
const applyPredicates = (q, options) => {
|
|
255
|
+
if (cursor.filter || cursor.query) {
|
|
256
|
+
applyEntityFilterToQuery.applyEntityFilterToQuery({
|
|
257
|
+
filter: cursor.filter,
|
|
258
|
+
query: cursor.query,
|
|
259
|
+
targetQuery: q,
|
|
260
|
+
onEntityIdField: "final_entities.entity_id",
|
|
261
|
+
knex: this.database
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
if (normalizedFullTextFilterTerm) {
|
|
265
|
+
if (options?.searchInScope && textFilterFields.length === 1 && textFilterFields[0] === sortField?.field) {
|
|
266
|
+
q.andWhereRaw(
|
|
267
|
+
"search.value like ?",
|
|
268
|
+
`%${normalizedFullTextFilterTerm.toLocaleLowerCase("en-US")}%`
|
|
269
|
+
);
|
|
270
|
+
} else {
|
|
271
|
+
const matchQuery = this.database("search").select("search.entity_id").whereIn(
|
|
272
|
+
"search.key",
|
|
273
|
+
textFilterFields.map((field) => field.toLocaleLowerCase("en-US"))
|
|
274
|
+
).andWhere(function keyFilter() {
|
|
275
|
+
this.andWhereRaw(
|
|
276
|
+
"search.value like ?",
|
|
277
|
+
`%${normalizedFullTextFilterTerm.toLocaleLowerCase("en-US")}%`
|
|
278
|
+
);
|
|
279
|
+
});
|
|
280
|
+
q.andWhere("final_entities.entity_id", "in", matchQuery);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
250
284
|
const dbQuery = this.database.with(
|
|
251
285
|
"filtered",
|
|
252
286
|
["entity_id", "final_entity", ...sortField ? ["value"] : []],
|
|
@@ -267,50 +301,19 @@ class DefaultEntitiesCatalog {
|
|
|
267
301
|
final_entity: "final_entities.final_entity"
|
|
268
302
|
});
|
|
269
303
|
}
|
|
270
|
-
|
|
271
|
-
applyEntityFilterToQuery.applyEntityFilterToQuery({
|
|
272
|
-
filter: cursor.filter,
|
|
273
|
-
query: cursor.query,
|
|
274
|
-
targetQuery: inner,
|
|
275
|
-
onEntityIdField: "final_entities.entity_id",
|
|
276
|
-
knex: this.database
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
const normalizedFullTextFilterTerm = cursor.fullTextFilter?.term?.trim();
|
|
280
|
-
const textFilterFields = cursor.fullTextFilter?.fields ?? [
|
|
281
|
-
sortField?.field || "metadata.uid"
|
|
282
|
-
];
|
|
283
|
-
if (normalizedFullTextFilterTerm) {
|
|
284
|
-
if (textFilterFields.length === 1 && textFilterFields[0] === sortField?.field) {
|
|
285
|
-
inner.andWhereRaw(
|
|
286
|
-
"search.value like ?",
|
|
287
|
-
`%${normalizedFullTextFilterTerm.toLocaleLowerCase("en-US")}%`
|
|
288
|
-
);
|
|
289
|
-
} else {
|
|
290
|
-
const matchQuery = this.database("search").select("search.entity_id").whereIn(
|
|
291
|
-
"search.key",
|
|
292
|
-
textFilterFields.map((field) => field.toLocaleLowerCase("en-US"))
|
|
293
|
-
).andWhere(function keyFilter() {
|
|
294
|
-
this.andWhereRaw(
|
|
295
|
-
"search.value like ?",
|
|
296
|
-
`%${normalizedFullTextFilterTerm.toLocaleLowerCase(
|
|
297
|
-
"en-US"
|
|
298
|
-
)}%`
|
|
299
|
-
);
|
|
300
|
-
});
|
|
301
|
-
inner.andWhere("final_entities.entity_id", "in", matchQuery);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
+
applyPredicates(inner, { searchInScope: !!sortField });
|
|
304
305
|
}
|
|
305
306
|
);
|
|
307
|
+
dbQuery.from("filtered").select("*");
|
|
308
|
+
let countQuery;
|
|
306
309
|
if (shouldComputeTotalItems) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
310
|
+
countQuery = this.database("final_entities").whereNotNull("final_entities.final_entity").count("*", { as: "count" });
|
|
311
|
+
if (sortField) {
|
|
312
|
+
countQuery.whereExists(
|
|
313
|
+
this.database("search").select(this.database.raw(1)).whereRaw("search.entity_id = final_entities.entity_id").where("search.key", sortField.field).whereNotNull("search.value")
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
applyPredicates(countQuery);
|
|
314
317
|
}
|
|
315
318
|
const isOrderingDescending = sortField?.order === "desc";
|
|
316
319
|
if (cursor.orderFieldValues) {
|
|
@@ -373,14 +376,17 @@ class DefaultEntitiesCatalog {
|
|
|
373
376
|
dbQuery.offset(request.offset);
|
|
374
377
|
}
|
|
375
378
|
dbQuery.limit(isFetchingBackwards ? limit : limit + 1);
|
|
376
|
-
const rows =
|
|
379
|
+
const [rows, countResult] = await Promise.all([
|
|
380
|
+
limit > 0 ? dbQuery : Promise.resolve([]),
|
|
381
|
+
countQuery ?? Promise.resolve(void 0)
|
|
382
|
+
]);
|
|
377
383
|
let totalItems;
|
|
378
384
|
if (cursor.totalItems !== void 0) {
|
|
379
385
|
totalItems = cursor.totalItems;
|
|
380
386
|
} else if (cursor.skipTotalItems) {
|
|
381
387
|
totalItems = 0;
|
|
382
|
-
} else if (
|
|
383
|
-
totalItems = Number(
|
|
388
|
+
} else if (countResult?.[0]) {
|
|
389
|
+
totalItems = Number(countResult[0].count);
|
|
384
390
|
} else {
|
|
385
391
|
totalItems = 0;
|
|
386
392
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DefaultEntitiesCatalog.cjs.js","sources":["../../src/service/DefaultEntitiesCatalog.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, stringifyEntityRef } from '@backstage/catalog-model';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { chunk as lodashChunk, isEqual } from 'lodash';\nimport {\n Cursor,\n EntitiesBatchRequest,\n EntitiesBatchResponse,\n EntitiesCatalog,\n EntitiesRequest,\n EntitiesResponse,\n EntityAncestryResponse,\n EntityFacetsRequest,\n EntityFacetsResponse,\n EntityOrder,\n EntityPagination,\n QueryEntitiesRequest,\n QueryEntitiesResponse,\n} from '../catalog/types';\nimport {\n DbFinalEntitiesRow,\n DbPageInfo,\n DbRefreshStateReferencesRow,\n DbRefreshStateRow,\n DbRelationsRow,\n DbSearchRow,\n} from '../database/tables';\nimport { Stitcher } from '../stitching/types';\n\nimport {\n expandLegacyCompoundRelationsInEntity,\n isQueryEntitiesCursorRequest,\n isQueryEntitiesInitialRequest,\n} from './util';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { applyEntityFilterToQuery } from './request/applyEntityFilterToQuery';\nimport { processRawEntitiesResult } from './response';\n\nconst DEFAULT_LIMIT = 200;\n\nfunction parsePagination(input?: EntityPagination): EntityPagination {\n if (!input) {\n return {};\n }\n\n let { limit, offset } = input;\n\n if (input.after === undefined) {\n return { limit, offset };\n }\n\n let cursor;\n try {\n const json = Buffer.from(input.after, 'base64').toString('utf8');\n cursor = JSON.parse(json);\n } catch {\n throw new InputError('Malformed after cursor, could not be parsed');\n }\n\n if (cursor.limit !== undefined) {\n if (!Number.isInteger(cursor.limit)) {\n throw new InputError('Malformed after cursor, limit was not an number');\n }\n limit = cursor.limit;\n }\n\n if (cursor.offset !== undefined) {\n if (!Number.isInteger(cursor.offset)) {\n throw new InputError('Malformed after cursor, offset was not a number');\n }\n offset = cursor.offset;\n }\n\n return { limit, offset };\n}\n\nfunction stringifyPagination(\n input: Required<Omit<EntityPagination, 'after'>>,\n): string {\n const { limit, offset } = input;\n const json = JSON.stringify({ limit, offset });\n const base64 = Buffer.from(json, 'utf8').toString('base64');\n return base64;\n}\n\nexport class DefaultEntitiesCatalog implements EntitiesCatalog {\n private readonly database: Knex;\n private readonly logger: LoggerService;\n private readonly stitcher: Stitcher;\n private readonly enableRelationsCompatibility: boolean;\n\n constructor(options: {\n database: Knex;\n logger: LoggerService;\n stitcher: Stitcher;\n enableRelationsCompatibility?: boolean;\n }) {\n this.database = options.database;\n this.logger = options.logger;\n this.stitcher = options.stitcher;\n this.enableRelationsCompatibility = Boolean(\n options.enableRelationsCompatibility,\n );\n }\n\n async entities(request?: EntitiesRequest): Promise<EntitiesResponse> {\n const db = this.database;\n const { limit, offset } = parsePagination(request?.pagination);\n const primaryOrder = request?.order?.[0];\n\n // When exactly one order field is specified we run a two-phase fetch\n // that drives from the search-by-key index for that field. The index\n // walks rows in already-sorted order, so the planner can short-circuit\n // on LIMIT instead of having to materialise and sort the full filtered\n // set. Phase 2 appends entities that lack the order field (NULLS LAST)\n // and is skipped when phase 1 already fills the request.\n //\n // Multi-field ordering falls back to the original LEFT JOIN shape\n // because tie-breaking on a second field requires materialisation of\n // the full set anyway.\n const useFastPath = primaryOrder && (request?.order?.length ?? 0) <= 1;\n let rows: DbFinalEntitiesRow[];\n if (useFastPath) {\n rows = await this.runOrderedEntitiesQuery(\n request!,\n primaryOrder,\n limit,\n offset,\n );\n } else {\n let entitiesQuery =\n db<DbFinalEntitiesRow>('final_entities').select('final_entities.*');\n\n request?.order?.forEach(({ field }, index) => {\n const alias = `order_${index}`;\n entitiesQuery = entitiesQuery.leftOuterJoin(\n { [alias]: 'search' },\n function search(inner) {\n inner\n .on(`${alias}.entity_id`, 'final_entities.entity_id')\n .andOn(`${alias}.key`, db.raw('?', [field]));\n },\n );\n });\n\n entitiesQuery = entitiesQuery.whereNotNull('final_entities.final_entity');\n\n if (request?.filter) {\n entitiesQuery = applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: entitiesQuery,\n onEntityIdField: 'final_entities.entity_id',\n knex: db,\n });\n }\n\n if (request?.order) {\n request.order.forEach(({ order }, index) => {\n if (db.client.config.client === 'pg') {\n entitiesQuery = entitiesQuery.orderBy([\n { column: `order_${index}.value`, order, nulls: 'last' },\n ]);\n } else {\n entitiesQuery = entitiesQuery.orderBy([\n {\n column: `order_${index}.value`,\n order: undefined,\n nulls: 'last',\n },\n { column: `order_${index}.value`, order },\n ]);\n }\n });\n entitiesQuery.orderBy('final_entities.entity_id', 'asc');\n } else {\n entitiesQuery = entitiesQuery.orderBy(\n 'final_entities.entity_ref',\n 'asc',\n );\n }\n\n if (limit !== undefined) {\n entitiesQuery = entitiesQuery.limit(limit + 1);\n }\n if (offset !== undefined) {\n entitiesQuery = entitiesQuery.offset(offset);\n }\n\n rows = await entitiesQuery;\n }\n\n let pageInfo: DbPageInfo;\n if (limit === undefined || rows.length <= limit) {\n pageInfo = { hasNextPage: false };\n } else {\n rows = rows.slice(0, -1);\n pageInfo = {\n hasNextPage: true,\n endCursor: stringifyPagination({\n limit,\n offset: (offset ?? 0) + limit,\n }),\n };\n }\n\n return {\n entities: processRawEntitiesResult(\n rows.map(r => r.final_entity!),\n this.enableRelationsCompatibility\n ? e => {\n expandLegacyCompoundRelationsInEntity(e);\n if (request?.fields) {\n return request.fields(e);\n }\n return e;\n }\n : request?.fields,\n ),\n pageInfo,\n };\n }\n\n /**\n * Two-phase fetch used when the caller has specified an order field.\n * See entities() for a longer description of the rationale.\n */\n private async runOrderedEntitiesQuery(\n request: EntitiesRequest,\n primaryOrder: EntityOrder,\n limit: number | undefined,\n offset: number | undefined,\n ): Promise<DbFinalEntitiesRow[]> {\n const db = this.database;\n const isPg = db.client.config.client === 'pg';\n const wantedRows =\n limit === undefined ? Number.MAX_SAFE_INTEGER : (offset ?? 0) + limit + 1;\n\n const applyFilter = <T extends object>(\n query: Knex.QueryBuilder<T>,\n ): Knex.QueryBuilder<T> => {\n if (!request.filter) {\n return query;\n }\n return applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: query,\n onEntityIdField: 'final_entities.entity_id',\n knex: db,\n });\n };\n\n // Phase 1 -- entities that have a non-NULL value for the order field.\n // Rows where the key exists but value IS NULL (e.g. the entity field is\n // explicitly null, or exceeded MAX_VALUE_LENGTH in buildEntitySearch) are\n // excluded here so they fall through to Phase 2 and sort in the same\n // NULLS-LAST bucket as entities that have no row for the key at all —\n // preserving the semantics of the previous LEFT JOIN approach.\n let withField = db('search as order_0')\n .innerJoin(\n 'final_entities',\n 'final_entities.entity_id',\n 'order_0.entity_id',\n )\n .where('order_0.key', primaryOrder.field)\n .whereNotNull('order_0.value')\n .whereNotNull('final_entities.final_entity')\n .select<DbFinalEntitiesRow[]>('final_entities.*');\n withField = applyFilter(withField);\n withField = isPg\n ? withField.orderBy([\n { column: 'order_0.value', order: primaryOrder.order, nulls: 'last' },\n { column: 'final_entities.entity_id', order: 'asc' },\n ])\n : withField.orderBy([\n { column: 'order_0.value', order: undefined, nulls: 'last' },\n { column: 'order_0.value', order: primaryOrder.order },\n { column: 'final_entities.entity_id', order: 'asc' },\n ]);\n if (wantedRows < Number.MAX_SAFE_INTEGER) {\n withField = withField.limit(wantedRows);\n }\n const withFieldRows = await withField;\n\n // If phase 1 already covered everything we asked for, skip the second\n // phase entirely. This is the common UI case where every entity in the\n // filtered set has the order field.\n if (withFieldRows.length >= wantedRows) {\n const skip = offset ?? 0;\n return withFieldRows.slice(skip, skip + (limit ?? wantedRows) + 1);\n }\n\n // Phase 2 -- entities that lack the order field, appended after.\n let withoutField = db<DbFinalEntitiesRow>('final_entities')\n .select<DbFinalEntitiesRow[]>('final_entities.*')\n .whereNotNull('final_entities.final_entity')\n .whereNotExists(qb =>\n qb\n .from('search')\n .where('search.entity_id', db.ref('final_entities.entity_id'))\n .andWhere('search.key', primaryOrder.field)\n .whereNotNull('search.value'),\n );\n withoutField = applyFilter(withoutField);\n withoutField = withoutField.orderBy(\n 'final_entities.entity_id',\n 'asc', // NULL group always stable-sorted ASC regardless of primary direction\n );\n if (limit !== undefined) {\n // Phase 2 only contributes the rows that phase 1 didn't cover.\n const remaining =\n wantedRows - Math.min(withFieldRows.length, (offset ?? 0) + limit + 1);\n withoutField = withoutField.limit(Math.max(0, remaining));\n }\n const withoutFieldRows = await withoutField;\n\n const combined = [...withFieldRows, ...withoutFieldRows];\n if (limit === undefined) {\n return combined.slice(offset ?? 0);\n }\n const skip = offset ?? 0;\n return combined.slice(skip, skip + limit + 1);\n }\n\n async entitiesBatch(\n request: EntitiesBatchRequest,\n ): Promise<EntitiesBatchResponse> {\n const lookup = new Map<string, string>();\n\n for (const chunk of lodashChunk(request.entityRefs, 200)) {\n let query = this.database<DbFinalEntitiesRow>('final_entities')\n .select({\n entityRef: 'final_entities.entity_ref',\n entity: 'final_entities.final_entity',\n })\n .whereIn('final_entities.entity_ref', chunk);\n\n if (request?.filter || request?.query) {\n query = applyEntityFilterToQuery({\n filter: request.filter,\n query: request.query,\n targetQuery: query,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n }\n\n for (const row of await query) {\n lookup.set(row.entityRef, row.entity ? row.entity : null);\n }\n }\n\n const items = request.entityRefs.map(ref => lookup.get(ref) ?? null);\n\n return { items: processRawEntitiesResult(items, request.fields) };\n }\n\n async queryEntities(\n request: QueryEntitiesRequest,\n ): Promise<QueryEntitiesResponse> {\n const limit = request.limit ?? DEFAULT_LIMIT;\n\n const cursor: Omit<Cursor, 'orderFieldValues'> & {\n orderFieldValues?: (string | null)[];\n skipTotalItems: boolean;\n } = {\n orderFields: [],\n isPrevious: false,\n ...parseCursorFromRequest(request),\n };\n\n // For performance reasons we invoke the count query only on the first\n // request. The result is then embedded into the cursor for subsequent\n // requests. Therefore this can be undefined here, but will then get\n // populated further down.\n const shouldComputeTotalItems =\n cursor.totalItems === undefined && !cursor.skipTotalItems;\n const isFetchingBackwards = cursor.isPrevious;\n\n if (cursor.orderFields.length > 1) {\n this.logger.warn(`Only one sort field is supported, ignoring the rest`);\n }\n\n const sortField = cursor.orderFields.at(0);\n\n // The first part of the query builder is a subquery that applies all of the\n // filtering. When a sort field is specified, the search table for that key\n // drives the query via INNER JOIN so that the (key, value, entity_id)\n // index walks rows in sort order, letting LIMIT short-circuit. Entities\n // that lack the sort field are excluded from both the result set and the\n // count — this is a deliberate choice that aligns totalItems with the\n // number of entities actually reachable through cursor pagination.\n const dbQuery = this.database.with(\n 'filtered',\n ['entity_id', 'final_entity', ...(sortField ? ['value'] : [])],\n inner => {\n if (sortField) {\n inner\n .from('search')\n .innerJoin(\n 'final_entities',\n 'final_entities.entity_id',\n 'search.entity_id',\n )\n .where('search.key', sortField.field)\n .whereNotNull('final_entities.final_entity')\n .select({\n entity_id: 'final_entities.entity_id',\n final_entity: 'final_entities.final_entity',\n value: 'search.value',\n });\n } else {\n inner\n .from<DbFinalEntitiesRow>('final_entities')\n .whereNotNull('final_entity')\n .select({\n entity_id: 'final_entities.entity_id',\n final_entity: 'final_entities.final_entity',\n });\n }\n\n // Add regular filters and/or predicate query, if given\n if (cursor.filter || cursor.query) {\n applyEntityFilterToQuery({\n filter: cursor.filter,\n query: cursor.query,\n targetQuery: inner,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n }\n\n // Add full text search filters, if given\n const normalizedFullTextFilterTerm =\n cursor.fullTextFilter?.term?.trim();\n const textFilterFields = cursor.fullTextFilter?.fields ?? [\n sortField?.field || 'metadata.uid',\n ];\n if (normalizedFullTextFilterTerm) {\n if (\n textFilterFields.length === 1 &&\n textFilterFields[0] === sortField?.field\n ) {\n // If there is one item, apply the like query to the top level query which is already\n // filtered based on the singular sortField.\n inner.andWhereRaw(\n 'search.value like ?',\n `%${normalizedFullTextFilterTerm.toLocaleLowerCase('en-US')}%`,\n );\n } else {\n const matchQuery = this.database<DbSearchRow>('search')\n .select('search.entity_id')\n // textFilterFields must be lowercased to match searchable keys in database, i.e. spec.profile.displayName -> spec.profile.displayname\n .whereIn(\n 'search.key',\n textFilterFields.map(field => field.toLocaleLowerCase('en-US')),\n )\n .andWhere(function keyFilter() {\n this.andWhereRaw(\n 'search.value like ?',\n `%${normalizedFullTextFilterTerm.toLocaleLowerCase(\n 'en-US',\n )}%`,\n );\n });\n inner.andWhere('final_entities.entity_id', 'in', matchQuery);\n }\n }\n },\n );\n\n // Only pay the cost of counting the number of items if needed\n if (shouldComputeTotalItems) {\n // Note the intentional cross join here. The filtered_count dataset is\n // always exactly one row, so it won't grow the result unnecessarily. But\n // it's also important that there IS at least one row, because even if the\n // filtered dataset is empty, we still want to know the total number of\n // items.\n dbQuery\n .with('filtered_count', ['count'], inner =>\n inner.from('filtered').count('*', { as: 'count' }),\n )\n .fromRaw('filtered_count, filtered')\n .select('count', 'filtered.*');\n } else {\n dbQuery.from('filtered').select('*');\n }\n\n const isOrderingDescending = sortField?.order === 'desc';\n\n // Move forward (or backward) in the set to the correct cursor position\n if (cursor.orderFieldValues) {\n if (cursor.orderFieldValues.length === 2) {\n // The first will be the sortField value, the second the entity_id\n const [first, second] = cursor.orderFieldValues;\n dbQuery.andWhere(function nested() {\n this.where(\n 'filtered.value',\n isFetchingBackwards !== isOrderingDescending ? '<' : '>',\n first,\n )\n .orWhere('filtered.value', '=', first)\n .andWhere(\n 'filtered.entity_id',\n isFetchingBackwards !== isOrderingDescending ? '<' : '>',\n second,\n );\n });\n } else if (cursor.orderFieldValues.length === 1) {\n // This will be the entity_id\n const [first] = cursor.orderFieldValues;\n dbQuery.andWhere('entity_id', isFetchingBackwards ? '<' : '>', first);\n }\n }\n\n // Add the ordering\n let order = sortField?.order ?? 'asc';\n if (isFetchingBackwards) {\n order = invertOrder(order);\n }\n if (this.database.client.config.client === 'pg') {\n // pg correctly orders by the column value and handling nulls in one go\n dbQuery.orderBy([\n ...(sortField\n ? [\n {\n column: 'filtered.value',\n order,\n nulls: 'last',\n },\n ]\n : []),\n {\n column: 'filtered.entity_id',\n order,\n },\n ]);\n } else {\n // sqlite and mysql translate the above statement ONLY into \"order by (value is null) asc\"\n // no matter what the order is, for some reason, so we have to manually add back the statement\n // that translates to \"order by value <order>\" while avoiding to give an order\n dbQuery.orderBy([\n ...(sortField\n ? [\n {\n column: 'filtered.value',\n order: undefined,\n nulls: 'last',\n },\n {\n column: 'filtered.value',\n order,\n },\n ]\n : []),\n {\n column: 'filtered.entity_id',\n order,\n },\n ]);\n }\n\n // Apply a manually set initial offset\n if (\n isQueryEntitiesInitialRequest(request) &&\n request.offset !== undefined\n ) {\n dbQuery.offset(request.offset);\n }\n // fetch an extra item to check if there are more items.\n dbQuery.limit(isFetchingBackwards ? limit : limit + 1);\n\n const rows = shouldComputeTotalItems || limit > 0 ? await dbQuery : [];\n\n let totalItems: number;\n if (cursor.totalItems !== undefined) {\n totalItems = cursor.totalItems;\n } else if (cursor.skipTotalItems) {\n totalItems = 0;\n } else if (rows.length) {\n totalItems = Number(rows[0].count);\n } else {\n totalItems = 0;\n }\n\n if (isFetchingBackwards) {\n rows.reverse();\n }\n const hasMoreResults =\n limit > 0 && (isFetchingBackwards || rows.length > limit);\n\n // discard the extra item only when fetching forward.\n if (rows.length > limit) {\n rows.length -= 1;\n }\n\n const isInitialRequest = cursor.firstSortFieldValues === undefined;\n\n const firstRow = rows[0];\n const lastRow = rows[rows.length - 1];\n\n const firstSortFieldValues =\n cursor.firstSortFieldValues || sortFieldsFromRow(firstRow, sortField);\n\n const nextCursor: Cursor | undefined = hasMoreResults\n ? {\n ...cursor,\n orderFieldValues: sortFieldsFromRow(lastRow, sortField),\n firstSortFieldValues,\n isPrevious: false,\n totalItems,\n }\n : undefined;\n\n const prevCursor: Cursor | undefined =\n !isInitialRequest &&\n rows.length > 0 &&\n !isEqual(\n sortFieldsFromRow(firstRow, sortField),\n cursor.firstSortFieldValues,\n )\n ? {\n ...cursor,\n orderFieldValues: sortFieldsFromRow(firstRow, sortField),\n firstSortFieldValues: cursor.firstSortFieldValues,\n isPrevious: true,\n totalItems,\n }\n : undefined;\n\n return {\n items: processRawEntitiesResult(\n rows.map(r => r.final_entity!),\n request.fields,\n ),\n pageInfo: {\n ...(!!prevCursor && { prevCursor }),\n ...(!!nextCursor && { nextCursor }),\n },\n totalItems,\n };\n }\n\n async removeEntityByUid(uid: string): Promise<void> {\n const relationPeerRefs = await this.database.transaction(async tx => {\n const dbConfig = tx.client.config;\n\n // Clear the hashed state of the immediate parents of the deleted entity.\n // This makes sure that when they get reprocessed, their output is written\n // down again. The reason for wanting to do this, is that if the user\n // deletes entities that ARE still emitted by the parent, the parent\n // processing will still generate the same output hash as always, which\n // means it'll never try to write down the children again (it assumes that\n // they already exist). This means that without the code below, the database\n // never \"heals\" from accidental deletes.\n if (dbConfig.client.includes('mysql')) {\n // MySQL doesn't support the syntax we need to do this in a single query,\n // http://dev.mysql.com/doc/refman/5.6/en/update.html\n const results = await tx<DbRefreshStateRow>('refresh_state')\n .select('entity_id')\n .whereIn('entity_ref', function parents(builder) {\n return builder\n .from<DbRefreshStateRow>('refresh_state')\n .innerJoin<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n {\n 'refresh_state_references.target_entity_ref':\n 'refresh_state.entity_ref',\n },\n )\n .where('refresh_state.entity_id', '=', uid)\n .select('refresh_state_references.source_entity_ref');\n });\n await tx<DbRefreshStateRow>('refresh_state')\n .update({\n result_hash: 'child-was-deleted',\n next_update_at: tx.fn.now(),\n })\n .whereIn(\n 'entity_id',\n results.map(key => key.entity_id),\n );\n } else {\n await tx<DbRefreshStateRow>('refresh_state')\n .update({\n result_hash: 'child-was-deleted',\n next_update_at: tx.fn.now(),\n })\n .whereIn('entity_ref', function parents(builder) {\n return builder\n .from<DbRefreshStateRow>('refresh_state')\n .innerJoin<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n {\n 'refresh_state_references.target_entity_ref':\n 'refresh_state.entity_ref',\n },\n )\n .where('refresh_state.entity_id', '=', uid)\n .select('refresh_state_references.source_entity_ref');\n });\n }\n\n const relationPeers = await tx\n .from<DbRelationsRow>('relations')\n .innerJoin<DbRefreshStateRow>('refresh_state', {\n 'refresh_state.entity_ref': 'relations.target_entity_ref',\n })\n .where('relations.originating_entity_id', '=', uid)\n .andWhere('refresh_state.entity_id', '!=', uid)\n .select({ ref: 'relations.target_entity_ref' })\n .union(other =>\n other\n .from<DbRelationsRow>('relations')\n .innerJoin<DbRefreshStateRow>('refresh_state', {\n 'refresh_state.entity_ref': 'relations.source_entity_ref',\n })\n .where('relations.originating_entity_id', '=', uid)\n .andWhere('refresh_state.entity_id', '!=', uid)\n .select({ ref: 'relations.source_entity_ref' }),\n );\n\n await tx<DbRefreshStateRow>('refresh_state')\n .where('entity_id', uid)\n .delete();\n\n return new Set(relationPeers.map(p => p.ref));\n });\n\n if (relationPeerRefs.size > 0) {\n await this.stitcher.stitch({\n entityRefs: relationPeerRefs,\n });\n }\n }\n\n async entityAncestry(rootRef: string): Promise<EntityAncestryResponse> {\n const [rootRow] = await this.database<DbFinalEntitiesRow>('final_entities')\n .where('final_entities.entity_ref', '=', rootRef)\n .select({\n entityJson: 'final_entities.final_entity',\n });\n\n if (!rootRow) {\n throw new NotFoundError(`No such entity ${rootRef}`);\n }\n\n const rootEntity = JSON.parse(rootRow.entityJson) as Entity;\n const seenEntityRefs = new Set<string>();\n const todo = new Array<Entity>();\n const items = new Array<{ entity: Entity; parentEntityRefs: string[] }>();\n\n for (\n let current: Entity | undefined = rootEntity;\n current;\n current = todo.pop()\n ) {\n const currentRef = stringifyEntityRef(current);\n seenEntityRefs.add(currentRef);\n\n const parentRows = await this.database<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .innerJoin<DbFinalEntitiesRow>('final_entities', {\n 'refresh_state_references.source_entity_ref':\n 'final_entities.entity_ref',\n })\n .where('refresh_state_references.target_entity_ref', '=', currentRef)\n .select({\n parentEntityRef: 'final_entities.entity_ref',\n parentEntityJson: 'final_entities.final_entity',\n });\n\n const parentRefs: string[] = [];\n for (const { parentEntityRef, parentEntityJson } of parentRows) {\n parentRefs.push(parentEntityRef);\n if (!seenEntityRefs.has(parentEntityRef)) {\n seenEntityRefs.add(parentEntityRef);\n todo.push(JSON.parse(parentEntityJson));\n }\n }\n\n items.push({\n entity: current,\n parentEntityRefs: parentRefs,\n });\n }\n\n return {\n rootEntityRef: stringifyEntityRef(rootEntity),\n items,\n };\n }\n\n async facets(request: EntityFacetsRequest): Promise<EntityFacetsResponse> {\n const query = this.database<DbSearchRow>('search')\n .whereIn(\n 'search.key',\n request.facets.map(f => f.toLocaleLowerCase('en-US')),\n )\n .whereNotNull('search.original_value')\n .select({\n facet: 'search.key',\n value: 'search.original_value',\n count: this.database.raw('count(*)'),\n })\n .groupBy(['search.key', 'search.original_value'])\n .orderBy(['search.key', 'search.original_value']);\n\n if (request.filter || request.query) {\n // Build a subquery that finds matching entity IDs via\n // final_entities, so that the EXISTS-based filters correlate\n // against one-row-per-entity rather than the much larger search\n // table. The whereNotNull guard on final_entity excludes\n // not-yet-stitched (or future tombstoned) entities.\n const entityIdSubquery = this.database('final_entities')\n .select('final_entities.entity_id')\n .whereNotNull('final_entities.final_entity');\n\n applyEntityFilterToQuery({\n filter: request.filter,\n query: request.query,\n targetQuery: entityIdSubquery,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n\n // Use INNER JOIN rather than `WHERE search.entity_id IN (...)`. The\n // results are the same but the JOIN form gives the planner more\n // freedom in join shape and ordering. On PostgreSQL with large\n // search tables, the IN form tends to materialize the full filtered\n // entity set up front and spill to temp; the JOIN form lets the\n // planner pick a much cheaper plan based on actual selectivities.\n query.innerJoin(\n entityIdSubquery.as('filtered_entities'),\n 'search.entity_id',\n 'filtered_entities.entity_id',\n );\n }\n\n const rows = await query;\n\n const facets: EntityFacetsResponse['facets'] = {};\n for (const facet of request.facets) {\n const facetLowercase = facet.toLocaleLowerCase('en-US');\n facets[facet] = rows\n .filter(row => row.facet === facetLowercase)\n .map(row => ({\n value: String(row.value),\n count: Number(row.count),\n }));\n }\n\n return { facets };\n }\n}\n\nfunction parseCursorFromRequest(\n request?: QueryEntitiesRequest,\n): Partial<Cursor> & { skipTotalItems: boolean } {\n if (isQueryEntitiesInitialRequest(request)) {\n const {\n filter,\n query,\n orderFields: sortFields = [],\n fullTextFilter,\n skipTotalItems = false,\n } = request;\n return {\n filter,\n query,\n orderFields: sortFields,\n fullTextFilter,\n skipTotalItems,\n };\n }\n if (isQueryEntitiesCursorRequest(request)) {\n return {\n ...request.cursor,\n // Doesn't matter here\n skipTotalItems: false,\n };\n }\n return {\n skipTotalItems: false,\n };\n}\n\nfunction invertOrder(order: EntityOrder['order']) {\n return order === 'asc' ? 'desc' : 'asc';\n}\n\nfunction sortFieldsFromRow(\n row: DbSearchRow & DbFinalEntitiesRow,\n sortField?: EntityOrder | undefined,\n) {\n return sortField ? [row?.value, row?.entity_id] : [row?.entity_id];\n}\n"],"names":["InputError","applyEntityFilterToQuery","processRawEntitiesResult","expandLegacyCompoundRelationsInEntity","skip","lodashChunk","isQueryEntitiesInitialRequest","isEqual","NotFoundError","stringifyEntityRef","isQueryEntitiesCursorRequest"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"DefaultEntitiesCatalog.cjs.js","sources":["../../src/service/DefaultEntitiesCatalog.ts"],"sourcesContent":["/*\n * Copyright 2020 The Backstage Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { Entity, stringifyEntityRef } from '@backstage/catalog-model';\nimport { InputError, NotFoundError } from '@backstage/errors';\nimport { Knex } from 'knex';\nimport { chunk as lodashChunk, isEqual } from 'lodash';\nimport {\n Cursor,\n EntitiesBatchRequest,\n EntitiesBatchResponse,\n EntitiesCatalog,\n EntitiesRequest,\n EntitiesResponse,\n EntityAncestryResponse,\n EntityFacetsRequest,\n EntityFacetsResponse,\n EntityOrder,\n EntityPagination,\n QueryEntitiesRequest,\n QueryEntitiesResponse,\n} from '../catalog/types';\nimport {\n DbFinalEntitiesRow,\n DbPageInfo,\n DbRefreshStateReferencesRow,\n DbRefreshStateRow,\n DbRelationsRow,\n DbSearchRow,\n} from '../database/tables';\nimport { Stitcher } from '../stitching/types';\n\nimport {\n expandLegacyCompoundRelationsInEntity,\n isQueryEntitiesCursorRequest,\n isQueryEntitiesInitialRequest,\n} from './util';\nimport { LoggerService } from '@backstage/backend-plugin-api';\nimport { applyEntityFilterToQuery } from './request/applyEntityFilterToQuery';\nimport { processRawEntitiesResult } from './response';\n\nconst DEFAULT_LIMIT = 200;\n\nfunction parsePagination(input?: EntityPagination): EntityPagination {\n if (!input) {\n return {};\n }\n\n let { limit, offset } = input;\n\n if (input.after === undefined) {\n return { limit, offset };\n }\n\n let cursor;\n try {\n const json = Buffer.from(input.after, 'base64').toString('utf8');\n cursor = JSON.parse(json);\n } catch {\n throw new InputError('Malformed after cursor, could not be parsed');\n }\n\n if (cursor.limit !== undefined) {\n if (!Number.isInteger(cursor.limit)) {\n throw new InputError('Malformed after cursor, limit was not an number');\n }\n limit = cursor.limit;\n }\n\n if (cursor.offset !== undefined) {\n if (!Number.isInteger(cursor.offset)) {\n throw new InputError('Malformed after cursor, offset was not a number');\n }\n offset = cursor.offset;\n }\n\n return { limit, offset };\n}\n\nfunction stringifyPagination(\n input: Required<Omit<EntityPagination, 'after'>>,\n): string {\n const { limit, offset } = input;\n const json = JSON.stringify({ limit, offset });\n const base64 = Buffer.from(json, 'utf8').toString('base64');\n return base64;\n}\n\nexport class DefaultEntitiesCatalog implements EntitiesCatalog {\n private readonly database: Knex;\n private readonly logger: LoggerService;\n private readonly stitcher: Stitcher;\n private readonly enableRelationsCompatibility: boolean;\n\n constructor(options: {\n database: Knex;\n logger: LoggerService;\n stitcher: Stitcher;\n enableRelationsCompatibility?: boolean;\n }) {\n this.database = options.database;\n this.logger = options.logger;\n this.stitcher = options.stitcher;\n this.enableRelationsCompatibility = Boolean(\n options.enableRelationsCompatibility,\n );\n }\n\n async entities(request?: EntitiesRequest): Promise<EntitiesResponse> {\n const db = this.database;\n const { limit, offset } = parsePagination(request?.pagination);\n const primaryOrder = request?.order?.[0];\n\n // When exactly one order field is specified we run a two-phase fetch\n // that drives from the search-by-key index for that field. The index\n // walks rows in already-sorted order, so the planner can short-circuit\n // on LIMIT instead of having to materialise and sort the full filtered\n // set. Phase 2 appends entities that lack the order field (NULLS LAST)\n // and is skipped when phase 1 already fills the request.\n //\n // Multi-field ordering falls back to the original LEFT JOIN shape\n // because tie-breaking on a second field requires materialisation of\n // the full set anyway.\n const useFastPath = primaryOrder && (request?.order?.length ?? 0) <= 1;\n let rows: DbFinalEntitiesRow[];\n if (useFastPath) {\n rows = await this.runOrderedEntitiesQuery(\n request!,\n primaryOrder,\n limit,\n offset,\n );\n } else {\n let entitiesQuery =\n db<DbFinalEntitiesRow>('final_entities').select('final_entities.*');\n\n request?.order?.forEach(({ field }, index) => {\n const alias = `order_${index}`;\n entitiesQuery = entitiesQuery.leftOuterJoin(\n { [alias]: 'search' },\n function search(inner) {\n inner\n .on(`${alias}.entity_id`, 'final_entities.entity_id')\n .andOn(`${alias}.key`, db.raw('?', [field]));\n },\n );\n });\n\n entitiesQuery = entitiesQuery.whereNotNull('final_entities.final_entity');\n\n if (request?.filter) {\n entitiesQuery = applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: entitiesQuery,\n onEntityIdField: 'final_entities.entity_id',\n knex: db,\n });\n }\n\n if (request?.order) {\n request.order.forEach(({ order }, index) => {\n if (db.client.config.client === 'pg') {\n entitiesQuery = entitiesQuery.orderBy([\n { column: `order_${index}.value`, order, nulls: 'last' },\n ]);\n } else {\n entitiesQuery = entitiesQuery.orderBy([\n {\n column: `order_${index}.value`,\n order: undefined,\n nulls: 'last',\n },\n { column: `order_${index}.value`, order },\n ]);\n }\n });\n entitiesQuery.orderBy('final_entities.entity_id', 'asc');\n } else {\n entitiesQuery = entitiesQuery.orderBy(\n 'final_entities.entity_ref',\n 'asc',\n );\n }\n\n if (limit !== undefined) {\n entitiesQuery = entitiesQuery.limit(limit + 1);\n }\n if (offset !== undefined) {\n entitiesQuery = entitiesQuery.offset(offset);\n }\n\n rows = await entitiesQuery;\n }\n\n let pageInfo: DbPageInfo;\n if (limit === undefined || rows.length <= limit) {\n pageInfo = { hasNextPage: false };\n } else {\n rows = rows.slice(0, -1);\n pageInfo = {\n hasNextPage: true,\n endCursor: stringifyPagination({\n limit,\n offset: (offset ?? 0) + limit,\n }),\n };\n }\n\n return {\n entities: processRawEntitiesResult(\n rows.map(r => r.final_entity!),\n this.enableRelationsCompatibility\n ? e => {\n expandLegacyCompoundRelationsInEntity(e);\n if (request?.fields) {\n return request.fields(e);\n }\n return e;\n }\n : request?.fields,\n ),\n pageInfo,\n };\n }\n\n /**\n * Two-phase fetch used when the caller has specified an order field.\n * See entities() for a longer description of the rationale.\n */\n private async runOrderedEntitiesQuery(\n request: EntitiesRequest,\n primaryOrder: EntityOrder,\n limit: number | undefined,\n offset: number | undefined,\n ): Promise<DbFinalEntitiesRow[]> {\n const db = this.database;\n const isPg = db.client.config.client === 'pg';\n const wantedRows =\n limit === undefined ? Number.MAX_SAFE_INTEGER : (offset ?? 0) + limit + 1;\n\n const applyFilter = <T extends object>(\n query: Knex.QueryBuilder<T>,\n ): Knex.QueryBuilder<T> => {\n if (!request.filter) {\n return query;\n }\n return applyEntityFilterToQuery({\n filter: request.filter,\n targetQuery: query,\n onEntityIdField: 'final_entities.entity_id',\n knex: db,\n });\n };\n\n // Phase 1 -- entities that have a non-NULL value for the order field.\n // Rows where the key exists but value IS NULL (e.g. the entity field is\n // explicitly null, or exceeded MAX_VALUE_LENGTH in buildEntitySearch) are\n // excluded here so they fall through to Phase 2 and sort in the same\n // NULLS-LAST bucket as entities that have no row for the key at all —\n // preserving the semantics of the previous LEFT JOIN approach.\n let withField = db('search as order_0')\n .innerJoin(\n 'final_entities',\n 'final_entities.entity_id',\n 'order_0.entity_id',\n )\n .where('order_0.key', primaryOrder.field)\n .whereNotNull('order_0.value')\n .whereNotNull('final_entities.final_entity')\n .select<DbFinalEntitiesRow[]>('final_entities.*');\n withField = applyFilter(withField);\n withField = isPg\n ? withField.orderBy([\n { column: 'order_0.value', order: primaryOrder.order, nulls: 'last' },\n { column: 'final_entities.entity_id', order: 'asc' },\n ])\n : withField.orderBy([\n { column: 'order_0.value', order: undefined, nulls: 'last' },\n { column: 'order_0.value', order: primaryOrder.order },\n { column: 'final_entities.entity_id', order: 'asc' },\n ]);\n if (wantedRows < Number.MAX_SAFE_INTEGER) {\n withField = withField.limit(wantedRows);\n }\n const withFieldRows = await withField;\n\n // If phase 1 already covered everything we asked for, skip the second\n // phase entirely. This is the common UI case where every entity in the\n // filtered set has the order field.\n if (withFieldRows.length >= wantedRows) {\n const skip = offset ?? 0;\n return withFieldRows.slice(skip, skip + (limit ?? wantedRows) + 1);\n }\n\n // Phase 2 -- entities that lack the order field, appended after.\n let withoutField = db<DbFinalEntitiesRow>('final_entities')\n .select<DbFinalEntitiesRow[]>('final_entities.*')\n .whereNotNull('final_entities.final_entity')\n .whereNotExists(qb =>\n qb\n .from('search')\n .where('search.entity_id', db.ref('final_entities.entity_id'))\n .andWhere('search.key', primaryOrder.field)\n .whereNotNull('search.value'),\n );\n withoutField = applyFilter(withoutField);\n withoutField = withoutField.orderBy(\n 'final_entities.entity_id',\n 'asc', // NULL group always stable-sorted ASC regardless of primary direction\n );\n if (limit !== undefined) {\n // Phase 2 only contributes the rows that phase 1 didn't cover.\n const remaining =\n wantedRows - Math.min(withFieldRows.length, (offset ?? 0) + limit + 1);\n withoutField = withoutField.limit(Math.max(0, remaining));\n }\n const withoutFieldRows = await withoutField;\n\n const combined = [...withFieldRows, ...withoutFieldRows];\n if (limit === undefined) {\n return combined.slice(offset ?? 0);\n }\n const skip = offset ?? 0;\n return combined.slice(skip, skip + limit + 1);\n }\n\n async entitiesBatch(\n request: EntitiesBatchRequest,\n ): Promise<EntitiesBatchResponse> {\n const lookup = new Map<string, string>();\n\n for (const chunk of lodashChunk(request.entityRefs, 200)) {\n let query = this.database<DbFinalEntitiesRow>('final_entities')\n .select({\n entityRef: 'final_entities.entity_ref',\n entity: 'final_entities.final_entity',\n })\n .whereIn('final_entities.entity_ref', chunk);\n\n if (request?.filter || request?.query) {\n query = applyEntityFilterToQuery({\n filter: request.filter,\n query: request.query,\n targetQuery: query,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n }\n\n for (const row of await query) {\n lookup.set(row.entityRef, row.entity ? row.entity : null);\n }\n }\n\n const items = request.entityRefs.map(ref => lookup.get(ref) ?? null);\n\n return { items: processRawEntitiesResult(items, request.fields) };\n }\n\n async queryEntities(\n request: QueryEntitiesRequest,\n ): Promise<QueryEntitiesResponse> {\n const limit = request.limit ?? DEFAULT_LIMIT;\n\n const cursor: Omit<Cursor, 'orderFieldValues'> & {\n orderFieldValues?: (string | null)[];\n skipTotalItems: boolean;\n } = {\n orderFields: [],\n isPrevious: false,\n ...parseCursorFromRequest(request),\n };\n\n // For performance reasons we invoke the count query only on the first\n // request. The result is then embedded into the cursor for subsequent\n // requests. Therefore this can be undefined here, but will then get\n // populated further down.\n const shouldComputeTotalItems =\n cursor.totalItems === undefined && !cursor.skipTotalItems;\n const isFetchingBackwards = cursor.isPrevious;\n\n if (cursor.orderFields.length > 1) {\n this.logger.warn(`Only one sort field is supported, ignoring the rest`);\n }\n\n const sortField = cursor.orderFields.at(0);\n\n const normalizedFullTextFilterTerm = cursor.fullTextFilter?.term?.trim();\n const textFilterFields = cursor.fullTextFilter?.fields ?? [\n sortField?.field || 'metadata.uid',\n ];\n\n // Shared predicate logic applied to both the list CTE and the\n // standalone count query so they stay in sync. The `searchInScope`\n // flag indicates whether a `search` table is already joined in the\n // target query (true for the list CTE when a sort field is set),\n // enabling a fast-path LIKE on the already-joined row.\n const applyPredicates = (\n q: Knex.QueryBuilder,\n options?: { searchInScope?: boolean },\n ) => {\n if (cursor.filter || cursor.query) {\n applyEntityFilterToQuery({\n filter: cursor.filter,\n query: cursor.query,\n targetQuery: q,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n }\n\n if (normalizedFullTextFilterTerm) {\n if (\n options?.searchInScope &&\n textFilterFields.length === 1 &&\n textFilterFields[0] === sortField?.field\n ) {\n q.andWhereRaw(\n 'search.value like ?',\n `%${normalizedFullTextFilterTerm.toLocaleLowerCase('en-US')}%`,\n );\n } else {\n const matchQuery = this.database<DbSearchRow>('search')\n .select('search.entity_id')\n .whereIn(\n 'search.key',\n textFilterFields.map(field => field.toLocaleLowerCase('en-US')),\n )\n .andWhere(function keyFilter() {\n this.andWhereRaw(\n 'search.value like ?',\n `%${normalizedFullTextFilterTerm.toLocaleLowerCase('en-US')}%`,\n );\n });\n q.andWhere('final_entities.entity_id', 'in', matchQuery);\n }\n }\n };\n\n // The list CTE. When a sort field is specified, the search table for\n // that key drives the query via INNER JOIN so that the covering index\n // walks rows in sort order, letting LIMIT short-circuit. Entities\n // that lack the sort field are excluded — this aligns totalItems with\n // the set reachable through cursor pagination.\n const dbQuery = this.database.with(\n 'filtered',\n ['entity_id', 'final_entity', ...(sortField ? ['value'] : [])],\n inner => {\n if (sortField) {\n inner\n .from('search')\n .innerJoin(\n 'final_entities',\n 'final_entities.entity_id',\n 'search.entity_id',\n )\n .where('search.key', sortField.field)\n .whereNotNull('final_entities.final_entity')\n .select({\n entity_id: 'final_entities.entity_id',\n final_entity: 'final_entities.final_entity',\n value: 'search.value',\n });\n } else {\n inner\n .from<DbFinalEntitiesRow>('final_entities')\n .whereNotNull('final_entity')\n .select({\n entity_id: 'final_entities.entity_id',\n final_entity: 'final_entities.final_entity',\n });\n }\n\n applyPredicates(inner, { searchInScope: !!sortField });\n },\n );\n\n // The list query references the CTE exactly once, allowing Postgres\n // 12+ to inline it and short-circuit on LIMIT.\n dbQuery.from('filtered').select('*');\n\n // Standalone count query — runs concurrently with the list so the\n // CTE stays single-referenced and inlineable.\n let countQuery: Knex.QueryBuilder | undefined;\n if (shouldComputeTotalItems) {\n countQuery = this.database('final_entities')\n .whereNotNull('final_entities.final_entity')\n .count('*', { as: 'count' });\n\n if (sortField) {\n countQuery.whereExists(\n this.database('search')\n .select(this.database.raw(1))\n .whereRaw('search.entity_id = final_entities.entity_id')\n .where('search.key', sortField.field)\n .whereNotNull('search.value'),\n );\n }\n\n applyPredicates(countQuery);\n }\n\n const isOrderingDescending = sortField?.order === 'desc';\n\n // Move forward (or backward) in the set to the correct cursor position\n if (cursor.orderFieldValues) {\n if (cursor.orderFieldValues.length === 2) {\n // The first will be the sortField value, the second the entity_id\n const [first, second] = cursor.orderFieldValues;\n dbQuery.andWhere(function nested() {\n this.where(\n 'filtered.value',\n isFetchingBackwards !== isOrderingDescending ? '<' : '>',\n first,\n )\n .orWhere('filtered.value', '=', first)\n .andWhere(\n 'filtered.entity_id',\n isFetchingBackwards !== isOrderingDescending ? '<' : '>',\n second,\n );\n });\n } else if (cursor.orderFieldValues.length === 1) {\n // This will be the entity_id\n const [first] = cursor.orderFieldValues;\n dbQuery.andWhere('entity_id', isFetchingBackwards ? '<' : '>', first);\n }\n }\n\n // Add the ordering\n let order = sortField?.order ?? 'asc';\n if (isFetchingBackwards) {\n order = invertOrder(order);\n }\n if (this.database.client.config.client === 'pg') {\n // pg correctly orders by the column value and handling nulls in one go\n dbQuery.orderBy([\n ...(sortField\n ? [\n {\n column: 'filtered.value',\n order,\n nulls: 'last',\n },\n ]\n : []),\n {\n column: 'filtered.entity_id',\n order,\n },\n ]);\n } else {\n // sqlite and mysql translate the above statement ONLY into \"order by (value is null) asc\"\n // no matter what the order is, for some reason, so we have to manually add back the statement\n // that translates to \"order by value <order>\" while avoiding to give an order\n dbQuery.orderBy([\n ...(sortField\n ? [\n {\n column: 'filtered.value',\n order: undefined,\n nulls: 'last',\n },\n {\n column: 'filtered.value',\n order,\n },\n ]\n : []),\n {\n column: 'filtered.entity_id',\n order,\n },\n ]);\n }\n\n // Apply a manually set initial offset\n if (\n isQueryEntitiesInitialRequest(request) &&\n request.offset !== undefined\n ) {\n dbQuery.offset(request.offset);\n }\n // fetch an extra item to check if there are more items.\n dbQuery.limit(isFetchingBackwards ? limit : limit + 1);\n\n // Run list and count queries concurrently\n const [rows, countResult] = await Promise.all([\n limit > 0 ? dbQuery : Promise.resolve([]),\n countQuery ?? Promise.resolve(undefined),\n ]);\n\n let totalItems: number;\n if (cursor.totalItems !== undefined) {\n totalItems = cursor.totalItems;\n } else if (cursor.skipTotalItems) {\n totalItems = 0;\n } else if (countResult?.[0]) {\n totalItems = Number(countResult[0].count);\n } else {\n totalItems = 0;\n }\n\n if (isFetchingBackwards) {\n rows.reverse();\n }\n const hasMoreResults =\n limit > 0 && (isFetchingBackwards || rows.length > limit);\n\n // discard the extra item only when fetching forward.\n if (rows.length > limit) {\n rows.length -= 1;\n }\n\n const isInitialRequest = cursor.firstSortFieldValues === undefined;\n\n const firstRow = rows[0];\n const lastRow = rows[rows.length - 1];\n\n const firstSortFieldValues =\n cursor.firstSortFieldValues || sortFieldsFromRow(firstRow, sortField);\n\n const nextCursor: Cursor | undefined = hasMoreResults\n ? {\n ...cursor,\n orderFieldValues: sortFieldsFromRow(lastRow, sortField),\n firstSortFieldValues,\n isPrevious: false,\n totalItems,\n }\n : undefined;\n\n const prevCursor: Cursor | undefined =\n !isInitialRequest &&\n rows.length > 0 &&\n !isEqual(\n sortFieldsFromRow(firstRow, sortField),\n cursor.firstSortFieldValues,\n )\n ? {\n ...cursor,\n orderFieldValues: sortFieldsFromRow(firstRow, sortField),\n firstSortFieldValues: cursor.firstSortFieldValues,\n isPrevious: true,\n totalItems,\n }\n : undefined;\n\n return {\n items: processRawEntitiesResult(\n rows.map(r => r.final_entity!),\n request.fields,\n ),\n pageInfo: {\n ...(!!prevCursor && { prevCursor }),\n ...(!!nextCursor && { nextCursor }),\n },\n totalItems,\n };\n }\n\n async removeEntityByUid(uid: string): Promise<void> {\n const relationPeerRefs = await this.database.transaction(async tx => {\n const dbConfig = tx.client.config;\n\n // Clear the hashed state of the immediate parents of the deleted entity.\n // This makes sure that when they get reprocessed, their output is written\n // down again. The reason for wanting to do this, is that if the user\n // deletes entities that ARE still emitted by the parent, the parent\n // processing will still generate the same output hash as always, which\n // means it'll never try to write down the children again (it assumes that\n // they already exist). This means that without the code below, the database\n // never \"heals\" from accidental deletes.\n if (dbConfig.client.includes('mysql')) {\n // MySQL doesn't support the syntax we need to do this in a single query,\n // http://dev.mysql.com/doc/refman/5.6/en/update.html\n const results = await tx<DbRefreshStateRow>('refresh_state')\n .select('entity_id')\n .whereIn('entity_ref', function parents(builder) {\n return builder\n .from<DbRefreshStateRow>('refresh_state')\n .innerJoin<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n {\n 'refresh_state_references.target_entity_ref':\n 'refresh_state.entity_ref',\n },\n )\n .where('refresh_state.entity_id', '=', uid)\n .select('refresh_state_references.source_entity_ref');\n });\n await tx<DbRefreshStateRow>('refresh_state')\n .update({\n result_hash: 'child-was-deleted',\n next_update_at: tx.fn.now(),\n })\n .whereIn(\n 'entity_id',\n results.map(key => key.entity_id),\n );\n } else {\n await tx<DbRefreshStateRow>('refresh_state')\n .update({\n result_hash: 'child-was-deleted',\n next_update_at: tx.fn.now(),\n })\n .whereIn('entity_ref', function parents(builder) {\n return builder\n .from<DbRefreshStateRow>('refresh_state')\n .innerJoin<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n {\n 'refresh_state_references.target_entity_ref':\n 'refresh_state.entity_ref',\n },\n )\n .where('refresh_state.entity_id', '=', uid)\n .select('refresh_state_references.source_entity_ref');\n });\n }\n\n const relationPeers = await tx\n .from<DbRelationsRow>('relations')\n .innerJoin<DbRefreshStateRow>('refresh_state', {\n 'refresh_state.entity_ref': 'relations.target_entity_ref',\n })\n .where('relations.originating_entity_id', '=', uid)\n .andWhere('refresh_state.entity_id', '!=', uid)\n .select({ ref: 'relations.target_entity_ref' })\n .union(other =>\n other\n .from<DbRelationsRow>('relations')\n .innerJoin<DbRefreshStateRow>('refresh_state', {\n 'refresh_state.entity_ref': 'relations.source_entity_ref',\n })\n .where('relations.originating_entity_id', '=', uid)\n .andWhere('refresh_state.entity_id', '!=', uid)\n .select({ ref: 'relations.source_entity_ref' }),\n );\n\n await tx<DbRefreshStateRow>('refresh_state')\n .where('entity_id', uid)\n .delete();\n\n return new Set(relationPeers.map(p => p.ref));\n });\n\n if (relationPeerRefs.size > 0) {\n await this.stitcher.stitch({\n entityRefs: relationPeerRefs,\n });\n }\n }\n\n async entityAncestry(rootRef: string): Promise<EntityAncestryResponse> {\n const [rootRow] = await this.database<DbFinalEntitiesRow>('final_entities')\n .where('final_entities.entity_ref', '=', rootRef)\n .select({\n entityJson: 'final_entities.final_entity',\n });\n\n if (!rootRow) {\n throw new NotFoundError(`No such entity ${rootRef}`);\n }\n\n const rootEntity = JSON.parse(rootRow.entityJson) as Entity;\n const seenEntityRefs = new Set<string>();\n const todo = new Array<Entity>();\n const items = new Array<{ entity: Entity; parentEntityRefs: string[] }>();\n\n for (\n let current: Entity | undefined = rootEntity;\n current;\n current = todo.pop()\n ) {\n const currentRef = stringifyEntityRef(current);\n seenEntityRefs.add(currentRef);\n\n const parentRows = await this.database<DbRefreshStateReferencesRow>(\n 'refresh_state_references',\n )\n .innerJoin<DbFinalEntitiesRow>('final_entities', {\n 'refresh_state_references.source_entity_ref':\n 'final_entities.entity_ref',\n })\n .where('refresh_state_references.target_entity_ref', '=', currentRef)\n .select({\n parentEntityRef: 'final_entities.entity_ref',\n parentEntityJson: 'final_entities.final_entity',\n });\n\n const parentRefs: string[] = [];\n for (const { parentEntityRef, parentEntityJson } of parentRows) {\n parentRefs.push(parentEntityRef);\n if (!seenEntityRefs.has(parentEntityRef)) {\n seenEntityRefs.add(parentEntityRef);\n todo.push(JSON.parse(parentEntityJson));\n }\n }\n\n items.push({\n entity: current,\n parentEntityRefs: parentRefs,\n });\n }\n\n return {\n rootEntityRef: stringifyEntityRef(rootEntity),\n items,\n };\n }\n\n async facets(request: EntityFacetsRequest): Promise<EntityFacetsResponse> {\n const query = this.database<DbSearchRow>('search')\n .whereIn(\n 'search.key',\n request.facets.map(f => f.toLocaleLowerCase('en-US')),\n )\n .whereNotNull('search.original_value')\n .select({\n facet: 'search.key',\n value: 'search.original_value',\n count: this.database.raw('count(*)'),\n })\n .groupBy(['search.key', 'search.original_value'])\n .orderBy(['search.key', 'search.original_value']);\n\n if (request.filter || request.query) {\n // Build a subquery that finds matching entity IDs via\n // final_entities, so that the EXISTS-based filters correlate\n // against one-row-per-entity rather than the much larger search\n // table. The whereNotNull guard on final_entity excludes\n // not-yet-stitched (or future tombstoned) entities.\n const entityIdSubquery = this.database('final_entities')\n .select('final_entities.entity_id')\n .whereNotNull('final_entities.final_entity');\n\n applyEntityFilterToQuery({\n filter: request.filter,\n query: request.query,\n targetQuery: entityIdSubquery,\n onEntityIdField: 'final_entities.entity_id',\n knex: this.database,\n });\n\n // Use INNER JOIN rather than `WHERE search.entity_id IN (...)`. The\n // results are the same but the JOIN form gives the planner more\n // freedom in join shape and ordering. On PostgreSQL with large\n // search tables, the IN form tends to materialize the full filtered\n // entity set up front and spill to temp; the JOIN form lets the\n // planner pick a much cheaper plan based on actual selectivities.\n query.innerJoin(\n entityIdSubquery.as('filtered_entities'),\n 'search.entity_id',\n 'filtered_entities.entity_id',\n );\n }\n\n const rows = await query;\n\n const facets: EntityFacetsResponse['facets'] = {};\n for (const facet of request.facets) {\n const facetLowercase = facet.toLocaleLowerCase('en-US');\n facets[facet] = rows\n .filter(row => row.facet === facetLowercase)\n .map(row => ({\n value: String(row.value),\n count: Number(row.count),\n }));\n }\n\n return { facets };\n }\n}\n\nfunction parseCursorFromRequest(\n request?: QueryEntitiesRequest,\n): Partial<Cursor> & { skipTotalItems: boolean } {\n if (isQueryEntitiesInitialRequest(request)) {\n const {\n filter,\n query,\n orderFields: sortFields = [],\n fullTextFilter,\n skipTotalItems = false,\n } = request;\n return {\n filter,\n query,\n orderFields: sortFields,\n fullTextFilter,\n skipTotalItems,\n };\n }\n if (isQueryEntitiesCursorRequest(request)) {\n return {\n ...request.cursor,\n // Doesn't matter here\n skipTotalItems: false,\n };\n }\n return {\n skipTotalItems: false,\n };\n}\n\nfunction invertOrder(order: EntityOrder['order']) {\n return order === 'asc' ? 'desc' : 'asc';\n}\n\nfunction sortFieldsFromRow(\n row: DbSearchRow & DbFinalEntitiesRow,\n sortField?: EntityOrder | undefined,\n) {\n return sortField ? [row?.value, row?.entity_id] : [row?.entity_id];\n}\n"],"names":["InputError","applyEntityFilterToQuery","processRawEntitiesResult","expandLegacyCompoundRelationsInEntity","skip","lodashChunk","isQueryEntitiesInitialRequest","isEqual","NotFoundError","stringifyEntityRef","isQueryEntitiesCursorRequest"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@backstage/plugin-catalog-backend",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.1",
|
|
4
4
|
"description": "The Backstage backend plugin that provides the Backstage catalog",
|
|
5
5
|
"backstage": {
|
|
6
6
|
"role": "backend-plugin",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
"dependencies": {
|
|
79
79
|
"@backstage/backend-openapi-utils": "^0.6.9",
|
|
80
80
|
"@backstage/backend-plugin-api": "^1.9.1",
|
|
81
|
-
"@backstage/catalog-client": "^1.15.
|
|
81
|
+
"@backstage/catalog-client": "^1.15.2",
|
|
82
82
|
"@backstage/catalog-model": "^1.9.0",
|
|
83
83
|
"@backstage/config": "^1.3.8",
|
|
84
84
|
"@backstage/errors": "^1.3.1",
|
|
@@ -112,7 +112,7 @@
|
|
|
112
112
|
"zod-validation-error": "^4.0.2"
|
|
113
113
|
},
|
|
114
114
|
"devDependencies": {
|
|
115
|
-
"@backstage/backend-defaults": "^0.17.
|
|
115
|
+
"@backstage/backend-defaults": "^0.17.2",
|
|
116
116
|
"@backstage/backend-test-utils": "^1.11.3",
|
|
117
117
|
"@backstage/cli": "^0.36.2",
|
|
118
118
|
"@backstage/plugin-catalog-backend-module-logs": "^0.1.22",
|