#Counting Documents

5 messages · Page 1 of 1 (latest)

vivid haven
#

how would I count all documents in a collection that has over 300,000 documents

gilded quailBOT
#

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

#

To reliably count documents in a very large table (300k+ docs) you should not use .collect() or try to scan the whole table in a single query—this will hit Convex’s read/write limits and/or be very slow. [Filters performance; Best practices collect]

Instead, use one of Convex’s aggregation patterns:


1. Use the Aggregate component (recommended for rich aggregates)

The Aggregate component maintains an indexed structure that lets you do count() in O(log n) time instead of scanning all 300k docs. [Aggregate overview; Efficient count/sum]

Install:

npm install @convex-dev/aggregate

Basic usage (conceptually):

import { Aggregate } from "@convex-dev/aggregate";

// setup once
const aggregate = new Aggregate(components.aggregateForMyTable);

// then in a query/mutation
const totalCount = await aggregate.count(ctx); // counts all documents efficiently

You can also keep multiple filtered counts and use countBatch() to get several counts in one call. [Aggregate batch ops]


2. Use the Sharded Counter component (simple “how many docs?”)

If you only need a total count for a table, Sharded Counter is a good fit and scales to very high write throughput. [Sharded counter table count]

Basic pattern:

import { ShardedCounter } from "@convex-dev/sharded-counter";
#

const counter = new ShardedCounter(components.shardedCounter);

// Centralized insert helper
async function insertUser(ctx, user) {
await ctx.db.insert("users", user);
await counter.add(ctx, "users"); // increment table count
}


Then you can read:

```ts
const totalUsers = await counter.count(ctx, "users");

You must ensure all inserts/deletes to that table go through helpers (or triggers) that also update the counter so it stays exact. [Sharded counter table count]


3. Triggers + a denormalized count document

Another option is a single “count” document maintained by a trigger whenever rows are inserted/deleted. [Denormalizing a count]

Example (from docs):

triggers.register("users", async (ctx, change) => {
  const countDoc = (await ctx.db.query("userCount").unique())!;
  if (change.operation === "insert") {
    await ctx.db.patch(countDoc._id, { count: countDoc.count + 1 });
  } else if (change.operation === "delete") {
    await ctx.db.patch(countDoc._id, { count: countDoc.count - 1 });
  }
});

Then counting is just reading that single doc. This is simple but can become a write hotspot on heavy write loads. [Denormalizing a count]


What not to do

#
  • Don’t do ctx.db.query("myTable").collect() just to do .length — that reads all 300k docs and will hit limits and bandwidth. [Best practices collect; Why no COUNT]
  • There’s no built-in .count() on OrderedQuery/.withIndex(...) today; that’s why Aggregate / Sharded Counter exist. [Discord count thread]

If you share whether you just need “total rows” or filtered/paginated counts, I can outline a concrete Aggregate or ShardedCounter setup for that case.

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