// convex/utils/enrichCardsWithCollectionStatus.ts
import { Doc, Id } from '../_generated/dataModel';
import { QueryCtx } from '../_generated/server';
import { getAuthUserInternal } from '../auth';
import { getLatestCardPricing } from './getCardPricing';
export type CardWithCollectionStatus = Doc<'pokemonCards'> & {
collectionStatus?: {
isOwned: boolean;
ownedCard?: Doc<'userCards'>;
collections: Array<Doc<'userCollections'>>;
};
};
export async function enrichPokemonCards(
ctx: QueryCtx,
cards: Array<Doc<'pokemonCards'>>
): Promise<CardWithCollectionStatus[]> {
// pricing enrichment
const prices = await getLatestCardPricing(ctx.db, cards);
// collection enrichment
const auth = await getAuthUserInternal(ctx);
if (auth.isErr) {
return prices.map((card) => ({ ...card, collectionStatus: undefined }));
}
if (!auth.value?._id) {
return prices.map((card) => ({ ...card, collectionStatus: undefined }));
}
const authUserId = auth.value._id;
// Get all user cards for these specific cards in a single query
const cardIds = prices.map((card) => card._id);
const userCards = await ctx.db
.query('userCards')
.withIndex('by_userId_cardId', (q) => q.eq('userId', authUserId))
.filter((q) =>
q.or(...cardIds.map((cardId) => q.eq(q.field('cardId'), cardId)))
)
.collect();
// Get all junction table entries for the user's cards in a single query
const userCardIds = userCards.map((uc) => uc._id);
const junctions =
userCardIds.length > 0
? await ctx.db
.query('userCardCollections')
.withIndex('by_userCardId_userCollectionId')
.filter((q) =>
q.or(
...userCardIds.map((userCardId) =>
q.eq(q.field('userCardId'), userCardId)
)
)
)
.collect()
: [];
// Get all collections referenced in junctions
const collectionIds = [...new Set(junctions.map((j) => j.userCollectionId))];
const collections = await Promise.all(
collectionIds.map((id) => ctx.db.get(id))
);
const validCollections = collections.filter(
(c): c is Doc<'userCollections'> => c !== null
);
// Build lookup maps for efficiency
const userCardsByCardId = new Map(userCards.map((uc) => [uc.cardId, uc]));
const junctionsByUserCardId = new Map<
Id<'userCards'>,
Id<'userCollections'>[]
>();
junctions.forEach((junction) => {
const existing = junctionsByUserCardId.get(junction.userCardId) || [];
existing.push(junction.userCollectionId);
junctionsByUserCardId.set(junction.userCardId, existing);
});
const collectionsById = new Map(
validCollections.map((collection) => [collection._id, collection])
);
// Enrich each card with collection status
const enrichedCards: CardWithCollectionStatus[] = prices.map((card) => {
const userCard = userCardsByCardId.get(card._id);
const isOwned = !!userCard;
let cardCollections: Array<Doc<'userCollections'>> = [];
if (userCard) {
const collectionIds = junctionsByUserCardId.get(userCard._id) || [];
// Get collection details
cardCollections = collectionIds
.map((collectionId) => collectionsById.get(collectionId))
.filter(
(collection): collection is Doc<'userCollections'> =>
collection !== undefined
);
}
return {
...card,
collectionStatus: {
isOwned,
ownedCard: userCard,
collections: cardCollections,
},
};
});
return enrichedCards;
}
Looking at how im looping over my junction table, is this an efficient way in convex to query all my ids with the q.or syntax? or is the withIndex & q.or not used correctly