denoland / denoland/deploy_feedback
[KV Feedback]: Allow multiple ranges in `list`
- 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