@toa.io/storages.mongodb 1.0.0-alpha.31 → 1.0.0-alpha.310
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +203 -0
- package/package.json +10 -9
- package/readme.md +15 -0
- package/src/client.js +173 -39
- package/src/factory.js +6 -12
- package/src/inbox.js +85 -0
- package/src/index.js +1 -7
- package/src/indexes.js +84 -0
- package/src/measurements.js +39 -0
- package/src/migrations.js +343 -0
- package/src/outbox.js +131 -0
- package/src/record.js +88 -20
- package/src/storage.js +497 -130
- package/src/translate/criteria.js +25 -16
- package/src/translate/options.js +17 -11
- package/src/translate/rename.js +1 -5
- package/src/translate.js +14 -15
- package/test/migrations.test.js +388 -0
- package/test/query/criteria.fixtures.js +2 -7
- package/test/query/criteria.test.js +5 -4
- package/test/record.test.js +98 -25
- package/test/storage.test.js +217 -0
- package/test/translate.test.js +59 -7
- package/types/connection.d.ts +18 -12
- package/types/pointer.d.ts +4 -6
- package/types/record.d.ts +5 -7
- package/src/collection.js +0 -69
- package/src/deployment.js +0 -22
package/src/record.js
CHANGED
|
@@ -1,32 +1,100 @@
|
|
|
1
|
-
|
|
1
|
+
export function to(entity) {
|
|
2
|
+
const { id, ...rest } = entity
|
|
3
|
+
|
|
4
|
+
return /** @type {toa.mongodb.Record} */ { _id: id, ...rest }
|
|
5
|
+
}
|
|
2
6
|
|
|
3
7
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
8
|
+
* Renamed in the object the driver decoded, which is a new one for every document; an entity
|
|
9
|
+
* copies a state before an operation that writes changes it. A copy costs ten times the rename,
|
|
10
|
+
* and what it would keep is only `id` in front, which a reply does not promise.
|
|
6
11
|
*/
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
id,
|
|
10
|
-
...rest
|
|
11
|
-
} = entity
|
|
12
|
+
export function from(record) {
|
|
13
|
+
if (record === undefined || record === null) return null
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
record.id = record._id
|
|
16
|
+
delete record._id
|
|
17
|
+
|
|
18
|
+
return record
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
22
|
+
* How this collection's records are written and read back.
|
|
23
|
+
*
|
|
24
|
+
* A property the entity declares as a moment is held as a BSON date rather than as what the
|
|
25
|
+
* entity carries. It is what the TTL monitor reads — it skips a field that is not a date,
|
|
26
|
+
* silently — and what sorts and compares as a moment rather than as text or as a number that
|
|
27
|
+
* happens to be one. The entity keeps what it declared, so nothing outside this connector
|
|
28
|
+
* learns a type only one storage has.
|
|
29
|
+
*
|
|
30
|
+
* Two ways to say it, because they differ in what userland holds rather than in what is
|
|
31
|
+
* stored: `{ string, date-time }` for an application that carries ISO strings, and
|
|
32
|
+
* `{ integer, epoch-millis }` for one that carries what `Date.now()` answers. The system
|
|
33
|
+
* timestamps of every record are the second.
|
|
34
|
+
*
|
|
35
|
+
* Top-level properties only. One nested inside an object or an array is left as it is, and
|
|
36
|
+
* `norm` refuses it, so that the declaration does not mean two things.
|
|
37
|
+
*
|
|
38
|
+
* A component that declares no moment holds the plain pair, and pays nothing for any of this.
|
|
19
39
|
*/
|
|
20
|
-
|
|
21
|
-
|
|
40
|
+
export function codec(properties) {
|
|
41
|
+
/** @type {Array<[string, (value: Date) => unknown]>} */
|
|
42
|
+
const read = []
|
|
43
|
+
|
|
44
|
+
for (const [name, schema] of Object.entries(properties ?? {})) {
|
|
45
|
+
const cast = READ[schema?.format]
|
|
46
|
+
|
|
47
|
+
if (cast !== undefined && schema.type === TYPES[schema.format]) read.push([name, cast])
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const dates = read.map(([name]) => name)
|
|
51
|
+
|
|
52
|
+
if (dates.length === 0) return { to, from, dates }
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
dates,
|
|
22
56
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
...rest
|
|
26
|
-
} = record
|
|
57
|
+
// one way in for both: `Date` takes the milliseconds and the ISO string alike
|
|
58
|
+
to: (entity) => convert(to(entity), dates.map((name) => [name, date])),
|
|
27
59
|
|
|
28
|
-
|
|
60
|
+
from: (record) => {
|
|
61
|
+
const state = from(record)
|
|
62
|
+
|
|
63
|
+
return state === null ? null : convert(state, read)
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Writes into an object either `to` or `from` has just made, so nothing the caller holds is
|
|
70
|
+
* touched.
|
|
71
|
+
*
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
function convert(record, casts) {
|
|
75
|
+
for (const [name, cast] of casts) {
|
|
76
|
+
const value = record[name]
|
|
77
|
+
|
|
78
|
+
// absent, or the null a property that has not been written to holds
|
|
79
|
+
if (value === undefined || value === null) continue
|
|
80
|
+
|
|
81
|
+
record[name] = cast(value)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return record
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const date = (value) => new Date(value)
|
|
88
|
+
|
|
89
|
+
/*
|
|
90
|
+
* What a record written before the property was declared a moment holds is what it was given,
|
|
91
|
+
* and it is answered as it is: a collection is converted by a migration rather than by every
|
|
92
|
+
* read that finds one.
|
|
93
|
+
*/
|
|
94
|
+
const READ = {
|
|
95
|
+
'date-time': (value) => (value instanceof Date ? value.toISOString() : value),
|
|
96
|
+
'epoch-millis': (value) => (value instanceof Date ? value.getTime() : value)
|
|
29
97
|
}
|
|
30
98
|
|
|
31
|
-
|
|
32
|
-
|
|
99
|
+
/** What each format is written on, so that one said of the wrong type is not acted on. */
|
|
100
|
+
const TYPES = { 'date-time': 'string', 'epoch-millis': 'integer' }
|