Skip to content

Commit 04b8f66

Browse files
committed
fix: cursors with query and blob lists
1 parent ab3f4eb commit 04b8f66

File tree

4 files changed

+59
-2
lines changed

4 files changed

+59
-2
lines changed

blob.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,27 @@ Deno.test({
464464
},
465465
});
466466

467+
Deno.test({
468+
name: "list - with cursor",
469+
async fn() {
470+
const kv = await setup();
471+
await set(kv, ["hello", 1], new Uint8Array([1, 2, 3]));
472+
await set(kv, ["hello", 2], new Uint8Array([1, 2, 3]));
473+
await set(kv, ["hello", 3], new Uint8Array([1, 2, 3]));
474+
await set(kv, ["hello"], new Uint8Array([1, 2, 3]));
475+
await set(kv, ["world"], new Uint8Array([1, 2, 3]));
476+
const iterator = list(kv, { prefix: ["hello"] }, { limit: 2 });
477+
const entries = await Array.fromAsync(iterator);
478+
assertEquals(entries.length, 2);
479+
const iterator2 = list(kv, { prefix: ["hello"] }, { limit: 2, cursor: iterator.cursor });
480+
const entries2 = await Array.fromAsync(iterator2);
481+
assertEquals(entries2.length, 1);
482+
assertEquals(entries2[0].key, ["hello", 3]);
483+
assertEquals(entries2[0].value, { kind: "buffer", size: 3 });
484+
return teardown();
485+
},
486+
});
487+
467488
Deno.test({
468489
name: "list - meta",
469490
async fn() {

blob_util.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,13 +333,17 @@ export class BlobListIterator extends AsyncIterator implements
333333
> {
334334
#iterator: Deno.KvListIterator<unknown>;
335335
#count = 0;
336+
#cursor?: string;
336337
#kv: Deno.Kv;
337338
#limit?: number;
338339
#options: Deno.KvListOptions;
339340
#valueKind: "meta" | "bytes" | "blob" | "stream";
340341

341342
get cursor(): string {
342-
return this.#iterator.cursor;
343+
if (!this.#cursor) {
344+
throw new Error("Cannot get cursor before first iteration");
345+
}
346+
return this.#cursor;
343347
}
344348

345349
constructor(
@@ -371,6 +375,7 @@ export class BlobListIterator extends AsyncIterator implements
371375
if (this.#limit && this.#count > this.#limit) {
372376
break;
373377
}
378+
this.#cursor = this.#iterator.cursor;
374379
const key: Deno.KvKey = entry.key.slice(0, -1);
375380
if (this.#valueKind === "meta") {
376381
return {

query.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,32 @@ Deno.test("Query - limit API", async () => {
539539
db.close();
540540
});
541541

542+
Deno.test("Query - with cursor", async () => {
543+
const db = await Deno.openKv(":memory:");
544+
await db
545+
.atomic()
546+
.set(["a", "b"], { age: 10 })
547+
.set(["a", "b", "c"], { age: 10 })
548+
.set(["a", "d", "e"], { age: 10 })
549+
.set(["a", "d", "f"], { age: 10 })
550+
.set(["a", "g"], { age: 20 })
551+
.set(["b", "h"], { age: 10 })
552+
.commit();
553+
const iterator = query(db, { prefix: [] }, { limit: 2 })
554+
.where("age", "==", 10)
555+
.get();
556+
const result = await Array.fromAsync(iterator);
557+
assertEquals(result.length, 2);
558+
const iterator2 = query(db, { prefix: [] }, { limit: 2, cursor: iterator.cursor })
559+
.where("age", "==", 10)
560+
.get();
561+
const result2 = await Array.fromAsync(iterator2);
562+
assertEquals(result2.length, 2);
563+
assertEquals(result2[0].key, ["a", "d", "e"]);
564+
assertEquals(result2[1].key, ["a", "d", "f"]);
565+
db.close();
566+
});
567+
542568
Deno.test({
543569
name: "query - blob with limit",
544570
async fn() {

query.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -811,11 +811,15 @@ const AsyncIterator = Object.getPrototypeOf(async function* () {}).constructor;
811811
class QueryListIterator<T = unknown> extends AsyncIterator implements Deno.KvListIterator<T> {
812812
#iterator: Deno.KvListIterator<T>;
813813
#count = 0;
814+
#cursor?: string;
814815
#limit?: number;
815816
#query: Filter[];
816817

817818
get cursor(): string {
818-
return this.#iterator.cursor;
819+
if (!this.#cursor) {
820+
throw new Error("Cannot get cursor before first iteration");
821+
}
822+
return this.#cursor;
819823
}
820824

821825
constructor(
@@ -836,6 +840,7 @@ class QueryListIterator<T = unknown> extends AsyncIterator implements Deno.KvLis
836840
if (this.#limit && this.#count > this.#limit) {
837841
return { value: undefined, done: true };
838842
}
843+
this.#cursor = this.#iterator.cursor;
839844
return { value: entry, done: false };
840845
}
841846
}

0 commit comments

Comments
 (0)