oferitz
oferitz•12mo ago

How would you perform a "contains" query

How would you perform a "contains" query as opposed to querying by a single id, meaning that the classic query will filter by a single id with the eq operator, for example:
export const findItemsByCategory = query({
args: { catgoryId: v.id('category') },
handler: async ({ db }, { catgoryId }) => {
return getManyFrom(db, 'items', 'by_catgoryId', catgoryId)
}
})
export const findItemsByCategory = query({
args: { catgoryId: v.id('category') },
handler: async ({ db }, { catgoryId }) => {
return getManyFrom(db, 'items', 'by_catgoryId', catgoryId)
}
})
But what if I want to filter by multiple category ids: ['cat1', 'cat2', ...]? How do I approach it in an efficient manner?
5 Replies
lee
lee•12mo ago
this article uses pretty much this exact example, and gives many ways to solve it https://stack.convex.dev/complex-filters-in-convex
Using TypeScript to Write Complex Query Filters
There’s a new Convex helper to perform generic TypeScript filters, with the same performance as built-in Convex filters, and unlimited potential.
oferitz
oferitzOP•12mo ago
Thanks! Don't know how i missed that, that's pretty new, right?
lee
lee•12mo ago
yes very new 🙂 actually that article's example might be different from what you want. for what you asked for, you should run the query for each id in parallel with await Promise.all(categories.map(catgoryId => getManyFrom(db, 'items', 'by_catgoryId', catgoryId))) and then join the results.
oferitz
oferitzOP•12mo ago
Yeah exactly, that's what i did, but it was a good read anyway. You can also collect all items and do the filtering in typescript like you suggested in the article.
export const findItemsByMultipleCategories = query({
args: { catIds: v.array(v.id('categories')) },
handler: async ({ db }, { catIds }) => {
const allItems = await db.query('items').collect()
return allTech.filter((item) => catIds.includes(item.categoryId))
}
})
export const findItemsByMultipleCategories = query({
args: { catIds: v.array(v.id('categories')) },
handler: async ({ db }, { catIds }) => {
const allItems = await db.query('items').collect()
return allTech.filter((item) => catIds.includes(item.categoryId))
}
})
But that will probably be a disaster if you have more than a couple of hundred items. @Lee How would you do pagination in this case?
await Promise.all(categories.map(catgoryId => getManyFrom(db, 'items', 'by_catgoryId', catgoryId)))
await Promise.all(categories.map(catgoryId => getManyFrom(db, 'items', 'by_catgoryId', catgoryId)))
lee
lee•12mo ago
Paginating a query like that is tricky. Unfortunately there's not yet a way to do multiple paginations in a query (i made a demo that supports it, but its semantics were too complex to ship). so the only way for now is do separate paginated queries, one for each category, from the client. Or you can do the filter thing which should work fine if the number of documents included in the filter is a non-negligible fraction of the total documents

Did you find this page helpful?