feat(30-01): add popular-items-by-tags query to discovery service
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,14 @@
|
|||||||
import { and, count, desc, eq, isNotNull, lt, sql } from "drizzle-orm";
|
import { and, count, desc, eq, inArray, isNotNull, lt, sql } from "drizzle-orm";
|
||||||
import { db as prodDb } from "../../db/index.ts";
|
import { db as prodDb } from "../../db/index.ts";
|
||||||
import { globalItems, setupItems, setups, users } from "../../db/schema.ts";
|
import {
|
||||||
|
globalItemTags,
|
||||||
|
globalItems,
|
||||||
|
items,
|
||||||
|
setupItems,
|
||||||
|
setups,
|
||||||
|
tags,
|
||||||
|
users,
|
||||||
|
} from "../../db/schema.ts";
|
||||||
|
|
||||||
type Db = typeof prodDb;
|
type Db = typeof prodDb;
|
||||||
|
|
||||||
@@ -125,3 +133,53 @@ export async function getTrendingCategories(
|
|||||||
itemCount: r.itemCount,
|
itemCount: r.itemCount,
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get popular global items filtered by tag names, ordered by owner count descending.
|
||||||
|
* Owner count = number of user items linked to each global item via globalItemId.
|
||||||
|
*/
|
||||||
|
export async function getPopularItemsByTags(
|
||||||
|
db: Db = prodDb,
|
||||||
|
tagNames: string[],
|
||||||
|
limit = 24,
|
||||||
|
): Promise<
|
||||||
|
Array<{
|
||||||
|
id: number;
|
||||||
|
brand: string | null;
|
||||||
|
model: string;
|
||||||
|
category: string | null;
|
||||||
|
weightGrams: number | null;
|
||||||
|
priceCents: number | null;
|
||||||
|
imageFilename: string | null;
|
||||||
|
description: string | null;
|
||||||
|
ownerCount: number;
|
||||||
|
}>
|
||||||
|
> {
|
||||||
|
if (tagNames.length === 0) return [];
|
||||||
|
|
||||||
|
const rows = await db
|
||||||
|
.select({
|
||||||
|
id: globalItems.id,
|
||||||
|
brand: globalItems.brand,
|
||||||
|
model: globalItems.model,
|
||||||
|
category: globalItems.category,
|
||||||
|
weightGrams: globalItems.weightGrams,
|
||||||
|
priceCents: globalItems.priceCents,
|
||||||
|
imageFilename: globalItems.imageFilename,
|
||||||
|
description: globalItems.description,
|
||||||
|
ownerCount: sql<number>`CAST(COUNT(DISTINCT ${items.id}) AS INT)`,
|
||||||
|
})
|
||||||
|
.from(globalItems)
|
||||||
|
.innerJoin(globalItemTags, eq(globalItemTags.globalItemId, globalItems.id))
|
||||||
|
.innerJoin(tags, eq(tags.id, globalItemTags.tagId))
|
||||||
|
.leftJoin(items, eq(items.globalItemId, globalItems.id))
|
||||||
|
.where(inArray(tags.name, tagNames))
|
||||||
|
.groupBy(globalItems.id)
|
||||||
|
.orderBy(
|
||||||
|
desc(sql<number>`COUNT(DISTINCT ${items.id})`),
|
||||||
|
desc(globalItems.id),
|
||||||
|
)
|
||||||
|
.limit(limit);
|
||||||
|
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user