dannyelo
dannyelo4mo ago

Cleanest way to handle optional filters

Hello, I need to filter orders table by status /orders route should get all orders /orders?status=pending should get orders with the status of 'pending' What should I pass to ignore the filter? I tried this and is not working.
export const getOrders = query({
args: {
organizationId: v.optional(v.id('organizations')),
status: v.optional(v.string()),
},
handler: async (ctx, args) => {
const orders = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))
.filter((q) => {
if (!args.status) {
return null
}
return q.eq(q.field('status'), args.status)
})
.collect()

const mappedOrders = await Promise.all(
orders.map(async (order) => {
return await transformOrderForClient(order, ctx)
}),
)
return mappedOrders
},
})
export const getOrders = query({
args: {
organizationId: v.optional(v.id('organizations')),
status: v.optional(v.string()),
},
handler: async (ctx, args) => {
const orders = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))
.filter((q) => {
if (!args.status) {
return null
}
return q.eq(q.field('status'), args.status)
})
.collect()

const mappedOrders = await Promise.all(
orders.map(async (order) => {
return await transformOrderForClient(order, ctx)
}),
)
return mappedOrders
},
})
9 Replies
dannyelo
dannyeloOP4mo ago
This code is working but is not optimal
let orders = []
if (args.status) {
orders = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))
.filter((q) => q.eq(q.field('status'), args.status))
.collect()
} else {
orders = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))
.collect()
}
let orders = []
if (args.status) {
orders = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))
.filter((q) => q.eq(q.field('status'), args.status))
.collect()
} else {
orders = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))
.collect()
}
ari
ari4mo ago
You could try storing your query in a variable, and adding filters onto it:
let orders = []
let query = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))

if (args.status) {
query = query.filter((q) => q.eq(q.field('status'), args.status))
}
orders = query.collect()
let orders = []
let query = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))

if (args.status) {
query = query.filter((q) => q.eq(q.field('status'), args.status))
}
orders = query.collect()
ari
ari4mo ago
Also, have you read about Convex indexes yet? They're a great way to make your queries more performant, and you can do conditional chaining similar to the example I gave above (but with slightly differenct syntax). docs here: https://docs.convex.dev/database/indexes/#querying-documents-using-indexes
Indexes | Convex Developer Hub
Indexes are a data structure that allow you to speed up your
lee
lee4mo ago
+1 to what @ari said: indexes and dynamic filter construction are great. The quickest answer to your initial question is instead of return null you can return true to disable a filter
dannyelo
dannyeloOP4mo ago
Thank you @ari and @lee!
ibrahimyaacob
ibrahimyaacob4mo ago
hey @ari @lee i have a question that extends to this one, what if i have multiple parameter to filter from (other than status). i know that there's combined operator like q.and. but in this case, all the parameters is optional. am i expected to catch all the possibilities
if (arg.filter1 && arg.filter2 && arg.filter3) {}

else if (arg.filter2 && arg.filter3){}

else if (arg.filter1 && arg.filter3){}

else if (arg.filter1){}

... many more
if (arg.filter1 && arg.filter2 && arg.filter3) {}

else if (arg.filter2 && arg.filter3){}

else if (arg.filter1 && arg.filter3){}

else if (arg.filter1){}

... many more
No description
M Zeeshan
M Zeeshan4mo ago
let queryBuilder = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))

if (args.filter1) {
queryBuilder = queryBuilder.filter((q) => q.eq(q.field('filed-to-filter'), args.filter1))
}

if (args.filter2) {
queryBuilder = queryBuilder.filter((q) => q.eq(q.field('filed-to-filter'), args.filter2))
}

if (args.filter3) {
queryBuilder = queryBuilder.filter((q) => q.eq(q.field('filed-to-filter'), args.filter3))
}

const orders = await queryBuilder.collect()
let queryBuilder = await ctx.db
.query('orders')
.filter((q) => q.eq(q.field('organizationId'), args.organizationId))

if (args.filter1) {
queryBuilder = queryBuilder.filter((q) => q.eq(q.field('filed-to-filter'), args.filter1))
}

if (args.filter2) {
queryBuilder = queryBuilder.filter((q) => q.eq(q.field('filed-to-filter'), args.filter2))
}

if (args.filter3) {
queryBuilder = queryBuilder.filter((q) => q.eq(q.field('filed-to-filter'), args.filter3))
}

const orders = await queryBuilder.collect()
ibrahimyaacob
ibrahimyaacob4mo ago
thanks for your sugggestion @M Zeeshan. whats the difference between thisand the combined operator?
M Zeeshan
M Zeeshan4mo ago
This way you can filter only when filter1, filter2 or filter3 is defined. e.g. filter 1 is defined but filter2 and filter3 both are undefined they will not participate in filter. https://discord.com/channels/1019350475847499849/1284787456373297152/1284792305009688576

Did you find this page helpful?