denoland / denoland/deploy_feedback

[KV Feedback]: Allow multiple ranges in `list`

Open
#414 0 comments 1 reaction 0 assignees View on GitHub
kv
Dominant language
No language data
Stars
79
Forks
5
PR merge metrics
No merged PRs in 30d

Description

### 🔍

- [X] Did you search for existing issues?

### Type of feedback

Feature request

### Description

It would be great if we could select multiple ranges in `list`.

Currently, we can only select a single range using the `prefix` and/or `start` & `end` options.

One use case for multiple ranges is to limit the list to specific ranges of nested keys in a keyspace.

A workaround is to list the whole parent keyspace and filter out the desired entries manually. Or to call list multiple times and implement consistency checks yourself.

The workarounds aren't ideal, because they are more error prone and inefficient. Also, they make using pagination hard or impossible.

### Steps to reproduce (if applicable)

Example

```js
const db = await Deno.openKv(":memory:");

await db
.atomic()
.set(["users", "10", "name"], null) // <-- we don't want that in the list
.set(["users", "10", "age"], null) // <-- we don't want that in the list
.set(["users", "12", "name"], null)
.set(["users", "12", "age"], null)
.set(["users", "20", "name"], null) // <-- we don't want that in the list
.set(["users", "20", "age"], null) // <-- we don't want that in the list
.set(["users", "24", "name"], null)
.set(["users", "24", "age"], null)
.commit();

// how to select here?
const entries = db.list({ ... });
```

### Expected behavior (if applicable)

An option to specify multiple ranges, for example an array in a `prefixes` argument

```js
const entries = db.list({ prefixes: [["users", "12"], ["users", "24"]] });

for await (const { key, value } of entries) {
// ... do stuff
}
```

### Possible solution (if applicable)

First workaround with listing whole parent keyspace and filtering

```js
const entries = db.list({ prefix: ["users"] });

for await (const { key, value } of entries) {
if (!["12", "24"].includes(key.at(1)) {
continue;
}

// ... do stuff
}
```

Second workaround listing separately and implementing consistency checks

```js
const entries = await listMultiple(db, [["users", "12"], ["users", "24"]]);

for (const entry of entries) {
// ... do stuff
}

/**
* Multi-range list
*
* note: pagination isn't possible
* @param db Deno KV database
* @param prefixes multiple prefixes
* @returns array of entries
*/
async function listMultiple(
db: Deno.Kv,
prefixes: Deno.KvKey[],
): Promise[]> {
const MAX_RETRIES = 100;

const checks: Deno.AtomicCheck[] = [];

let res: Deno.KvEntry[] = [];

let count = 0;
let checkRes = { ok: false };

while (!checkRes.ok) {
// prevent infinite loop
if (count > MAX_RETRIES) {
throw new Error(`Exceeded maximum retries to concurrent changes`);
}

// reset previous iteration
res = [];

for (const prefix of prefixes) {
const entries = db.list({ prefix });

for await (const entry of entries) {
checks.push({ key: entry.key, versionstamp: entry.versionstamp });
res.push(entry);
}
}

checkRes = await db.atomic().check(...checks).commit();
count += 1;
}

return res;
}
```

### Additional context

_No response_

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with the Deno.Kv.list entry point shown in the examples and compare its current prefix, start, end, and pagination behavior with the proposed prefixes option. Define how multiple ranges should be represented, ordered, paginated, and kept consistent, then verify that the example selecting the 12 and 24 user ranges works without including 10 or 20.

Written by the indexing model from the issue text.

Assessment

Tech stack
deno, javascript
Domain
databases
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
28/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.