Skip to content

Commit

Permalink
Merge pull request #32 from oliver-oloughlin/feature/pagination-docum…
Browse files Browse the repository at this point in the history
…entation-update

Feature/pagination documentation update
  • Loading branch information
oliver-oloughlin authored Jul 15, 2023
2 parents 2dd1fe7 + ef3fe17 commit b4865e2
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 45 deletions.
25 changes: 15 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# KVDEX
# kvdex

Database wrapper for Deno's KV store. Simple and type-safe storing/retrieving of
data.
Expand All @@ -14,7 +14,7 @@ Optional and nullable properties are allowed. If you wish to use Zod, you can
create your Zod object schema and use its type as your model.

```ts
import type { Model } from "https://deno.land/x/kvdex@v0.3.2/mod.ts"
import type { Model } from "https://deno.land/x/kvdex@v0.4.0/mod.ts"

interface User extends Model {
username: string
Expand All @@ -35,7 +35,7 @@ The "createDb" function is used for creating a new database instance. It takes a
Deno KV instance and a schema builder function as arguments.

```ts
import { createDb } from "https://deno.land/x/kvdex@v0.3.2/mod.ts"
import { createDb } from "https://deno.land/x/kvdex@v0.4.0/mod.ts"

const kv = await Deno.openKv()

Expand Down Expand Up @@ -221,20 +221,20 @@ documents in the collection.

```ts
// Retrieves all user documents
const allUsers = await db.users.getMany()
const { result } = await db.users.getMany()

// Retrieves all user documents where the user's age is above or equal to 18
const canBasciallyDrinkEverywhereExceptUSA = await db.users.getMany({
const { result } = await db.users.getMany({
filter: (doc) => doc.value.age >= 18,
})

// Retrieves the first 10 user documents in the KV store
const first10 = await db.users.getMany({
const { result } = await db.users.getMany({
limit: 10,
})

// Retrieves the last 10 user documents in the KV store
const last10 = await db.users.getMany({
const { result } = await db.users.getMany({
limit: 10,
reverse: true,
})
Expand Down Expand Up @@ -295,7 +295,7 @@ const userDoc = await db.indexableUsers.findByPrimaryIndex({
age: 24,
})

// Will return null as age is not defined as a primary index.
// Will return null as age is not defined as a primary index
const notFound = await db.indexableUsers.findByPrimaryIndex({
age: 24,
})
Expand All @@ -315,7 +315,7 @@ const userDocs = await db.indexableUsers.findBySecondaryIndex({
age: 24,
})

// Returns empty list as username is not defined as a secondary index
// Returns empty list, as username is not defined as a secondary index
const empty = await db.indexableUsers.findBySecondaryIndex({
username: "oliver",
})
Expand Down Expand Up @@ -425,7 +425,7 @@ result will be an object containing: id, versionstamp and all the entries in the
document value.

```ts
import { flatten } from "https://deno.land/x/kvdex@v0.3.2/mod.ts"
import { flatten } from "https://deno.land/x/kvdex@v0.4.0/mod.ts"

// We assume the document exists in the KV store
const doc = await db.users.find(123n)
Expand Down Expand Up @@ -453,10 +453,15 @@ Any contributions are welcomed and appreciated. How to contribute:

- Clone this repository
- Add feature / Refactor
- Add or refactor tests as needed
- Run tests using `deno task test`
- Prepare code (format + test) using `deno task prep`
- Open Pull Request

This project aims at having as high test coverage as possible to improve code quality and to avoid breaking features when refactoring. Therefore it is encouraged that any feature contributions are also accompanied by relevant unit tests to ensure those features remain stable.

The goal of kvdex is to provide a type safe, higher level API to Deno KV, while trying to retain as much of the native functionality as possible. Additionally, this module should be light-weight and should not rely on any third-party dependencies. Please kleep this in mind when making any contributions.

## License

Published under [MIT License](./LICENSE.md)
10 changes: 5 additions & 5 deletions src/collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,7 @@ export class Collection<const T extends KvValue> {
}
}
return {
cursor: iter.cursor,
cursor: iter.cursor || undefined,
}
}

Expand All @@ -347,10 +347,10 @@ export class Collection<const T extends KvValue> {
* **Example:**
* ```ts
* // Get all users
* const userDocs1 = await db.users.getMany()
* const { result } = await db.users.getMany()
*
* // Only get users with username that starts with "a"
* const userDocs2 = await db.users.getMany({
* const { result } = await db.users.getMany({
* filter: doc => doc.value.username.startsWith("a")
* })
* ```
Expand All @@ -377,7 +377,7 @@ export class Collection<const T extends KvValue> {

return {
result,
cursor: iter.cursor,
cursor: iter.cursor || undefined,
}
}

Expand Down Expand Up @@ -417,7 +417,7 @@ export class Collection<const T extends KvValue> {
if (!options?.filter || options.filter(doc)) fn(doc)
}
return {
cursor: iter.cursor,
cursor: iter.cursor || undefined,
}
}
}
22 changes: 10 additions & 12 deletions src/indexable_collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import type {
KvKey,
ListOptions,
Model,
NoPaginationListOptions,
PrimaryIndexSelection,
SecondaryIndexSelection,
} from "./types.ts"
Expand Down Expand Up @@ -246,14 +247,15 @@ export class IndexableCollection<
*/
async findBySecondaryIndex(
selection: SecondaryIndexSelection<T1, T2>,
options?: ListOptions<T1>,
options?: NoPaginationListOptions<T1>,
) {
const indexList = Object.entries(selection).filter(([_, value]) =>
typeof value !== "undefined"
) as [string, KvId][]
if (indexList.length < 1) return { result: [], cursor: undefined }
const indexList = Object.entries(selection)
.filter(([_, value]) => typeof value !== "undefined") as [string, KvId][]

if (indexList.length < 1) {
return []
}

let cursor = ""
const result: Document<T1>[] = []
const keys = indexList.map(([index, indexValue]) =>
extendKey(this.keys.secondaryIndexKey, index, indexValue)
Expand All @@ -277,12 +279,8 @@ export class IndexableCollection<
result.push(doc)
}
}
cursor = iter.cursor
}
return {
result,
cursor,
}
return result
}

async delete(id: KvId) {
Expand Down Expand Up @@ -353,7 +351,7 @@ export class IndexableCollection<
await atomic.commit()
}
return {
cursor: iter.cursor,
cursor: iter.cursor || undefined,
}
}
}
5 changes: 5 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,11 @@ export type ListOptions<T extends KvValue> = Deno.KvListOptions & {
filter?: (doc: Document<T>) => boolean
}

export type NoPaginationListOptions<T extends KvValue> = Omit<
ListOptions<T>,
"cursor" | "limit"
>

export type FindOptions = Parameters<Deno.Kv["get"]>[1]

export type FindManyOptions = Parameters<Deno.Kv["getMany"]>[1]
Expand Down
67 changes: 67 additions & 0 deletions test/tests/collection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,26 @@ Deno.test({
assert(allPeople2.result.length === 1)
},
})

await t2.step("Should delete all records using pagination", async () => {
await reset()

await db.people.add(testPerson)
await db.people.add(testPerson)
await db.people.add(testPerson)

const allPeople1 = await db.people.getMany()
assert(allPeople1.result.length === 3)

let cursor: string | undefined
do {
const r = await db.people.deleteMany({ cursor, limit: 1 })
cursor = r.cursor
} while (cursor)

const allPeople2 = await db.people.getMany()
assert(allPeople2.result.length === 0)
})
})

// Test "getMany" method
Expand Down Expand Up @@ -517,6 +537,27 @@ Deno.test({
assert(allPeople2.result.length === 2)
},
})

await t.step("Should get all documents by pagination", async () => {
await reset()

await db.people.add(testPerson)
await db.people.add(testPerson)
await db.people.add(testPerson)

const allPeople1 = await db.people.getMany()
assert(allPeople1.result.length === 3)

let cursor: string | undefined
const result = []
do {
const data = await db.people.getMany({ cursor, limit: 1 })
result.push(...data.result)
cursor = data.cursor
} while (cursor)

assert(result.length === 3)
})
},
})

Expand Down Expand Up @@ -579,6 +620,32 @@ Deno.test({
assert(!list.some((doc) => doc.id === id2))
},
})

await t.step(
"Should add all documents to list by pagination",
async () => {
await reset()

await db.people.add(testPerson)
await db.people.add(testPerson)
await db.people.add(testPerson)

const allPeople1 = await db.people.getMany()
assert(allPeople1.result.length === 3)

let cursor: string | undefined
const result: Document<Person>[] = []
do {
const data = await db.people.forEach((doc) => result.push(doc), {
cursor,
limit: 1,
})
cursor = data.cursor
} while (cursor)

assert(result.length === 3)
},
)
},
})

Expand Down
10 changes: 5 additions & 5 deletions test/tests/db.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,8 +235,8 @@ Deno.test("db", async (t1) => {
const indexDocs = await db.indexablePeople.findBySecondaryIndex({
age: 24,
})
assert(indexDocs.result.some((doc) => doc.id === indexDoc1.id))
assert(indexDocs.result.some((doc) => doc.id === id2))
assert(indexDocs.some((doc) => doc.id === indexDoc1.id))
assert(indexDocs.some((doc) => doc.id === id2))
},
)

Expand Down Expand Up @@ -272,7 +272,7 @@ Deno.test("db", async (t1) => {
const indexDocs = await db.indexablePeople.findBySecondaryIndex({
age: 24,
})
assert(indexDocs.result.length === 0)
assert(indexDocs.length === 0)
})

await t2.step(
Expand Down Expand Up @@ -302,7 +302,7 @@ Deno.test("db", async (t1) => {
const indexDocs1 = await db.indexablePeople.findBySecondaryIndex({
age: 24,
})
assert(indexDocs1.result.some((doc) => doc.id === idDoc1.id))
assert(indexDocs1.some((doc) => doc.id === idDoc1.id))

await db
.atomic((schema) => schema.indexablePeople)
Expand All @@ -323,7 +323,7 @@ Deno.test("db", async (t1) => {
const indexDocs2 = await db.indexablePeople.findBySecondaryIndex({
age: 24,
})
assert(indexDocs2.result.length === 0)
assert(indexDocs2.length === 0)
},
)

Expand Down
Loading

0 comments on commit b4865e2

Please sign in to comment.