#Counting Documents
5 messages · Page 1 of 1 (latest)
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()onOrderedQuery/.withIndex(...)today; that’s why Aggregate / Sharded Counter exist. [Discord count thread]