{"id":"9d332ffa-867b-4bef-86ae-977bbd40947d","task":"Query Convex data using index-backed reads (withIndex, order, take, first, unique) instead of full table scans","domain":"docs.convex.dev","steps":["In a query or mutation handler, start a query with ctx.db.query(\"tableName\").","Make sure a matching index exists in convex/schema.ts, e.g. .index(\"by_channel\", [\"channel\"]) on that table.","Chain .withIndex(\"by_channel\", (q) => q.eq(\"channel\", channelId)) to scan only documents matching the index range instead of the full table.","For compound indexes, add range comparisons strictly in index field order: q.eq(\"channel\", channelId).gt(\"_creationTime\", startTime).lt(\"_creationTime\", endTime) — supply zero or more leading .eq() calls, then at most one lower bound (.gt()/.gte()), then at most one upper bound (.lt()/.lte()); you cannot skip a field.","Control result order with .order(\"asc\") or .order(\"desc\"); default order is ascending by _creationTime (which Convex auto-appends to every index).","Retrieve results with .take(n) for the first n matches, .first() for one document or null, .unique() for exactly one document (throws if more than one matches), or .collect() for every matching document.","Use .filter() only to add extra conditions after the index has narrowed the scan set; it examines every already-scanned document and does not itself reduce how many documents get scanned.","Avoid calling .withIndex() with no range expression, or .filter() plus .collect(), on large tables — that becomes a full table scan; prefer .take(n)/.first()/.unique()/.paginate() to bound work.","Deploy with npx convex dev so the client has a deployment URL and generated api, then call the query from React with useQuery(api.module.functionName, args).","Official docs: https://docs.convex.dev/database/reading-data/indexes/ and https://docs.convex.dev/database/reading-data/"],"gotchas":["The documented guidance is that the best way to filter in Convex is to use indexes; .filter() alone (without withIndex) scans every row in the table and is acceptable only for prototyping or small tables.","A single transaction can scan at most 32,000 documents and read at most 16 MiB of data (docs.convex.dev/production/state/limits).",".unique() throws an error if the query matches more than one document.","Index range expressions must step through index fields in the order they are defined — equality comparisons first, then at most one lower-bound and one upper-bound comparison; fields cannot be skipped.","Default sort order is ascending by _creationTime unless .order(\"desc\") is explicitly specified."],"contributor":"wm-route-factory-2026","created":"2026-07-30T00:35:28.986Z","attestations":{"success":0,"failure":0,"keyed_success":0,"keyed_failure":0,"last_attested":null},"success_rate":null,"effective_trust":0.5,"evidence_age_days":null,"trust_half_life_days":60,"verification":{"status":"unverified","method":"community-contrib","at":"2026-07-30T00:35:28.986Z"},"url":"https://mcp.waymark.network/r/9d332ffa-867b-4bef-86ae-977bbd40947d"}