To paginate search results across multiple fields (e.g., searching both name and phoneNumber), Convex currently does not support a single search index spanning multiple fields. Instead, you need to create separate search indexes for each field (as you have done), run separate queries for each, and then merge the results in your query function.
Here’s how you can approach this:
- Run two search queries: One using the
search_name index and one using the search_phoneNumber index, both filtered by organizationId and the search term.
- Merge the results: Combine the results from both queries, deduplicate users (since a user could match both fields), and paginate the merged list.
Important limitations:
- Pagination is tricky: Since you’re merging two paginated sources, you’ll need to fetch enough results from each to fill your page, and you may need to over-fetch and then trim to the desired page size.
- There is no built-in way to paginate across merged search results with cursor-based pagination; you’ll need to implement offset-based pagination (e.g., skip/take) after merging, which is less efficient for large datasets.
Example approach:
export const searchUsers = query({
args: {
organizationId: v.id("organizations"),
searchTerm: v.optional(v.string()),
page: v.optional(v.number()),
pageSize: v.optional(v.number()),
},
handler: async (ctx, args) => {
const pageSize = args.pageSize ?? 25;
const offset = (args.page ?? 0) * pageSize;
if (!args.searchTerm) {
return await ctx.db
.query("users")
.withIndex("organizationId", (q) =>
q.eq("organizationId", args.organizationId)
)
.order("desc")
.take(pageSize, offset);
}