Skip to content

Commit 1e92a94

Browse files
refactored tests for improved structure and perfromance
1 parent 2466d93 commit 1e92a94

File tree

91 files changed

+4357
-4630
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+4357
-4630
lines changed

.github/workflows/test.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ on:
1616

1717
permissions:
1818
contents: read
19-
19+
2020
jobs:
2121
test:
2222
runs-on: ubuntu-latest
@@ -37,4 +37,4 @@ jobs:
3737
run: deno lint
3838

3939
- name: Run tests
40-
run: deno task test
40+
run: deno test -A --unstable

README.md

+9-11
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,8 @@ much of the native functionality as possible, such as atomic operations.
5959
- [Atomic Operations](#atomic-operations)
6060
- [Without checking](#without-checking)
6161
- [With checking](#with-checking)
62-
- [Utils](#utils)
63-
- [flatten()](#flatten)
62+
- [Document Methods](#document-methods)
63+
- [flat()](#flat)
6464
- [Development](#development)
6565
- [License](#license)
6666

@@ -750,22 +750,20 @@ while (!result || !result.ok) {
750750
}
751751
```
752752

753-
## Utils
753+
## Document Methods
754754

755-
Additional utility functions.
755+
Document functions.
756756

757-
### flatten()
757+
### flat()
758758

759-
Flatten documents with a value of type Model. Only flattens the first layer of
760-
the document, meaning the result will be an object containing: id, versionstamp
761-
and all the entries in the document value.
759+
Flatten top layer of document data. Returns an object containing the id,
760+
versionstamp and value entries for documents of type Model, else simply returns
761+
the document data.
762762

763763
```ts
764-
import { flatten } from "https://deno.land/x/kvdex/mod.ts"
765-
766764
// We assume the document exists in the KV store
767765
const doc = await db.users.find(123n)
768-
const flattened = flatten(doc)
766+
const flattened = doc.flat()
769767

770768
// Document:
771769
// {

deno.jsonc

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,6 @@
77
"semiColons": false
88
},
99
"test": {
10-
"include": ["./test/tests"]
10+
"include": ["./tests"]
1111
}
1212
}

mod.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
// Expose kvdex, collection builders and utils functions
2-
export { kvdex } from "./src/db.ts"
3-
export * from "./src/utils.ts"
1+
// Expose kvdex and collection builders
2+
export { kvdex } from "./src/kvdex.ts"
43
export {
54
collection,
65
indexableCollection,
@@ -12,6 +11,10 @@ export { Collection } from "./src/collection.ts"
1211
export { IndexableCollection } from "./src/indexable_collection.ts"
1312
export { LargeCollection } from "./src/large_collection.ts"
1413
export { AtomicBuilder } from "./src/atomic_builder.ts"
14+
export { Document } from "./src/document.ts"
15+
16+
// Expose errors
17+
export * from "./src/errors.ts"
1518

1619
// Expose types
1720
export type * from "./src/types.ts"

src/atomic_builder.ts

+58-2
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
keyEq,
2626
prepareEnqueue,
2727
setIndices,
28-
} from "./utils.internal.ts"
28+
} from "./utils.ts"
2929

3030
/**
3131
* Builder object for creating and executing atomic operations in the KV store.
@@ -58,7 +58,9 @@ export class AtomicBuilder<
5858
) {
5959
// Check for large collection
6060
if (collection instanceof LargeCollection) {
61-
throw new InvalidAtomicBuilderCollectionError()
61+
throw new InvalidAtomicBuilderCollectionError(
62+
"Atomic operations are not supported for LargeCollection",
63+
)
6264
}
6365

6466
// Set kv and schema
@@ -281,6 +283,60 @@ export class AtomicBuilder<
281283
return this
282284
}
283285

286+
/**
287+
* Sets the document value to the minimum of the existing and the given value.
288+
*
289+
* min only works for documents of type Deno.KvU64 and will throw an error for documents of any other type.
290+
*
291+
* @example
292+
* ```ts
293+
* db
294+
* .atomic(schema => schema.u64s) // Select collection of Deno.KvU64 values
295+
* .min("num1", 100n)
296+
* ```
297+
*
298+
* @param id - Id of document that contains the value to be updated.
299+
* @param value - The value to compare with the existing value.
300+
* @returns Current AtomicBuilder instance.
301+
*/
302+
min(id: KvId, value: T2 extends Deno.KvU64 ? bigint : never) {
303+
// Create id key from id and collection id key
304+
const idKey = extendKey(this.collection._keys.idKey, id)
305+
306+
// Add min operation to atomic ops list
307+
this.operations.atomic.min(idKey, value)
308+
309+
// Return current AtomicBuilder
310+
return this
311+
}
312+
313+
/**
314+
* Sets the document value to the maximum of the existing and the given value.
315+
*
316+
* max only works for documents of type Deno.KvU64 and will throw an error for documents of any other type.
317+
*
318+
* @example
319+
* ```ts
320+
* db
321+
* .atomic(schema => schema.u64s) // Select collection of Deno.KvU64 values
322+
* .max("num1", 100n)
323+
* ```
324+
*
325+
* @param id - Id of document that contains the value to be updated.
326+
* @param value - The value to compare with the existing value.
327+
* @returns Current AtomicBuilder instance.
328+
*/
329+
max(id: KvId, value: T2 extends Deno.KvU64 ? bigint : never) {
330+
// Create id key from id and collection id key
331+
const idKey = extendKey(this.collection._keys.idKey, id)
332+
333+
// Add max operation to atomic ops list
334+
this.operations.atomic.max(idKey, value)
335+
336+
// Return current AtomicBuilder
337+
return this
338+
}
339+
284340
/**
285341
* Specifies atomic mutations to be formed on documents.
286342
*

src/collection.ts

+23-25
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import {
2-
COLLECTION_ID_KEY_SUFFIX,
2+
ID_KEY_PREFIX,
33
KVDEX_KEY_PREFIX,
4-
UNDELIVERED_KEY_SUFFIX,
4+
UNDELIVERED_KEY_PREFIX,
55
} from "./constants.ts"
66
import type {
77
CollectionKeys,
88
CollectionOptions,
99
CommitResult,
1010
CountOptions,
11-
Document,
1211
EnqueueOptions,
1312
FindManyOptions,
1413
FindOptions,
@@ -34,7 +33,8 @@ import {
3433
kvGetMany,
3534
parseQueueMessage,
3635
prepareEnqueue,
37-
} from "./utils.internal.ts"
36+
} from "./utils.ts"
37+
import { Document } from "./document.ts"
3838

3939
/**
4040
* Represents a collection of documents stored in the KV store.
@@ -74,7 +74,7 @@ export class Collection<
7474
idKey: extendKey(
7575
[KVDEX_KEY_PREFIX],
7676
...key,
77-
COLLECTION_ID_KEY_SUFFIX,
77+
ID_KEY_PREFIX,
7878
),
7979
}
8080
}
@@ -105,15 +105,12 @@ export class Collection<
105105
return null
106106
}
107107

108-
// Create the document
109-
const doc: Document<T1> = {
108+
// Return document
109+
return new Document<T1>({
110110
id,
111111
versionstamp: result.versionstamp,
112112
value: result.value,
113-
}
114-
115-
// Return the document
116-
return doc
113+
})
117114
}
118115

119116
/**
@@ -153,11 +150,13 @@ export class Collection<
153150
}
154151

155152
// Add document to result list
156-
result.push({
157-
id,
158-
versionstamp,
159-
value,
160-
})
153+
result.push(
154+
new Document<T1>({
155+
id,
156+
versionstamp,
157+
value,
158+
}),
159+
)
161160
}
162161

163162
// Return result list
@@ -210,6 +209,8 @@ export class Collection<
210209
*
211210
* Sets a new document entry if no matching id already exists, overwrites the exisiting entry if it exists.
212211
*
212+
* Does not overwrite existing entries if there is a primary index collision.
213+
*
213214
* @example
214215
* ```ts
215216
* const result = await db.users.write("anders", {
@@ -613,23 +614,20 @@ export class Collection<
613614
options?: FindOptions,
614615
) {
615616
// Create document key, get document entry
616-
const key = extendKey(this._keys.idKey, UNDELIVERED_KEY_SUFFIX, id)
617+
const key = extendKey(this._keys.baseKey, UNDELIVERED_KEY_PREFIX, id)
617618
const result = await this.kv.get<T>(key, options)
618619

619620
// If no entry exists, return null
620621
if (result.value === null || result.versionstamp === null) {
621622
return null
622623
}
623624

624-
// Create the document
625-
const doc: Document<T> = {
625+
// Return document
626+
return new Document<T>({
626627
id,
627628
versionstamp: result.versionstamp,
628629
value: result.value,
629-
}
630-
631-
// Return the document
632-
return doc
630+
})
633631
}
634632

635633
/** PROTECTED METHODS */
@@ -659,11 +657,11 @@ export class Collection<
659657
}
660658

661659
// Create document
662-
const doc: Document<T1> = {
660+
const doc = new Document<T1>({
663661
id,
664662
versionstamp,
665663
value: value,
666-
}
664+
})
667665

668666
// Filter document and add to documents list
669667
if (!options?.filter || options.filter(doc)) {

src/constants.ts

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
1-
// Key constants
1+
// Key prefixes
22
export const KVDEX_KEY_PREFIX = "__kvdex__"
33

4-
export const COLLECTION_ID_KEY_SUFFIX = "__id__"
4+
export const ID_KEY_PREFIX = "__id__"
55

6-
export const COLLECTION_PRIMARY_INDEX_KEY_SUFFIX = "__index_primary__"
6+
export const PRIMARY_INDEX_KEY_PREFIX = "__index_primary__"
77

8-
export const COLLECTION_SECONDARY_INDEX_KEY_SUFFIX = "__index_secondary__"
8+
export const SECONDARY_INDEX_KEY_PREFIX = "__index_secondary__"
99

10-
export const COLLECTION_SEGMENT_KEY_SUFFIX = "__segment__"
10+
export const SEGMENT_KEY_PREFIX = "__segment__"
1111

12-
export const UNDELIVERED_KEY_SUFFIX = "__undelivered__"
12+
export const UNDELIVERED_KEY_PREFIX = "__undelivered__"
1313

14-
// Limit constants
14+
// Fixed limits
1515
export const ATOMIC_OPERATION_MUTATION_LIMIT = 20
1616

1717
export const GET_MANY_KEY_LIMIT = 10

src/document.ts

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import type {
2+
DocumentData,
3+
FlatDocumentData,
4+
KvId,
5+
KvObject,
6+
KvValue,
7+
KvVersionstamp,
8+
} from "./types.ts"
9+
import { isKvObject } from "./utils.ts"
10+
11+
export class Document<T extends KvValue> {
12+
readonly id: KvId
13+
readonly versionstamp: KvVersionstamp<T>
14+
readonly value: T
15+
16+
constructor({
17+
id,
18+
versionstamp,
19+
value,
20+
}: DocumentData<T>) {
21+
this.id = id
22+
this.versionstamp = versionstamp
23+
this.value = value
24+
}
25+
26+
/**
27+
* Flatten top layer of document data. Returns an object containing the id,
28+
* versionstamp and value entries for documents of type Model, else simply returns
29+
* the document data.
30+
*
31+
* @example
32+
* ```ts
33+
* // We assume the document exists in the KV store
34+
* const doc = await db.users.find(123n)
35+
* const flattened = doc.flat()
36+
*
37+
* // Document:
38+
* // {
39+
* // id,
40+
* // versionstamp,
41+
* // value
42+
* // }
43+
*
44+
* // Flattened:
45+
* // {
46+
* // id,
47+
* // versionstamp,
48+
* // ...value
49+
* // }
50+
* ```
51+
*
52+
* @returns Object containing the id, versionstamp and value entries
53+
* for documents of type Model, else simply returns the document data.
54+
*/
55+
flat(): FlatDocumentData<T> {
56+
if (isKvObject(this.value)) {
57+
return {
58+
id: this.id,
59+
versionstamp: this.versionstamp,
60+
...this.value as KvObject,
61+
} as unknown as FlatDocumentData<T>
62+
}
63+
64+
return {
65+
id: this.id,
66+
versionstamp: this.versionstamp,
67+
value: this.value,
68+
} as unknown as FlatDocumentData<T>
69+
}
70+
}

src/errors.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
export class InvalidAtomicBuilderCollectionError extends Error {
2-
constructor(options?: ErrorOptions) {
3-
super("Atomic operations are not supported for large collections", options)
2+
constructor(message?: string, options?: ErrorOptions) {
3+
super(message, options)
4+
}
5+
}
6+
7+
export class CorruptedDocumentDataError extends Error {
8+
constructor(message?: string, options?: ErrorOptions) {
9+
super(message, options)
410
}
511
}

0 commit comments

Comments
 (0)