#fix pagianted query

8 messages · Page 1 of 1 (latest)

neat cove
#

This query or mutation function ran multiple paginated queries. Convex only supports a single paginated query in each function.

export const fnA = query({
  args: {
    a: v.id("x")
  },
  handler: async (b, c) => {
    // Fetch list
    const d = await HelperA.getAll(b, c.a)

    // Map totals
    const e = new Map<string, number>()
    for (const f of d) {
      const g = await HelperB.sum(b, {
        namespace: `${c.a}-${f}`
      })
      if (g > 0) {
        e.set(f, g)
      }
    }

    // Rolling top list
    const h: Array<{
      i: Id<"u">
      j: Id<"p">
      k: number
      l: number
      m: number
    }> = []

    // Pagination
    let n: string | null = null
    let o = false

    while (!o) {
      const p = await b.db
        .query("q")
        .filter((r) => r.eq(r.field("x"), c.a))
        .paginate({ cursor: n, numItems: CONST_A })

      // Process batch
      for (const s of p.page) {
        const t = e.get(s.j) || 0

        if (t > 0) {
          const u = (s.v / t) * 100
          const w = Math.round(u * 100) / 100

          const x = {
            i: s.i,
            j: s.j,
            k: s.v,
            l: t,
            m: w
          }

          if (h.length < 10) {
            h.push(x)
            h.sort((y, z) => z.m - y.m)
          } else {
            invariant(h.length === 10, "should be 10")
            invariant(h[9], "index 9 defined")
            if (w > h[9].m) {
              h[9] = x
              h.sort((y, z) => z.m - y.m)
            }
          }
        }
      }

      n = p.continueCursor
      o = p.isDone
    }

    // Enrich results
    const aa = await asyncMap(h, async (ab) => {
      const [ac, ad] = await Promise.all([
        b.db.get(ab.j),
        b.db.get(ab.i)
      ])

      return {
        ...ab,
        ae: ac?.af || "Unknown A",
        ag: ad?.ah || "Unknown B"
      }
    })

    return aa
  }
})

tardy spearBOT
#

Hi @neat cove. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

The error "This query or mutation function ran multiple paginated queries. Convex only supports a single paginated query in each function." occurs because Convex only allows a single call to .paginate() per query or mutation function. In your code, the issue is likely not in the main while loop (which paginates over "q"), but in the helper functions you call earlier (HelperA.getAll and HelperB.sum). If either of those helpers internally calls .paginate(), that would trigger this error.

How to fix:

  • Ensure that only one .paginate() is called per function execution. If you need to paginate over multiple sources, you must use a custom pagination helper, such as the one provided by convex-helpers, which is designed to work around this limitation.

Recommended Solution:

Use the convex-helpers paginator to manually paginate over multiple sources. This helper allows you to implement custom pagination logic without hitting the single .paginate() limitation. For example, you can use getPage to control pagination over multiple queries or streams, as described in the Stack article on advanced pagination patterns.

Example pattern:

Instead of calling .paginate() multiple times, you can fetch pages from each source using getPage and manage the cursors yourself. Here’s a simplified pattern:

import { getPage } from "convex-helpers/server/pagination";

export const fnA = query({
  args: { a: v.id("x") },
  handler: async (ctx, args) => {
    // Example: paginate over "q" table
    const { page, continueCursor, isDone } = await getPage(ctx, {
      table: "q",
      // ...other options like index, startIndexKey, etc.
    });
#

// Process page as needed...

return { page, continueCursor, isDone };

}
});


**Key points:**
- Refactor your helpers so that only the top-level function handles pagination.
- If you need to combine results from multiple sources, use `getPage` or similar helpers to fetch and merge results, but only paginate in one place per function execution.

For more details and advanced patterns (like joins or unions with pagination), see the [Stack article on pagination](<https://stack.convex.dev/pagination#patterns>) and the [convex-helpers paginator documentation](<https://github.com/get-convex/convex-helpers/tree/main/packages/convex-helpers#paginator-manual-pagination-with-familiar-syntax>).

If you need to paginate over multiple sources in a single query, you must manage the cursors and merging logic yourself, as Convex's built-in `.paginate()` does not support this out of the box. The convex-helpers package is the recommended approach for these scenarios<sup>[source](<https://discord.com/channels/1019350475847499849/1381120269262782474>)</sup>.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
#

@neat cove Kapa.ai is still learning and improving, please let me know how I did by reacting below.

neat cove
#

@tardy spear can u confirm using paginator in my query above would allow me to do the for loop inside?

tardy spearBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

Yes, using the paginator from convex-helpers (such as getPage) would allow you to perform a for loop inside your query, iterating over multiple pages of results, without hitting the Convex limitation of only one .paginate() per function. The paginator is designed specifically to work around this limitation by letting you manually control pagination and fetch pages as needed, even in more complex scenarios like loops or combining results from multiple sources.

This approach is recommended in Convex support threads for cases where you need to paginate over multiple sources or perform more advanced pagination logic inside a function, as the built-in .paginate() only allows a single use per function execution. The paginator helper gives you the flexibility to fetch and process each page in a loop, as your use case requires Discord support thread: Filtered and paginated list.

So, you can safely use a for loop with the paginator to process each page of results inside your query function.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: