bun-sqlite-key-value
Advanced tools
Comparing version 1.3.11 to 1.3.12
{ | ||
"name": "bun-sqlite-key-value", | ||
"version": "1.3.11", | ||
"version": "1.3.12", | ||
"author": "Gerold Penz<gerold@gp-softwaretechnik.at>", | ||
@@ -23,2 +23,3 @@ "repository": { | ||
"Bun", | ||
"Bun.js", | ||
"SQLite", | ||
@@ -25,0 +26,0 @@ "Key-Value", |
@@ -48,3 +48,3 @@ # Bun SQLite Key Value | ||
```typescript | ||
const store = new BunSqliteKeyValue([filename], [options]) | ||
const store = new BunSqliteKeyValue(filename?, options?) | ||
``` | ||
@@ -91,3 +91,3 @@ | ||
```typescript | ||
set(key: string, value: any, [ttlMs: number]): void | ||
set(key: string, value: any, ttlMs?: number) | ||
``` | ||
@@ -129,6 +129,30 @@ | ||
store.set("my-key-2", "item-with-ttl", 30000) | ||
``` | ||
## Write Multiple Items | ||
```typescript | ||
setItems(items: {key: string, value: T, ttlMs?: number}[]) { | ||
``` | ||
Adds a large number of items to the database and takes only | ||
a small fraction of the time that `set()` would take individually. | ||
### Example | ||
```typescript | ||
import { BunSqliteKeyValue } from "bun-sqlite-key-value" | ||
const store = new BunSqliteKeyValue() | ||
// Add many records | ||
store.setItems([ | ||
{key: "a:1", value: "test-value-1"}, | ||
{key: "a:2", value: "test-value-2"}, | ||
]) | ||
``` | ||
## Read Value | ||
@@ -320,3 +344,3 @@ | ||
## Read and write binary files (images) | ||
## Read and Write Binary Files (Images) | ||
@@ -346,3 +370,3 @@ SQLite has no problem with images and other binaries. | ||
## Cache values with TTL | ||
## Cache Values with TTL | ||
@@ -370,2 +394,22 @@ You can specify a caching period when you open the database. | ||
## Has (key) | ||
```typescript | ||
has(key: string): boolean | ||
``` | ||
Checks if key exists. Returns `false` if the item is expired. | ||
### Example | ||
```typescript | ||
import { BunSqliteKeyValue } from "bun-sqlite-key-value" | ||
const store = new BunSqliteKeyValue() | ||
store.has("my-key") --> false | ||
``` | ||
## All Methods | ||
@@ -381,2 +425,5 @@ | ||
### Set items | ||
- `setItems({key: string, value: any}[])` | ||
### Get value | ||
@@ -432,3 +479,5 @@ - `get(key: string)` | ||
- `has(key: string)` --> Boolean | ||
- [ ] getKeys() --> Array with all Keys | ||
- [ ] getKeys(startsWith: string) --> Array | ||
- [ ] `getKeys()` --> Array with all Keys | ||
- [ ] `getKeys(startsWith: string)` --> Array | ||
- [ ] `getKeys(keys: string[])` --> Array | ||
@@ -30,3 +30,6 @@ import { Database, type Statement } from "bun:sqlite" | ||
const MIN_UTF8_CHAR: string = String.fromCodePoint(1) | ||
const MAX_UTF8_CHAR: string = String.fromCodePoint(1_114_111) | ||
export class BunSqliteKeyValue { | ||
@@ -45,3 +48,3 @@ | ||
private getItemsStartsWithStatement: Statement<Record> | ||
private getKeyStatement: Statement<Omit<Record, "value">> | ||
private getKeyStatement: Statement<Omit<Record, "value">> | ||
// private getAllKeysStatement: Statement<Omit<Record, "value">> | ||
@@ -84,3 +87,3 @@ // private getKeysStartsWithStatement: Statement<Omit<Record, "value">> | ||
this.getItemStatement = this.db.query("SELECT value, expires FROM items WHERE key = $key") | ||
this.getItemsStartsWithStatement = this.db.query("SELECT key, value, expires FROM items WHERE key LIKE $startsWith") | ||
this.getItemsStartsWithStatement = this.db.query("SELECT key, value, expires FROM items WHERE key = $key OR key >= $gte AND key < $lt") | ||
this.deleteStatement = this.db.query("DELETE FROM items WHERE key = $key") | ||
@@ -160,2 +163,13 @@ this.getKeyStatement = this.db.query("SELECT key, expires FROM items WHERE key = $key") | ||
// Adds a large number of entries to the database and takes only | ||
// a small fraction of the time that `set()` would take individually. | ||
setItems<T = any>(items: {key: string, value: T, ttlMs?: number}[]) { | ||
this.db.transaction(() => { | ||
items.forEach(({key, value, ttlMs}) => { | ||
this.set<T>(key, value, ttlMs) | ||
}) | ||
})() | ||
} | ||
// Get one value | ||
@@ -194,3 +208,11 @@ get<T = any>(key: string): T | undefined { | ||
// Filtered items (startsWith) | ||
records = this.getItemsStartsWithStatement.all({startsWith: startsWithOrKeys + "%"}) | ||
// key = "addresses:" | ||
// gte = key + MIN_UTF8_CHAR | ||
// "addresses:aaa" | ||
// "addresses:xxx" | ||
// lt = key + MAX_UTF8_CHAR | ||
const key: string = startsWithOrKeys | ||
const gte: string = key + MIN_UTF8_CHAR | ||
const lt: string = key + MAX_UTF8_CHAR | ||
records = this.getItemsStartsWithStatement.all({key, gte, lt}) | ||
} else if (startsWithOrKeys) { | ||
@@ -197,0 +219,0 @@ // Filtered items (array with keys) |
import { afterAll, beforeAll, expect, test } from "bun:test" | ||
import { BunSqliteKeyValue } from "../src" | ||
import { join, resolve } from "node:path" | ||
import { tmpdir } from 'node:os' | ||
import { mkdtemp } from 'node:fs/promises' | ||
import { mkdtemp, rmdir } from 'node:fs/promises' | ||
import { rm, exists } from "node:fs/promises" | ||
import { BunSqliteKeyValue } from "../src" | ||
@@ -58,9 +58,11 @@ | ||
afterAll(async () => { | ||
// Remove temp files | ||
if (await exists(dbPath)) { | ||
await rm(dbPath) | ||
// Remove all | ||
const glob = new Bun.Glob("*") | ||
for await (const fileName of glob.scan({cwd: tmpDirname})) { | ||
const filePath = join(tmpDirname, fileName) | ||
await rm(filePath) | ||
} | ||
if (await exists(targetImagePath)) { | ||
await rm(targetImagePath) | ||
if (await exists(tmpDirname)) { | ||
await rmdir(tmpDirname) | ||
} | ||
}) |
@@ -5,3 +5,3 @@ import { beforeAll, afterAll, expect, test } from "bun:test" | ||
import { mkdtemp } from 'node:fs/promises' | ||
import { rm, exists } from "node:fs/promises" | ||
import { rm, rmdir, exists } from "node:fs/promises" | ||
import { BunSqliteKeyValue } from "../src" | ||
@@ -15,2 +15,3 @@ | ||
let dbDir: string | ||
let dbPath: string | ||
@@ -20,4 +21,4 @@ | ||
beforeAll(async () => { | ||
const dirname = await mkdtemp(join(tmpdir(), "bun-sqlite-key-value")) | ||
dbPath = join(dirname, "filesystemtest.sqlite") | ||
dbDir = await mkdtemp(join(tmpdir(), "bun-sqlite-key-value")) | ||
dbPath = join(dbDir, "filesystemtest.sqlite") | ||
console.log("SQLite database path:", dbPath) | ||
@@ -59,8 +60,37 @@ }) | ||
test("Get items as array (extended tests)", () => { | ||
const store: BunSqliteKeyValue = new BunSqliteKeyValue(dbPath) | ||
store.set<string>("a:1:", STRING_VALUE_1) | ||
store.set<string>("a:1:" + String.fromCodePoint(2), STRING_VALUE_1) | ||
store.set<string>("a:1:" + String.fromCodePoint(1_000_000), STRING_VALUE_1) | ||
store.set<string>("a:1:*", STRING_VALUE_1) | ||
store.set<string>("a:2:a", STRING_VALUE_1) | ||
// Add many additional records | ||
const items = [] | ||
for (let i = 0; i < 1_000; i++) { | ||
items.push({key: "a:1:" + String(i), value: STRING_VALUE_1}) | ||
} | ||
store.setItems(items) | ||
// Tests | ||
expect(store.getItems("a:1:*")).toHaveLength(1) | ||
expect(store.getItems("a:1:" + String.fromCodePoint(2))).toHaveLength(1) | ||
expect(store.getItems("a:1:" + String.fromCodePoint(1_000_000))).toHaveLength(1) | ||
expect(store.getValues("a:1:")).toHaveLength(1_004) | ||
expect(store.getValues("a:1:55")).toHaveLength(11) | ||
}) | ||
afterAll(async () => { | ||
// Remove test database | ||
if (await exists(dbPath)) { | ||
await rm(dbPath) | ||
// Remove all | ||
const glob = new Bun.Glob("*") | ||
for await (const fileName of glob.scan({cwd: dbDir})) { | ||
const filePath = join(dbDir, fileName) | ||
await rm(filePath) | ||
} | ||
if (await exists(dbDir)) { | ||
await rmdir(dbDir) | ||
} | ||
}) | ||
51556
794
476