fix(CQ-API-7): Fix N+1 query in knowledge tag lookup
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful

Replace Promise.all of individual findUnique queries per tag with a
single findMany batch query. Only missing tags are created individually.
Tag associations now use createMany instead of individual creates.
Also deduplicates tags by slug via Map, preventing duplicate entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Jason Woltje
2026-02-06 13:56:39 -06:00
parent d9efa85924
commit 6dd2ce1014
2 changed files with 385 additions and 29 deletions

View File

@@ -821,45 +821,48 @@ export class KnowledgeService {
return;
}
// Get or create tags
const tags = await Promise.all(
tagNames.map(async (name) => {
const tagSlug = this.generateSlug(name);
// Build slug map: slug -> original tag name
const slugToName = new Map<string, string>();
for (const name of tagNames) {
slugToName.set(this.generateSlug(name), name);
}
const tagSlugs = [...slugToName.keys()];
// Try to find existing tag
let tag = await tx.knowledgeTag.findUnique({
where: {
workspaceId_slug: {
workspaceId,
slug: tagSlug,
},
},
});
// Batch fetch all existing tags in a single query (fixes N+1)
const existingTags = await tx.knowledgeTag.findMany({
where: {
workspaceId,
slug: { in: tagSlugs },
},
});
// Create if doesn't exist
tag ??= await tx.knowledgeTag.create({
// Determine which tags need to be created
const existingSlugs = new Set(existingTags.map((t) => t.slug));
const missingSlugs = tagSlugs.filter((s) => !existingSlugs.has(s));
// Create missing tags
const newTags = await Promise.all(
missingSlugs.map((slug) => {
const name = slugToName.get(slug) ?? slug;
return tx.knowledgeTag.create({
data: {
workspaceId,
name,
slug: tagSlug,
slug,
},
});
return tag;
})
);
// Create tag associations
await Promise.all(
tags.map((tag) =>
tx.knowledgeEntryTag.create({
data: {
entryId,
tagId: tag.id,
},
})
)
);
const allTags = [...existingTags, ...newTags];
// Create tag associations in a single batch
await tx.knowledgeEntryTag.createMany({
data: allTags.map((tag) => ({
entryId,
tagId: tag.id,
})),
});
}
/**