Close #2718 : added querying with prefix patterns example and documentation (#2778)
Docker Image CI / build-and-push-image (push) Has been cancelled
Maintain Release Merge PR / update-release-pr (push) Has been cancelled
Notify HeyPuter / notify (push) Has been cancelled
release-please / release-please (push) Has been cancelled

* feat: add querying with prefix patterns example and documentation

* refine

---------

Co-authored-by: Reynaldi Chernando <reynaldichernando@gmail.com>
This commit is contained in:
Hemant
2026-04-08 12:19:00 +05:30
committed by GitHub
parent 8cc29724b2
commit 69bb7e4a38
4 changed files with 141 additions and 3 deletions
+1
View File
@@ -232,6 +232,7 @@ You can see various Puter.js Key-Value Store features in action from the followi
- [Decrement](/playground/kv-decr/)
- [Delete](/playground/kv-del/)
- [List](/playground/kv-list/)
- [Querying with Prefix Patterns](/playground/kv-prefix-patterns/)
- [Flush](/playground/kv-flush/)
- [Expire](/playground/kv-expire/)
- [Expire At](/playground/kv-expireAt/)
+69
View File
@@ -6,6 +6,8 @@ platforms: [websites, apps, nodejs, workers]
Returns an array of all keys in the user's key-value store for the current app. If the user has no keys, the array will be empty.
Because filtering is prefix-based, `list()` works best when you design keys around the read patterns your app needs. In practice, that means your key naming convention becomes your query plan.
Results are sorted lexicographically (string order) by key.
## Syntax
@@ -193,3 +195,70 @@ If the user has no keys, the array will be empty.
</body>
</html>
```
<strong class="example-title">Design keys for query-like filtering with prefix patterns</strong>
```html;kv-prefix-patterns
<html>
<body>
<script src="https://js.puter.com/v2/"></script>
<script>
(async () => {
const orders = [
{ id: '0001', status: 'pending', customer: 'alice', total: 48 },
{ id: '0002', status: 'shipped', customer: 'alice', total: 72 },
{ id: '0003', status: 'pending', customer: 'bob', total: 15 },
];
// In KV, key design is your query plan.
// We store the same order under multiple prefixes so each read path
// becomes a simple prefix query with puter.kv.list().
for (const order of orders) {
await puter.kv.set(`demo:order:by-id:${order.id}`, order);
await puter.kv.set(`demo:order:by-status:${order.status}:${order.id}`, order);
await puter.kv.set(`demo:order:by-customer:${order.customer}:${order.id}`, order);
await puter.kv.set(`demo:order:by-status-customer:${order.status}:${order.customer}:${order.id}`, order);
}
puter.print('<b>Stored read paths</b><br>');
puter.print('demo:order:by-status:pending:*<br>');
puter.print('demo:order:by-customer:alice:*<br>');
puter.print('demo:order:by-status-customer:pending:alice:*<br><br>');
const pendingOrders = await puter.kv.list('demo:order:by-status:pending:*', true);
puter.print('<b>Query: status = pending</b><br>');
pendingOrders.forEach(({ key, value }) => {
puter.print(`${key} => ${value.customer} ($${value.total})<br>`);
});
puter.print('<br>');
const aliceOrders = await puter.kv.list('demo:order:by-customer:alice:*', true);
puter.print('<b>Query: customer = alice</b><br>');
aliceOrders.forEach(({ key, value }) => {
puter.print(`${key} => ${value.status} ($${value.total})<br>`);
});
puter.print('<br>');
const alicePendingOrders = await puter.kv.list('demo:order:by-status-customer:pending:alice:*', true);
puter.print('<b>Query: status = pending AND customer = alice</b><br>');
alicePendingOrders.forEach(({ key, value }) => {
puter.print(`${key} => order ${value.id} ($${value.total})<br>`);
});
puter.print('<br>');
puter.print('<b>Takeaway</b><br>');
puter.print('With puter.kv.list(), filtering comes from key prefixes.<br>');
puter.print('If you need another query path, add another prefix-friendly key.<br><br>');
// Cleanup
for (const order of orders) {
await puter.kv.del(`demo:order:by-id:${order.id}`);
await puter.kv.del(`demo:order:by-status:${order.status}:${order.id}`);
await puter.kv.del(`demo:order:by-customer:${order.customer}:${order.id}`);
await puter.kv.del(`demo:order:by-status-customer:${order.status}:${order.customer}:${order.id}`);
}
})();
</script>
</body>
</html>
```
+9 -3
View File
@@ -418,13 +418,19 @@ const examples = [
description: 'See how keys are returned in lexicographic order with puter.kv.list(). Run and modify this example in the playground.',
slug: 'kv-list-sort',
source: '/playground/examples/kv-list-sort.html',
},
{
},
{
title: 'List (Zero-Padding)',
description: 'Learn how to sort numeric keys correctly by zero-padding. Run and experiment with this example in the playground.',
slug: 'kv-list-padding',
source: '/playground/examples/kv-list-padding.html',
},
},
{
title: 'List (Prefix Patterns)',
description: 'Learn how to model SQL-style filtering in Puter.js KV by designing keys for prefix queries. Run and modify this example in the playground.',
slug: 'kv-prefix-patterns',
source: '/playground/examples/kv-prefix-patterns.html',
},
{
title: 'Flush',
description: 'Clear all data with Puter.js key-value API. Run and experiment with this flush example in the playground.',
@@ -0,0 +1,62 @@
<html>
<body>
<script src="https://js.puter.com/v2/"></script>
<script>
(async () => {
const orders = [
{ id: '0001', status: 'pending', customer: 'alice', total: 48 },
{ id: '0002', status: 'shipped', customer: 'alice', total: 72 },
{ id: '0003', status: 'pending', customer: 'bob', total: 15 },
];
// In KV, key design is your query plan.
// We store the same order under multiple prefixes so each read path
// becomes a simple prefix query with puter.kv.list().
for (const order of orders) {
await puter.kv.set(`demo:order:by-id:${order.id}`, order);
await puter.kv.set(`demo:order:by-status:${order.status}:${order.id}`, order);
await puter.kv.set(`demo:order:by-customer:${order.customer}:${order.id}`, order);
await puter.kv.set(`demo:order:by-status-customer:${order.status}:${order.customer}:${order.id}`, order);
}
puter.print('<b>Stored read paths</b><br>');
puter.print('demo:order:by-status:pending:*<br>');
puter.print('demo:order:by-customer:alice:*<br>');
puter.print('demo:order:by-status-customer:pending:alice:*<br><br>');
const pendingOrders = await puter.kv.list('demo:order:by-status:pending:*', true);
puter.print('<b>Query: status = pending</b><br>');
pendingOrders.forEach(({ key, value }) => {
puter.print(`${key} => ${value.customer} ($${value.total})<br>`);
});
puter.print('<br>');
const aliceOrders = await puter.kv.list('demo:order:by-customer:alice:*', true);
puter.print('<b>Query: customer = alice</b><br>');
aliceOrders.forEach(({ key, value }) => {
puter.print(`${key} => ${value.status} ($${value.total})<br>`);
});
puter.print('<br>');
const alicePendingOrders = await puter.kv.list('demo:order:by-status-customer:pending:alice:*', true);
puter.print('<b>Query: status = pending AND customer = alice</b><br>');
alicePendingOrders.forEach(({ key, value }) => {
puter.print(`${key} => order ${value.id} ($${value.total})<br>`);
});
puter.print('<br>');
puter.print('<b>Takeaway</b><br>');
puter.print('With puter.kv.list(), filtering comes from key prefixes.<br>');
puter.print('If you need another query path, add another prefix-friendly key.<br><br>');
// Cleanup
for (const order of orders) {
await puter.kv.del(`demo:order:by-id:${order.id}`);
await puter.kv.del(`demo:order:by-status:${order.status}:${order.id}`);
await puter.kv.del(`demo:order:by-customer:${order.customer}:${order.id}`);
await puter.kv.del(`demo:order:by-status-customer:${order.status}:${order.customer}:${order.id}`);
}
})();
</script>
</body>
</html>