@toa.io/storages.mongodb 1.0.0-alpha.15 → 1.0.0-alpha.156
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 +8 -8
- package/src/client.js +26 -9
- package/src/factory.js +1 -3
- package/src/record.js +6 -19
- package/src/storage.js +151 -64
- package/src/translate.js +6 -6
- package/test/record.test.js +0 -15
- package/src/collection.js +0 -69
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.156",
|
|
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/
|
|
23
|
-
"@toa.io/
|
|
24
|
-
"@toa.io/
|
|
25
|
-
"@toa.io/
|
|
26
|
-
"
|
|
27
|
-
"
|
|
22
|
+
"@toa.io/conveyor": "1.0.0-alpha.93",
|
|
23
|
+
"@toa.io/core": "1.0.0-alpha.156",
|
|
24
|
+
"@toa.io/generic": "1.0.0-alpha.93",
|
|
25
|
+
"@toa.io/pointer": "1.0.0-alpha.143",
|
|
26
|
+
"mongodb": "6.7.0",
|
|
27
|
+
"openspan": "1.0.0-alpha.93",
|
|
28
28
|
"saslprep": "1.0.3"
|
|
29
29
|
},
|
|
30
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "687ebdf64b8fbc177e3a802440ecd6c96d6b068f"
|
|
31
31
|
}
|
package/src/client.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* @typedef {import('@toa.io/core').Locator} Locator
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
const { console } = require('openspan')
|
|
9
10
|
const { Connector } = require('@toa.io/core')
|
|
10
11
|
const { resolve } = require('@toa.io/pointer')
|
|
11
12
|
const { ID } = require('./deployment')
|
|
@@ -17,6 +18,8 @@ const { MongoClient } = require('mongodb')
|
|
|
17
18
|
const INSTANCES = {}
|
|
18
19
|
|
|
19
20
|
class Client extends Connector {
|
|
21
|
+
name
|
|
22
|
+
|
|
20
23
|
/**
|
|
21
24
|
* @public
|
|
22
25
|
* @type {import('mongodb').Collection}
|
|
@@ -48,6 +51,7 @@ class Client extends Connector {
|
|
|
48
51
|
super()
|
|
49
52
|
|
|
50
53
|
this.locator = locator
|
|
54
|
+
this.name = locator.lowercase
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
/**
|
|
@@ -57,17 +61,30 @@ class Client extends Connector {
|
|
|
57
61
|
*/
|
|
58
62
|
async open () {
|
|
59
63
|
const urls = await this.resolveURLs()
|
|
60
|
-
const
|
|
61
|
-
const collection = this.locator.lowercase
|
|
64
|
+
const dbname = this.resolveDB()
|
|
62
65
|
|
|
63
|
-
this.key = getKey(
|
|
66
|
+
this.key = getKey(dbname, urls)
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
try {
|
|
69
|
+
INSTANCES[this.key] ??= this.createInstance(urls)
|
|
70
|
+
} catch (error) {
|
|
71
|
+
console.error('Failed to connect to MongoDB', { urls, error })
|
|
72
|
+
}
|
|
66
73
|
|
|
67
74
|
this.instance = await INSTANCES[this.key]
|
|
68
75
|
this.instance.count++
|
|
69
76
|
|
|
70
|
-
|
|
77
|
+
const db = this.instance.client.db(dbname)
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
this.collection = await db.createCollection(this.name)
|
|
81
|
+
} catch (e) {
|
|
82
|
+
if (e.code !== ALREADY_EXISTS) {
|
|
83
|
+
throw e
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.collection = db.collection(this.name)
|
|
87
|
+
}
|
|
71
88
|
}
|
|
72
89
|
|
|
73
90
|
/**
|
|
@@ -95,7 +112,7 @@ class Client extends Connector {
|
|
|
95
112
|
const client = new MongoClient(urls.join(','), OPTIONS)
|
|
96
113
|
const hosts = urls.map((str) => new URL(str).host)
|
|
97
114
|
|
|
98
|
-
console.info('Connecting to MongoDB
|
|
115
|
+
console.info('Connecting to MongoDB', { address: hosts.join(', ') })
|
|
99
116
|
|
|
100
117
|
await client.connect()
|
|
101
118
|
|
|
@@ -139,9 +156,9 @@ function getKey (db, urls) {
|
|
|
139
156
|
}
|
|
140
157
|
|
|
141
158
|
const OPTIONS = {
|
|
142
|
-
ignoreUndefined: true
|
|
143
|
-
connectTimeoutMS: 0,
|
|
144
|
-
serverSelectionTimeoutMS: 0
|
|
159
|
+
ignoreUndefined: true
|
|
145
160
|
}
|
|
146
161
|
|
|
162
|
+
const ALREADY_EXISTS = 48
|
|
163
|
+
|
|
147
164
|
exports.Client = Client
|
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
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* @returns {toa.mongodb.Record}
|
|
6
|
-
*/
|
|
7
|
-
const to = (entity) => {
|
|
8
|
-
const {
|
|
9
|
-
id,
|
|
10
|
-
...rest
|
|
11
|
-
} = entity
|
|
3
|
+
function to (entity) {
|
|
4
|
+
const { id, ...rest } = entity
|
|
12
5
|
|
|
13
6
|
return /** @type {toa.mongodb.Record} */ { _id: id, ...rest }
|
|
14
7
|
}
|
|
15
8
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
*/
|
|
20
|
-
const from = (record) => {
|
|
21
|
-
if (record === undefined || record === null) return null
|
|
9
|
+
function from (record) {
|
|
10
|
+
if (record === undefined || record === null)
|
|
11
|
+
return null
|
|
22
12
|
|
|
23
|
-
const {
|
|
24
|
-
_id,
|
|
25
|
-
...rest
|
|
26
|
-
} = record
|
|
13
|
+
const { _id, ...rest } = record
|
|
27
14
|
|
|
28
15
|
return { id: _id, ...rest }
|
|
29
16
|
}
|
package/src/storage.js
CHANGED
|
@@ -1,58 +1,90 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
|
|
5
|
-
exceptions
|
|
6
|
-
} = require('@toa.io/core')
|
|
7
|
-
|
|
3
|
+
const { Connector, exceptions } = require('@toa.io/core')
|
|
4
|
+
const { console } = require('openspan')
|
|
8
5
|
const { translate } = require('./translate')
|
|
9
|
-
const {
|
|
10
|
-
|
|
11
|
-
from
|
|
12
|
-
} = require('./record')
|
|
6
|
+
const { to, from } = require('./record')
|
|
7
|
+
const { ReturnDocument } = require('mongodb')
|
|
13
8
|
|
|
14
9
|
class Storage extends Connector {
|
|
15
|
-
#
|
|
10
|
+
#client
|
|
11
|
+
|
|
12
|
+
/** @type {import('mongodb').Collection} */
|
|
13
|
+
#collection
|
|
16
14
|
#entity
|
|
17
15
|
|
|
18
|
-
|
|
16
|
+
#logs
|
|
17
|
+
|
|
18
|
+
constructor (client, entity) {
|
|
19
19
|
super()
|
|
20
20
|
|
|
21
|
-
this.#
|
|
21
|
+
this.#client = client
|
|
22
22
|
this.#entity = entity
|
|
23
23
|
|
|
24
|
-
this.depends(
|
|
24
|
+
this.depends(client)
|
|
25
|
+
|
|
26
|
+
this.#logs = console.fork({ collection: client.name })
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
get raw () {
|
|
30
|
+
return this.#collection
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
async open () {
|
|
34
|
+
this.#collection = this.#client.collection
|
|
35
|
+
|
|
28
36
|
await this.index()
|
|
29
37
|
}
|
|
30
38
|
|
|
31
39
|
async get (query) {
|
|
32
|
-
const {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
} = translate(query)
|
|
40
|
+
const { criteria, options } = translate(query)
|
|
41
|
+
|
|
42
|
+
this.debug('findOne', { criteria, options })
|
|
36
43
|
|
|
37
|
-
const record = await this.#
|
|
44
|
+
const record = await this.#collection.findOne(criteria, options)
|
|
38
45
|
|
|
39
46
|
return from(record)
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
async find (query) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
debugger
|
|
51
|
+
const { criteria, options, sample } = translate(query)
|
|
52
|
+
|
|
53
|
+
criteria._deleted = null
|
|
54
|
+
|
|
55
|
+
let cursor
|
|
56
|
+
|
|
57
|
+
if (sample === undefined) {
|
|
58
|
+
this.debug('find', { criteria, options })
|
|
59
|
+
|
|
60
|
+
cursor = this.#collection.find(criteria, options)
|
|
61
|
+
} else {
|
|
62
|
+
const pipeline = toPipeline(criteria, options, sample)
|
|
63
|
+
|
|
64
|
+
this.debug('aggregate', { pipeline })
|
|
65
|
+
|
|
66
|
+
cursor = this.#collection.aggregate(pipeline)
|
|
67
|
+
}
|
|
47
68
|
|
|
48
|
-
const recordset = await
|
|
69
|
+
const recordset = await cursor.toArray()
|
|
49
70
|
|
|
50
71
|
return recordset.map((item) => from(item))
|
|
51
72
|
}
|
|
52
73
|
|
|
74
|
+
async stream (query = undefined) {
|
|
75
|
+
const { criteria, options } = translate(query)
|
|
76
|
+
|
|
77
|
+
this.debug('find (stream)', { criteria, options })
|
|
78
|
+
|
|
79
|
+
return this.#collection.find(criteria, options).stream({ transform: from })
|
|
80
|
+
}
|
|
81
|
+
|
|
53
82
|
async add (entity) {
|
|
54
83
|
const record = to(entity)
|
|
55
|
-
|
|
84
|
+
|
|
85
|
+
this.debug('insertOne', { record })
|
|
86
|
+
|
|
87
|
+
const result = await this.#collection.insertOne(record)
|
|
56
88
|
|
|
57
89
|
return result.acknowledged
|
|
58
90
|
}
|
|
@@ -62,41 +94,47 @@ class Storage extends Connector {
|
|
|
62
94
|
_id: entity.id,
|
|
63
95
|
_version: entity._version - 1
|
|
64
96
|
}
|
|
65
|
-
|
|
97
|
+
|
|
98
|
+
const record = to(entity)
|
|
99
|
+
|
|
100
|
+
this.debug('findOneAndReplace', { criteria, record })
|
|
101
|
+
|
|
102
|
+
const result = await this.#collection.findOneAndReplace(criteria, record)
|
|
66
103
|
|
|
67
104
|
return result !== null
|
|
68
105
|
}
|
|
69
106
|
|
|
70
|
-
async store (entity) {
|
|
107
|
+
async store (entity, attempt = 0) {
|
|
71
108
|
try {
|
|
72
|
-
if (entity._version === 1)
|
|
109
|
+
if (entity._version === 1)
|
|
73
110
|
return await this.add(entity)
|
|
74
|
-
|
|
111
|
+
else
|
|
75
112
|
return await this.set(entity)
|
|
76
|
-
}
|
|
77
113
|
} catch (error) {
|
|
78
114
|
if (error.code === ERR_DUPLICATE_KEY) {
|
|
79
|
-
|
|
80
115
|
const id = error.keyPattern === undefined
|
|
81
116
|
? error.message.includes(' index: _id_ ') // AWS DocumentDB
|
|
82
117
|
: error.keyPattern._id === 1
|
|
83
118
|
|
|
84
|
-
if (id)
|
|
119
|
+
if (id)
|
|
85
120
|
return false
|
|
86
|
-
|
|
87
|
-
throw new exceptions.DuplicateException()
|
|
88
|
-
|
|
89
|
-
|
|
121
|
+
else
|
|
122
|
+
throw new exceptions.DuplicateException(this.#client.name, entity)
|
|
123
|
+
} else if (error.cause?.code === 'ECONNREFUSED') {
|
|
124
|
+
// This is temporary and should be replaced with a class decorator.
|
|
125
|
+
if (attempt > 10)
|
|
126
|
+
throw error
|
|
127
|
+
|
|
128
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
129
|
+
|
|
130
|
+
return this.store(entity)
|
|
131
|
+
} else
|
|
90
132
|
throw error
|
|
91
|
-
}
|
|
92
133
|
}
|
|
93
134
|
}
|
|
94
135
|
|
|
95
136
|
async upsert (query, changeset) {
|
|
96
|
-
const {
|
|
97
|
-
criteria,
|
|
98
|
-
options
|
|
99
|
-
} = translate(query)
|
|
137
|
+
const { criteria, options } = translate(query)
|
|
100
138
|
|
|
101
139
|
if (!('_deleted' in changeset) || changeset._deleted === null) {
|
|
102
140
|
delete criteria._deleted
|
|
@@ -108,13 +146,36 @@ class Storage extends Connector {
|
|
|
108
146
|
$inc: { _version: 1 }
|
|
109
147
|
}
|
|
110
148
|
|
|
111
|
-
options.returnDocument =
|
|
149
|
+
options.returnDocument = ReturnDocument.AFTER
|
|
150
|
+
|
|
151
|
+
this.debug('findOneAndUpdate', { criteria, update, options })
|
|
112
152
|
|
|
113
|
-
const result = await this.#
|
|
153
|
+
const result = await this.#collection.findOneAndUpdate(criteria, update, options)
|
|
114
154
|
|
|
115
155
|
return from(result)
|
|
116
156
|
}
|
|
117
157
|
|
|
158
|
+
async ensure (query, properties, state) {
|
|
159
|
+
let { criteria, options } = translate(query)
|
|
160
|
+
|
|
161
|
+
if (query === undefined)
|
|
162
|
+
criteria = properties
|
|
163
|
+
|
|
164
|
+
const update = { $setOnInsert: to(state) }
|
|
165
|
+
|
|
166
|
+
options.upsert = true
|
|
167
|
+
options.returnDocument = ReturnDocument.AFTER
|
|
168
|
+
|
|
169
|
+
this.#logs.debug('Database query', { method: 'findOneAndUpdate', criteria, update, options })
|
|
170
|
+
|
|
171
|
+
const result = await this.#collection.findOneAndUpdate(criteria, update, options)
|
|
172
|
+
|
|
173
|
+
if (result._deleted !== undefined && result._deleted !== null)
|
|
174
|
+
return null
|
|
175
|
+
else
|
|
176
|
+
return from(result)
|
|
177
|
+
}
|
|
178
|
+
|
|
118
179
|
async index () {
|
|
119
180
|
const indexes = []
|
|
120
181
|
|
|
@@ -131,20 +192,18 @@ class Storage extends Connector {
|
|
|
131
192
|
for (const [suffix, declaration] of Object.entries(this.#entity.index)) {
|
|
132
193
|
const name = 'index_' + suffix
|
|
133
194
|
const fields = Object.fromEntries(Object.entries(declaration)
|
|
134
|
-
.map(([name, type]) => [name, INDEX_TYPES[type]]))
|
|
195
|
+
.map(([name, type]) => [name, INDEX_TYPES[type] ?? type]))
|
|
135
196
|
|
|
136
197
|
const sparse = this.checkFields(Object.keys(fields))
|
|
137
198
|
|
|
138
|
-
await this.#
|
|
139
|
-
name,
|
|
140
|
-
sparse
|
|
141
|
-
})
|
|
199
|
+
await this.#collection.createIndex(fields, { name, sparse })
|
|
200
|
+
.catch((e) => this.#logs.warn('Index creation failed', { name, fields, error: e }))
|
|
142
201
|
|
|
143
202
|
indexes.push(name)
|
|
144
203
|
}
|
|
145
204
|
}
|
|
146
205
|
|
|
147
|
-
await this.
|
|
206
|
+
await this.removeObsoleteIndexes(indexes)
|
|
148
207
|
}
|
|
149
208
|
|
|
150
209
|
async uniqueIndex (name, properties, sparse = false) {
|
|
@@ -155,23 +214,30 @@ class Storage extends Connector {
|
|
|
155
214
|
|
|
156
215
|
name = 'unique_' + name
|
|
157
216
|
|
|
158
|
-
await this.#
|
|
159
|
-
name,
|
|
160
|
-
unique: true,
|
|
161
|
-
sparse
|
|
162
|
-
})
|
|
217
|
+
await this.#collection.createIndex(fields, { name, unique: true, sparse })
|
|
218
|
+
.catch((e) => this.#logs.warn('Unique index creation failed', { name, fields, error: e }))
|
|
163
219
|
|
|
164
220
|
return name
|
|
165
221
|
}
|
|
166
222
|
|
|
167
|
-
async
|
|
168
|
-
const current = await this
|
|
223
|
+
async removeObsoleteIndexes (desired) {
|
|
224
|
+
const current = await this.getCurrentIndexes()
|
|
169
225
|
const obsolete = current.filter((name) => !desired.includes(name))
|
|
170
226
|
|
|
171
227
|
if (obsolete.length > 0) {
|
|
172
|
-
|
|
228
|
+
this.#logs.info('Removing obsolete indexes', { indexes: obsolete.join(', ') })
|
|
173
229
|
|
|
174
|
-
await this.#
|
|
230
|
+
await Promise.all(obsolete.map((name) => this.#collection.dropIndex(name)))
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
async getCurrentIndexes () {
|
|
235
|
+
try {
|
|
236
|
+
const array = await this.#collection.listIndexes().toArray()
|
|
237
|
+
|
|
238
|
+
return array.map(({ name }) => name).filter((name) => name !== '_id_')
|
|
239
|
+
} catch {
|
|
240
|
+
return []
|
|
175
241
|
}
|
|
176
242
|
}
|
|
177
243
|
|
|
@@ -179,24 +245,45 @@ class Storage extends Connector {
|
|
|
179
245
|
const optional = []
|
|
180
246
|
|
|
181
247
|
for (const field of fields) {
|
|
182
|
-
if (!(field in this.#entity.schema.properties))
|
|
248
|
+
if (!(field in this.#entity.schema.properties))
|
|
183
249
|
throw new Error(`Index field '${field}' is not defined.`)
|
|
184
|
-
}
|
|
185
250
|
|
|
186
|
-
if (!this.#entity.schema.required?.includes(field))
|
|
251
|
+
if (!this.#entity.schema.required?.includes(field))
|
|
187
252
|
optional.push(field)
|
|
188
|
-
}
|
|
189
253
|
}
|
|
190
254
|
|
|
191
255
|
if (optional.length > 0) {
|
|
192
|
-
|
|
256
|
+
this.#logs.info('Index fields are optional, creating sparse index', { fields: optional })
|
|
193
257
|
|
|
194
258
|
return true
|
|
195
|
-
} else
|
|
259
|
+
} else
|
|
196
260
|
return false
|
|
197
|
-
}
|
|
198
261
|
}
|
|
199
262
|
|
|
263
|
+
debug (method, attributes) {
|
|
264
|
+
this.#logs.debug('Database query', {
|
|
265
|
+
method,
|
|
266
|
+
...attributes
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function toPipeline (criteria, options, sample) {
|
|
272
|
+
const pipeline = []
|
|
273
|
+
|
|
274
|
+
if (criteria !== undefined)
|
|
275
|
+
pipeline.push({ $match: criteria })
|
|
276
|
+
|
|
277
|
+
if (sample !== undefined)
|
|
278
|
+
pipeline.push({ $sample: { size: sample } })
|
|
279
|
+
|
|
280
|
+
if (options?.sort !== undefined)
|
|
281
|
+
pipeline.push({ $sort: options.sort })
|
|
282
|
+
|
|
283
|
+
if (options?.projection !== undefined)
|
|
284
|
+
pipeline.push({ $project: options.projection })
|
|
285
|
+
|
|
286
|
+
return pipeline
|
|
200
287
|
}
|
|
201
288
|
|
|
202
289
|
const INDEX_TYPES = {
|
package/src/translate.js
CHANGED
|
@@ -9,18 +9,18 @@ const parse = { ...require('./translate/criteria'), ...require('./translate/opti
|
|
|
9
9
|
const translate = (query) => {
|
|
10
10
|
const result = {
|
|
11
11
|
criteria: query?.criteria === undefined ? {} : parse.criteria(query.criteria),
|
|
12
|
-
options: query?.options === undefined ? {} : parse.options(query.options)
|
|
12
|
+
options: query?.options === undefined ? {} : parse.options(query.options),
|
|
13
|
+
sample: query?.options?.sample
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
if (query?.id !== undefined)
|
|
16
|
+
if (query?.id !== undefined)
|
|
16
17
|
result.criteria._id = query.id
|
|
17
|
-
}
|
|
18
18
|
|
|
19
|
-
if (query?.version !== undefined)
|
|
19
|
+
if (query?.version !== undefined)
|
|
20
20
|
result.criteria._version = query.version
|
|
21
|
-
}
|
|
22
21
|
|
|
23
|
-
|
|
22
|
+
if (query?.search !== undefined)
|
|
23
|
+
result.criteria.$text = { $search: query.search }
|
|
24
24
|
|
|
25
25
|
return result
|
|
26
26
|
}
|
package/test/record.test.js
CHANGED
|
@@ -47,19 +47,4 @@ describe('from', () => {
|
|
|
47
47
|
_version: 0
|
|
48
48
|
})
|
|
49
49
|
})
|
|
50
|
-
|
|
51
|
-
it('should not modify argument', () => {
|
|
52
|
-
/** @type {toa.mongodb.Record} */
|
|
53
|
-
const record = {
|
|
54
|
-
_id: '1',
|
|
55
|
-
_version: 0
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
from(record)
|
|
59
|
-
|
|
60
|
-
expect(record).toStrictEqual({
|
|
61
|
-
_id: '1',
|
|
62
|
-
_version: 0
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
50
|
})
|
package/src/collection.js
DELETED
|
@@ -1,69 +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
|
-
const cursor = this.#collection.find(query, options)
|
|
29
|
-
|
|
30
|
-
return cursor.toArray()
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/** @hot */
|
|
34
|
-
async add (record) {
|
|
35
|
-
return await this.#collection.insertOne(record)
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** @hot */
|
|
39
|
-
async replace (query, record, options) {
|
|
40
|
-
return await this.#collection.findOneAndReplace(query, record, options)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/** @hot */
|
|
44
|
-
async update (query, update, options) {
|
|
45
|
-
return this.#collection.findOneAndUpdate(query, update, options)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async index (keys, options) {
|
|
49
|
-
return this.#collection.createIndex(keys, options)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
async indexes () {
|
|
53
|
-
try {
|
|
54
|
-
const array = await this.#collection.listIndexes().toArray()
|
|
55
|
-
|
|
56
|
-
return array.map(({ name }) => name).filter((name) => name !== '_id_')
|
|
57
|
-
} catch {
|
|
58
|
-
return []
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
async dropIndexes (names) {
|
|
63
|
-
const all = names.map((name) => this.#collection.dropIndex(name))
|
|
64
|
-
|
|
65
|
-
return Promise.all(all)
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
exports.Collection = Collection
|