@toa.io/storages.mongodb 1.0.0-alpha.35 → 1.0.0-alpha.37
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/package.json +7 -7
- package/src/client.js +1 -3
- package/src/factory.js +1 -3
- package/src/record.js +2 -3
- package/src/storage.js +71 -44
- package/src/collection.js +0 -71
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/storages.mongodb",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.37",
|
|
4
4
|
"description": "Toa MongoDB Storage Connector",
|
|
5
5
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/toa-io/toa#readme",
|
|
@@ -19,13 +19,13 @@
|
|
|
19
19
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@toa.io/console": "1.0.0-alpha.
|
|
23
|
-
"@toa.io/conveyor": "1.0.0-alpha.
|
|
24
|
-
"@toa.io/core": "1.0.0-alpha.
|
|
25
|
-
"@toa.io/generic": "1.0.0-alpha.
|
|
26
|
-
"@toa.io/pointer": "1.0.0-alpha.
|
|
22
|
+
"@toa.io/console": "1.0.0-alpha.37",
|
|
23
|
+
"@toa.io/conveyor": "1.0.0-alpha.37",
|
|
24
|
+
"@toa.io/core": "1.0.0-alpha.37",
|
|
25
|
+
"@toa.io/generic": "1.0.0-alpha.37",
|
|
26
|
+
"@toa.io/pointer": "1.0.0-alpha.37",
|
|
27
27
|
"mongodb": "6.3.0",
|
|
28
28
|
"saslprep": "1.0.3"
|
|
29
29
|
},
|
|
30
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "e78a9aedb64a52dc34b9271fd90791934863d0a6"
|
|
31
31
|
}
|
package/src/client.js
CHANGED
package/src/factory.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const { Client } = require('./client')
|
|
4
|
-
const { Collection } = require('./collection')
|
|
5
4
|
const { Storage } = require('./storage')
|
|
6
5
|
|
|
7
6
|
class Factory {
|
|
8
7
|
storage (locator, entity) {
|
|
9
8
|
const client = new Client(locator)
|
|
10
|
-
const connection = new Collection(client)
|
|
11
9
|
|
|
12
|
-
return new Storage(
|
|
10
|
+
return new Storage(client, entity)
|
|
13
11
|
}
|
|
14
12
|
}
|
|
15
13
|
|
package/src/record.js
CHANGED
|
@@ -10,10 +10,9 @@ function from (record) {
|
|
|
10
10
|
if (record === undefined || record === null)
|
|
11
11
|
return null
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
delete record._id
|
|
13
|
+
const { _id, ...rest } = record
|
|
15
14
|
|
|
16
|
-
return
|
|
15
|
+
return { id: _id, ...rest }
|
|
17
16
|
}
|
|
18
17
|
|
|
19
18
|
exports.to = to
|
package/src/storage.js
CHANGED
|
@@ -4,34 +4,40 @@ const { Connector, exceptions } = require('@toa.io/core')
|
|
|
4
4
|
|
|
5
5
|
const { translate } = require('./translate')
|
|
6
6
|
const { to, from } = require('./record')
|
|
7
|
+
const { ReturnDocument } = require('mongodb')
|
|
7
8
|
|
|
8
9
|
class Storage extends Connector {
|
|
9
|
-
#
|
|
10
|
+
#client
|
|
11
|
+
|
|
12
|
+
/** @type {import('mongodb').Collection} */
|
|
13
|
+
#collection
|
|
10
14
|
#entity
|
|
11
15
|
|
|
12
|
-
constructor (
|
|
16
|
+
constructor (client, entity) {
|
|
13
17
|
super()
|
|
14
18
|
|
|
15
|
-
this.#
|
|
19
|
+
this.#client = client
|
|
16
20
|
this.#entity = entity
|
|
17
21
|
|
|
18
|
-
this.depends(
|
|
22
|
+
this.depends(client)
|
|
19
23
|
}
|
|
20
24
|
|
|
21
25
|
async open () {
|
|
26
|
+
this.#collection = this.#client.collection
|
|
27
|
+
|
|
22
28
|
await this.index()
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
async get (query) {
|
|
26
32
|
const { criteria, options } = translate(query)
|
|
27
|
-
const record = await this.#
|
|
33
|
+
const record = await this.#collection.findOne(criteria, options)
|
|
28
34
|
|
|
29
35
|
return from(record)
|
|
30
36
|
}
|
|
31
37
|
|
|
32
38
|
async find (query) {
|
|
33
39
|
const { criteria, options } = translate(query)
|
|
34
|
-
const recordset = await this.#
|
|
40
|
+
const recordset = await this.#collection.find(criteria, options).toArray()
|
|
35
41
|
|
|
36
42
|
return recordset.map((item) => from(item))
|
|
37
43
|
}
|
|
@@ -39,12 +45,12 @@ class Storage extends Connector {
|
|
|
39
45
|
async stream (query = undefined) {
|
|
40
46
|
const { criteria, options } = translate(query)
|
|
41
47
|
|
|
42
|
-
return await this.#
|
|
48
|
+
return await this.#collection.find(criteria, options).stream({ transform: from })
|
|
43
49
|
}
|
|
44
50
|
|
|
45
51
|
async add (entity) {
|
|
46
52
|
const record = to(entity)
|
|
47
|
-
const result = await this.#
|
|
53
|
+
const result = await this.#collection.insertOne(record)
|
|
48
54
|
|
|
49
55
|
return result.acknowledged
|
|
50
56
|
}
|
|
@@ -54,41 +60,43 @@ class Storage extends Connector {
|
|
|
54
60
|
_id: entity.id,
|
|
55
61
|
_version: entity._version - 1
|
|
56
62
|
}
|
|
57
|
-
|
|
63
|
+
|
|
64
|
+
const result = await this.#collection.findOneAndReplace(criteria, to(entity))
|
|
58
65
|
|
|
59
66
|
return result !== null
|
|
60
67
|
}
|
|
61
68
|
|
|
62
|
-
async store (entity) {
|
|
69
|
+
async store (entity, attempt = 0) {
|
|
63
70
|
try {
|
|
64
|
-
if (entity._version === 1)
|
|
71
|
+
if (entity._version === 1)
|
|
65
72
|
return await this.add(entity)
|
|
66
|
-
|
|
73
|
+
else
|
|
67
74
|
return await this.set(entity)
|
|
68
|
-
}
|
|
69
75
|
} catch (error) {
|
|
70
76
|
if (error.code === ERR_DUPLICATE_KEY) {
|
|
71
|
-
|
|
72
77
|
const id = error.keyPattern === undefined
|
|
73
78
|
? error.message.includes(' index: _id_ ') // AWS DocumentDB
|
|
74
79
|
: error.keyPattern._id === 1
|
|
75
80
|
|
|
76
|
-
if (id)
|
|
81
|
+
if (id)
|
|
77
82
|
return false
|
|
78
|
-
|
|
83
|
+
else
|
|
79
84
|
throw new exceptions.DuplicateException()
|
|
80
|
-
|
|
81
|
-
|
|
85
|
+
} else if (error.cause?.code === 'ECONNREFUSED') {
|
|
86
|
+
// This is temporary and should be replaced with a class decorator.
|
|
87
|
+
if (attempt > 10)
|
|
88
|
+
throw error
|
|
89
|
+
|
|
90
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
91
|
+
|
|
92
|
+
return this.store(entity)
|
|
93
|
+
} else
|
|
82
94
|
throw error
|
|
83
|
-
}
|
|
84
95
|
}
|
|
85
96
|
}
|
|
86
97
|
|
|
87
98
|
async upsert (query, changeset) {
|
|
88
|
-
const {
|
|
89
|
-
criteria,
|
|
90
|
-
options
|
|
91
|
-
} = translate(query)
|
|
99
|
+
const { criteria, options } = translate(query)
|
|
92
100
|
|
|
93
101
|
if (!('_deleted' in changeset) || changeset._deleted === null) {
|
|
94
102
|
delete criteria._deleted
|
|
@@ -100,13 +108,32 @@ class Storage extends Connector {
|
|
|
100
108
|
$inc: { _version: 1 }
|
|
101
109
|
}
|
|
102
110
|
|
|
103
|
-
options.returnDocument =
|
|
111
|
+
options.returnDocument = ReturnDocument.AFTER
|
|
104
112
|
|
|
105
|
-
const result = await this.#
|
|
113
|
+
const result = await this.#collection.findOneAndUpdate(criteria, update, options)
|
|
106
114
|
|
|
107
115
|
return from(result)
|
|
108
116
|
}
|
|
109
117
|
|
|
118
|
+
async ensure (query, properties, state) {
|
|
119
|
+
let { criteria, options } = translate(query)
|
|
120
|
+
|
|
121
|
+
if (query === undefined)
|
|
122
|
+
criteria = properties
|
|
123
|
+
|
|
124
|
+
const update = { $setOnInsert: to(state) }
|
|
125
|
+
|
|
126
|
+
options.upsert = true
|
|
127
|
+
options.returnDocument = ReturnDocument.AFTER
|
|
128
|
+
|
|
129
|
+
const result = await this.#collection.findOneAndUpdate(criteria, update, options)
|
|
130
|
+
|
|
131
|
+
if (result._deleted !== undefined && result._deleted !== null)
|
|
132
|
+
return null
|
|
133
|
+
else
|
|
134
|
+
return from(result)
|
|
135
|
+
}
|
|
136
|
+
|
|
110
137
|
async index () {
|
|
111
138
|
const indexes = []
|
|
112
139
|
|
|
@@ -127,16 +154,13 @@ class Storage extends Connector {
|
|
|
127
154
|
|
|
128
155
|
const sparse = this.checkFields(Object.keys(fields))
|
|
129
156
|
|
|
130
|
-
await this.#
|
|
131
|
-
name,
|
|
132
|
-
sparse
|
|
133
|
-
})
|
|
157
|
+
await this.#collection.createIndex(fields, { name, sparse })
|
|
134
158
|
|
|
135
159
|
indexes.push(name)
|
|
136
160
|
}
|
|
137
161
|
}
|
|
138
162
|
|
|
139
|
-
await this.
|
|
163
|
+
await this.removeObsoleteIndexes(indexes)
|
|
140
164
|
}
|
|
141
165
|
|
|
142
166
|
async uniqueIndex (name, properties, sparse = false) {
|
|
@@ -147,23 +171,29 @@ class Storage extends Connector {
|
|
|
147
171
|
|
|
148
172
|
name = 'unique_' + name
|
|
149
173
|
|
|
150
|
-
await this.#
|
|
151
|
-
name,
|
|
152
|
-
unique: true,
|
|
153
|
-
sparse
|
|
154
|
-
})
|
|
174
|
+
await this.#collection.createIndex(fields, { name, unique: true, sparse })
|
|
155
175
|
|
|
156
176
|
return name
|
|
157
177
|
}
|
|
158
178
|
|
|
159
|
-
async
|
|
160
|
-
const current = await this
|
|
179
|
+
async removeObsoleteIndexes (desired) {
|
|
180
|
+
const current = await this.getCurrentIndexes()
|
|
161
181
|
const obsolete = current.filter((name) => !desired.includes(name))
|
|
162
182
|
|
|
163
183
|
if (obsolete.length > 0) {
|
|
164
184
|
console.info(`Remove obsolete indexes: [${obsolete.join(', ')}]`)
|
|
165
185
|
|
|
166
|
-
await this.#
|
|
186
|
+
await Promise.all(obsolete.map((name) => this.#collection.dropIndex(name)))
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async getCurrentIndexes () {
|
|
191
|
+
try {
|
|
192
|
+
const array = await this.#collection.listIndexes().toArray()
|
|
193
|
+
|
|
194
|
+
return array.map(({ name }) => name).filter((name) => name !== '_id_')
|
|
195
|
+
} catch {
|
|
196
|
+
return []
|
|
167
197
|
}
|
|
168
198
|
}
|
|
169
199
|
|
|
@@ -171,22 +201,19 @@ class Storage extends Connector {
|
|
|
171
201
|
const optional = []
|
|
172
202
|
|
|
173
203
|
for (const field of fields) {
|
|
174
|
-
if (!(field in this.#entity.schema.properties))
|
|
204
|
+
if (!(field in this.#entity.schema.properties))
|
|
175
205
|
throw new Error(`Index field '${field}' is not defined.`)
|
|
176
|
-
}
|
|
177
206
|
|
|
178
|
-
if (!this.#entity.schema.required?.includes(field))
|
|
207
|
+
if (!this.#entity.schema.required?.includes(field))
|
|
179
208
|
optional.push(field)
|
|
180
|
-
}
|
|
181
209
|
}
|
|
182
210
|
|
|
183
211
|
if (optional.length > 0) {
|
|
184
212
|
console.info(`Index fields [${optional.join(', ')}] are optional, creating sparse index.`)
|
|
185
213
|
|
|
186
214
|
return true
|
|
187
|
-
} else
|
|
215
|
+
} else
|
|
188
216
|
return false
|
|
189
|
-
}
|
|
190
217
|
}
|
|
191
218
|
|
|
192
219
|
}
|
package/src/collection.js
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const { Connector } = require('@toa.io/core')
|
|
4
|
-
|
|
5
|
-
class Collection extends Connector {
|
|
6
|
-
#client
|
|
7
|
-
#collection
|
|
8
|
-
|
|
9
|
-
constructor (client) {
|
|
10
|
-
super()
|
|
11
|
-
|
|
12
|
-
this.#client = client
|
|
13
|
-
|
|
14
|
-
this.depends(client)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async open () {
|
|
18
|
-
this.#collection = this.#client.collection
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** @hot */
|
|
22
|
-
async get (query, options) {
|
|
23
|
-
return /** @type {toa.mongodb.Record} */ this.#collection.findOne(query, options)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** @hot */
|
|
27
|
-
async find (query, options) {
|
|
28
|
-
return this.#collection.find(query, options).toArray()
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async stream (query, options, transform) {
|
|
32
|
-
return this.#collection.find(query, options).stream({ transform })
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/** @hot */
|
|
36
|
-
async add (record) {
|
|
37
|
-
return await this.#collection.insertOne(record)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/** @hot */
|
|
41
|
-
async replace (query, record, options) {
|
|
42
|
-
return await this.#collection.findOneAndReplace(query, record, options)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** @hot */
|
|
46
|
-
async update (query, update, options) {
|
|
47
|
-
return this.#collection.findOneAndUpdate(query, update, options)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async index (keys, options) {
|
|
51
|
-
return this.#collection.createIndex(keys, options)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async indexes () {
|
|
55
|
-
try {
|
|
56
|
-
const array = await this.#collection.listIndexes().toArray()
|
|
57
|
-
|
|
58
|
-
return array.map(({ name }) => name).filter((name) => name !== '_id_')
|
|
59
|
-
} catch {
|
|
60
|
-
return []
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async dropIndexes (names) {
|
|
65
|
-
const all = names.map((name) => this.#collection.dropIndex(name))
|
|
66
|
-
|
|
67
|
-
return Promise.all(all)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
exports.Collection = Collection
|