Skip to content

Commit

Permalink
feat: add query module
Browse files Browse the repository at this point in the history
  • Loading branch information
kitsonk committed Jan 8, 2025
1 parent 68718b4 commit fb3fa44
Show file tree
Hide file tree
Showing 8 changed files with 1,005 additions and 58 deletions.
2 changes: 1 addition & 1 deletion batched_atomic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* @example
*
* ```ts
* import { batchedAtomic } from "jsr:/@kitsonk/kv-toolbox/batched_atomic";
* import { batchedAtomic } from "@kitsonk/kv-toolbox/batched_atomic";
*
* const kv = await Deno.openKv();
* await batchedAtomic(kv)
Expand Down
16 changes: 8 additions & 8 deletions blob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ export interface GetAsResponseOptions {
* functionality and would be terribly inefficient in production:
*
* ```ts
* import { getAsResponse } from "jsr:/@kitsonk/kv-toolbox/blob";
* import { getAsResponse } from "@kitsonk/kv-toolbox/blob";
*
* const kv = await Deno.openKv();
*
Expand Down Expand Up @@ -658,7 +658,7 @@ export function toBlob(value: string, type = "text/plain"): Blob {
* @example Convert a `Uint8Array` to JSON
*
* ```ts
* import { toJSON } from "jsr:/@kitsonk/kv-toolbox/blob";
* import { toJSON } from "@kitsonk/kv-toolbox/blob";
*
* const u8 = new Uint8Array();
* const json = JSON.stringify(toJSON(u8));
Expand All @@ -672,7 +672,7 @@ export async function toJSON(blob: File): Promise<BlobFileJSON>;
* @example Convert a `Uint8Array` to JSON
*
* ```ts
* import { toJSON } from "jsr:/@kitsonk/kv-toolbox/blob";
* import { toJSON } from "@kitsonk/kv-toolbox/blob";
*
* const u8 = new Uint8Array();
* const json = JSON.stringify(toJSON(u8));
Expand All @@ -686,7 +686,7 @@ export async function toJSON(blob: Blob): Promise<BlobBlobJSON>;
* @example Convert a `Uint8Array` to JSON
*
* ```ts
* import { toJSON } from "jsr:/@kitsonk/kv-toolbox/blob";
* import { toJSON } from "@kitsonk/kv-toolbox/blob";
*
* const u8 = new Uint8Array();
* const json = JSON.stringify(toJSON(u8));
Expand Down Expand Up @@ -723,7 +723,7 @@ export async function toJSON(
* @example Convert some JSON to a File
*
* ```ts
* import { toValue } from "jsr:/@kitsonk/kv-toolbox/blob";
* import { toValue } from "@kitsonk/kv-toolbox/blob";
*
* const file = toValue({
* meta: {
Expand All @@ -743,7 +743,7 @@ export function toValue(json: BlobFileJSON): File;
* @example Convert some JSON to a File
*
* ```ts
* import { toValue } from "jsr:/@kitsonk/kv-toolbox/blob";
* import { toValue } from "@kitsonk/kv-toolbox/blob";
*
* const blob = toValue({
* meta: {
Expand All @@ -762,7 +762,7 @@ export function toValue(json: BlobBlobJSON): Blob;
* @example Convert some JSON to a File
*
* ```ts
* import { toValue } from "jsr:/@kitsonk/kv-toolbox/blob";
* import { toValue } from "@kitsonk/kv-toolbox/blob";
*
* const u8 = toValue({
* meta: { kind: "buffer" },
Expand All @@ -778,7 +778,7 @@ export function toValue(json: BlobBufferJSON): Uint8Array;
* @example Convert some JSON to a File
*
* ```ts
* import { toValue } from "jsr:/@kitsonk/kv-toolbox/blob";
* import { toValue } from "@kitsonk/kv-toolbox/blob";
*
* const u8 = toValue({
* meta: { kind: "buffer" },
Expand Down
22 changes: 11 additions & 11 deletions crypto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* @example
*
* ```ts
* import { generateKey, openCryptoKv } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey, openCryptoKv }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await openCryptoKv(generateKey());
* const res = await kv.setBlob(
Expand Down Expand Up @@ -104,7 +104,7 @@ function importKey(key: string | Uint8Array): Promise<CryptoKey> {
* @example
*
* ```ts
* import { CryptoKv, generateKey } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { CryptoKv, generateKey }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await Deno.openKv();
* const cryptoKv = new CryptoKv(kv, generateKey());
Expand Down Expand Up @@ -298,7 +298,7 @@ export class CryptoKv {
* @example
*
* ```ts
* import { generateKey, openCryptoKv } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey, openCryptoKv }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await openCryptoKv(generateKey());
* const maybeValue = await kv.getBlob(["hello"], { blob: true });
Expand All @@ -317,7 +317,7 @@ export class CryptoKv {
* @example
*
* ```ts
* import { generateKey, openCryptoKv } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey, openCryptoKv }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await openCryptoKv(generateKey());
* const maybeValue = await kv.getBlob(["hello"]);
Expand Down Expand Up @@ -370,7 +370,7 @@ export class CryptoKv {
* @example
*
* ```ts
* import { generateKey, openCryptoKv } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey, openCryptoKv }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await openCryptoKv(generateKey());
* const value = await kv.getAsBlob(["hello"]);
Expand Down Expand Up @@ -400,7 +400,7 @@ export class CryptoKv {
* @example Retrieve a JSON object from the store
*
* ```ts
* import { generateKey, openCryptoKv } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey, openCryptoKv }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await openCryptoKv(generateKey());
* const value = await kv.getAsJSON(["hello"]);
Expand Down Expand Up @@ -430,7 +430,7 @@ export class CryptoKv {
* @example
*
* ```ts
* import { generateKey, openCryptoKv } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey, openCryptoKv }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await openCryptoKv(generateKey());
* const meta = await kv.getBlobMeta(["hello"]);
Expand Down Expand Up @@ -471,7 +471,7 @@ export class CryptoKv {
* @example
*
* ```ts
* import { generateKey, openCryptoKv } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey, openCryptoKv }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await openCryptoKv(generateKey());
* const res = await kv.setBlob(
Expand Down Expand Up @@ -514,7 +514,7 @@ export class CryptoKv {
* @example
*
* ```ts
* import { generateKey, openCryptoKv } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey, openCryptoKv }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await openCryptoKv(generateKey());
* await kv.deleteBlob(["hello"]);
Expand Down Expand Up @@ -545,7 +545,7 @@ export class CryptoKv {
* @example
*
* ```ts
* import { generateKey } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey }from "@kitsonk/kv-toolbox/crypto";
*
* const key = generateKey();
* ```
Expand Down Expand Up @@ -580,7 +580,7 @@ export function generateKey(bitLength: 128 | 192 | 256 = 256): string {
* @example
*
* ```ts
* import { generateKey, openCryptoKv } from "jsr:@kitsonk/kv-toolbox/crypto";
* import { generateKey, openCryptoKv }from "@kitsonk/kv-toolbox/crypto";
*
* const kv = await openCryptoKv(generateKey());
* // kv is now an instance of CryptoKv
Expand Down
5 changes: 3 additions & 2 deletions deno.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
{
"name": "@kitsonk/kv-toolbox",
"version": "0.24.1",
"version": "0.25.0-beta.1",
"exports": {
".": "./toolbox.ts",
"./batched_atomic": "./batched_atomic.ts",
"./blob": "./blob.ts",
"./crypto": "./crypto.ts",
"./keys": "./keys.ts"
"./keys": "./keys.ts",
"./query": "./query.ts"
},
"publish": {
"exclude": [
Expand Down
179 changes: 179 additions & 0 deletions query.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { assert } from "@std/assert/assert";
import { assertEquals } from "@std/assert/equals";
import { assertThrows } from "@std/assert/throws";
import { Filter, PropertyPath, query } from "./query.ts";

Deno.test("PropertyPath - exists", () => {
const path = new PropertyPath("a", "b", "c");
assert(path.exists({ a: { b: { c: 1 } } }));
assert(!path.exists({ a: { b: { d: 1 } } }));
});

Deno.test("PropertyPath - exists - Map", () => {
const path = new PropertyPath("a", "b", "c");
assert(path.exists(new Map([["a", new Map([["b", new Map([["c", 1]])]])]])));
assert(!path.exists(new Map([["a", new Map([["b", new Map([["d", 1]])]])]])));
});

Deno.test("PropertyPath - value", () => {
const path = new PropertyPath("a", "b", "c");
assertEquals(path.value({ a: { b: { c: 1 } } }), 1);
});

Deno.test("PropertyPath - value - Map", () => {
const path = new PropertyPath("a", "b", "c");
assertEquals(
path.value(new Map([["a", new Map([["b", new Map([["c", 1]])]])]])),
1,
);
});

Deno.test("PropertyPath - value - not exists", () => {
const path = new PropertyPath("a", "b", "c");
assertThrows(
() => path.value({ a: { b: { d: 1 } } }),
Error,
"Property does not exist",
);
});

Deno.test("PropertyPath - value - not mappable", () => {
const path = new PropertyPath("a", "b", "c");
assertThrows(
() => path.value({ a: { b: 1 } }),
Error,
"Value is not mappable",
);
});

Deno.test("Filter.where() - less than", () => {
const filter = Filter.where("age", "<", 10);
assert(filter.test({ age: 9 }));
assert(!filter.test({ age: 10 }));
});

Deno.test("Filter.where() - less than or equal", () => {
const filter = Filter.where("age", "<=", 10);
assert(filter.test({ age: 10 }));
assert(!filter.test({ age: 11 }));
});

Deno.test("Filter.where() - equals", () => {
const filter = Filter.where("name", "==", "test");
assert(filter.test({ name: "test" }));
assert(!filter.test({ name: "test2" }));
});

Deno.test("Filter.where() - equals deeply", () => {
const filter = Filter.where("name", "==", { a: 1 });
assert(filter.test({ name: { a: 1 } }));
assert(!filter.test({ name: { a: 2 } }));
});

Deno.test("Filter.where() - not equals", () => {
const filter = Filter.where("name", "!=", "test");
assert(!filter.test({ name: "test" }));
assert(filter.test({ name: "test2" }));
});

Deno.test("Filter.where() - greater than", () => {
const filter = Filter.where("age", ">", 10);
assert(filter.test({ age: 11 }));
assert(!filter.test({ age: 10 }));
});

Deno.test("Filter.where() - greater than or equal", () => {
const filter = Filter.where("age", ">=", 10);
assert(filter.test({ age: 10 }));
assert(!filter.test({ age: 9 }));
});

Deno.test("Filter.where() - array contains", () => {
const filter = Filter.where("tags", "array-contains", "test");
assert(filter.test({ tags: ["test"] }));
assert(!filter.test({ tags: ["test2"] }));
});

Deno.test("Filter.where() - array contains any", () => {
const filter = Filter.where("tags", "array-contains-any", ["test", "test2"]);
assert(filter.test({ tags: ["test"] }));
assert(filter.test({ tags: ["test2"] }));
assert(!filter.test({ tags: ["test3"] }));
});

Deno.test("Filter.where() - in", () => {
const filter = Filter.where("age", "in", [10, 20]);
assert(filter.test({ age: 10 }));
assert(filter.test({ age: 20 }));
assert(!filter.test({ age: 30 }));
});

Deno.test("Filter.where() - not in", () => {
const filter = Filter.where("age", "not-in", [10, 20]);
assert(!filter.test({ age: 10 }));
assert(!filter.test({ age: 20 }));
assert(filter.test({ age: 30 }));
});

Deno.test("Filter.where() - PropertyPath", () => {
const filter = Filter.where(new PropertyPath("a", "b", "c"), "==", 1);
assert(filter.test({ a: { b: { c: 1 } } }));
assert(!filter.test({ a: { b: { c: 2 } } }));
assert(!filter.test({ a: { b: { d: 1 } } }));
});

Deno.test("Filter.and()", () => {
const filter = Filter.and(
Filter.where("age", ">", 10),
Filter.where("age", "<", 20),
);
assert(filter.test({ age: 15 }));
assert(!filter.test({ age: 10 }));
assert(!filter.test({ age: 20 }));
});

Deno.test("Filter.or()", () => {
const filter = Filter.or(
Filter.where("age", "<", 10),
Filter.where("age", ">", 20),
);
assert(filter.test({ age: 5 }));
assert(filter.test({ age: 25 }));
assert(!filter.test({ age: 15 }));
});

Deno.test("query() - value", async () => {
const db = await Deno.openKv(":memory:");
await db
.atomic()
.set(["a"], { age: 10 })
.set(["b"], { age: 20 })
.commit();
const result = query(db, { prefix: [] }).value("==", { age: 10 }).get();
const entries = [];
for await (const entry of result) {
entries.push(entry);
}
assertEquals(entries.length, 1);
assertEquals(entries[0].key, ["a"]);
assertEquals(entries[0].value, { age: 10 });
db.close();
});

Deno.test("query() - where - equals", async () => {
const db = await Deno.openKv(":memory:");
await db
.atomic()
.set(["a"], { age: 10 })
.set(["b"], { age: 20 })
.commit();
const result = query(db, { prefix: [] }).where("age", "==", 10).get();
const entries = [];
for await (const entry of result) {
entries.push(entry);
}
assertEquals(entries.length, 1);
assertEquals(entries[0].key, ["a"]);
assertEquals(entries[0].value, { age: 10 });
db.close();
});
Loading

0 comments on commit fb3fa44

Please sign in to comment.