Template recommendation implemented

This commit is contained in:
adilallo
2026-04-29 19:24:50 -06:00
parent c4c74ecdb4
commit a4f0c4bf27
20 changed files with 899 additions and 82 deletions
+69
View File
@@ -169,3 +169,72 @@ export async function scoreTemplatesByFacets(args: {
return null;
}
}
/**
* Slugs that have at least one `TemplateFacet` row (Template Composition
* matrix, cols GY) — use {@link scoreTemplatesByTemplateFacets} for these;
* others use {@link scoreTemplatesByFacets}.
*/
export async function getTemplateFacetSlugSet(): Promise<Set<string> | null> {
if (!isDatabaseConfigured()) return null;
try {
const rows = await prisma.templateFacet.findMany({
where: { matches: true },
distinct: ["templateSlug"],
select: { templateSlug: true },
});
return new Set(rows.map((r) => r.templateSlug));
} catch {
return null;
}
}
/**
* Per-template score from the `TemplateFacet` table: one point per
* user-requested `(group, value)` that exists for that `templateSlug`.
* Same counting semantics as the pre-DB matrix JSON path.
*/
export async function scoreTemplatesByTemplateFacets(args: {
templateSlugs: ReadonlyArray<string>;
facets: RequestedFacets;
}): Promise<TemplateRanking[] | null> {
if (!isDatabaseConfigured()) return null;
const requested = flattenRequestedFacets(args.facets);
if (requested.length === 0) {
return args.templateSlugs.map((templateSlug) => ({
templateSlug,
score: 0,
matchedFacets: [] as string[],
}));
}
if (args.templateSlugs.length === 0) {
return [];
}
try {
const rows = await prisma.templateFacet.findMany({
where: {
matches: true,
templateSlug: { in: [...args.templateSlugs] },
OR: requested.map(({ group, value }) => ({ group, value })),
},
select: { templateSlug: true, group: true, value: true },
});
const pairSet = new Set(
rows.map((r) => `${r.templateSlug}\0${r.group}\0${r.value}` as const),
);
return args.templateSlugs.map((templateSlug) => {
const matched: string[] = [];
for (const { group, value } of requested) {
if (pairSet.has(`${templateSlug}\0${group}\0${value}`)) {
matched.push(`${group}:${value}`);
}
}
return { templateSlug, score: matched.length, matchedFacets: matched };
});
} catch {
return null;
}
}