Cursor — stream a filtered result set

A cursor is a query in progress. It holds onto the filter/sort and hands out rows one-at-a-time (nextItem()) or in small batches (nextBatch(n)). Unlike list(), it never builds the full array in memory — handy for large datasets, progress UIs, and pagination that survives across await boundaries.

How it works

flowchart LR
    L["list(opts)
returns all rows at once"] --> Arr[Array of all N rows] C["cursor(opts)
returns a handle"] --> S["Cursor
count=N position=0"] S --> N1["nextItem()"] --> R1[1 row, position=1] R1 --> N2["nextBatch(10)"] --> R2[10 rows, position=11] R2 --> NE{hasMore?} NE -- yes --> N1 NE -- no --> E[done] style L fill:#fff3e0,stroke:#ef6c00 style C fill:#e3f2fd,stroke:#1565c0 style E fill:#c8e6c9,stroke:#2e7d32

Try it — a four-step tour

  1. Click Seed 100 records. Creates items with random value (0–99) and category.
  2. Click Create cursor. The default filter (value > 50) runs — the status row shows how many rows remain.
  3. Click Next (1) a few times, then Next batch (10). Watch Remaining drop and Position climb.
  4. Click Reset — same query, position returns to 0, you can iterate again.

Setup

1 · Seed data

2 · Create cursor

Cursor

Remaining
Position
Has more

Latest items

No items fetched yet.

Automated test suite

Run all tests runs 8 steps across a fresh test_cursor namespace and reports pass/fail for each:

  1. Seed 50 records with values 1–50
  2. Unfiltered cursor has 50 items
  3. Filter value > 25 yields 25 items
  4. nextItem twice → position=2
  5. nextBatch(10) → position=12
  6. hasMore() agrees with count() > 0
  7. Draining the cursor leaves 0 remaining
  8. Sort desc returns rows in decreasing order
Click the button to run.

Log