From e453f3bf5ba223ec696f5ae71f169f2699b67510 Mon Sep 17 00:00:00 2001 From: Kuchizu Date: Wed, 6 May 2026 19:17:50 +0300 Subject: [PATCH 1/3] perf(api): apply $limit before $lookup in dailyEvents pipeline when no content filters --- src/models/eventsFactory.js | 39 +++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/models/eventsFactory.js b/src/models/eventsFactory.js index c552bc41..9424440d 100644 --- a/src/models/eventsFactory.js +++ b/src/models/eventsFactory.js @@ -403,6 +403,17 @@ class EventsFactory extends Factory { return { 'event.assignee': String(assignee) }; })(); + /** When false, $limit can move before the $lookups. */ + const hasContentFilters = + escapedSearch.length > 0 || + Boolean(release) || + Boolean(assignee) || + Object.keys(matchFilter).length > 0; + + if (!hasContentFilters) { + pipeline.push({ $limit: limit + 1 }); + } + pipeline.push( /** * Left outer join original event on groupHash field @@ -434,21 +445,25 @@ class EventsFactory extends Factory { path: '$repetition', preserveNullAndEmptyArrays: true, }, - }, - { - $match: { - ...matchFilter, - ...searchFilter, - ...releaseFilter, - ...assigneeFilter, - }, - }, - { $limit: limit + 1 }, - { - $unset: 'groupHash', } ); + if (hasContentFilters) { + pipeline.push( + { + $match: { + ...matchFilter, + ...searchFilter, + ...releaseFilter, + ...assigneeFilter, + }, + }, + { $limit: limit + 1 } + ); + } + + pipeline.push({ $unset: 'groupHash' }); + const cursor = this.getCollection(this.TYPES.DAILY_EVENTS).aggregate(pipeline); const result = await cursor.toArray(); From 8fd1cab51a0f8b08858647b83145ae4e053cbde3 Mon Sep 17 00:00:00 2001 From: Kuchizu Date: Wed, 6 May 2026 19:17:50 +0300 Subject: [PATCH 2/3] perf(api): support optional projection in DataLoader batch fns --- src/dataLoaders.ts | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/dataLoaders.ts b/src/dataLoaders.ts index 377bd702..cb5c0a2e 100644 --- a/src/dataLoaders.ts +++ b/src/dataLoaders.ts @@ -68,12 +68,19 @@ export default class DataLoaders { * Batching function for resolving entities from their ids * @param collectionName - collection name to get entities * @param ids - ids for resolving + * @param projection - optional mongo projection to limit returned fields */ private async batchByIds( collectionName: string, - ids: ReadonlyArray + ids: ReadonlyArray, + projection?: Record ): Promise<(WithId | null)[]> { - return this.batchByField(collectionName, '_id', ids.map(id => new ObjectId(id))); + return this.batchByField( + collectionName, + '_id', + ids.map(id => new ObjectId(id)), + projection + ); } /** @@ -81,6 +88,7 @@ export default class DataLoaders { * @param collectionName - collection name to get entities * @param fieldName - field name to resolve * @param values - values for resolving + * @param projection - optional mongo projection to limit returned fields */ private async batchByField< T extends Record, @@ -88,7 +96,8 @@ export default class DataLoaders { >( collectionName: string, fieldName: FieldType, - values: ReadonlyArray + values: ReadonlyArray, + projection?: Record ): Promise<(WithId | null)[]> { type Doc = WithId; const valuesMap = new Map(); @@ -99,9 +108,10 @@ export default class DataLoaders { const queryResult = await this.dbConnection .collection(collectionName) - .find({ - [fieldName]: { $in: Array.from(valuesMap.values()) }, - } as any) + .find( + { [fieldName]: { $in: Array.from(valuesMap.values()) } } as any, + projection ? { projection } : {} + ) .toArray(); /** From 16b0e23cd0b6fec275c7b9c2665ebdff99e0e2bd Mon Sep 17 00:00:00 2001 From: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 16:22:06 +0000 Subject: [PATCH 3/3] Bump version up to 1.5.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b9aaa8e8..d484a908 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hawk.api", - "version": "1.5.0", + "version": "1.5.1", "main": "index.ts", "license": "BUSL-1.1", "scripts": {