@toa.io/storages.mongodb 1.0.0-alpha.13 → 1.0.0-alpha.136
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 +37 -8
- package/src/factory.js +1 -3
- package/src/record.js +6 -19
- package/src/storage.js +130 -65
- package/src/translate.js +9 -5
- 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.136",
|
|
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.136",
|
|
24
|
+
"@toa.io/generic": "1.0.0-alpha.93",
|
|
25
|
+
"@toa.io/pointer": "1.0.0-alpha.125",
|
|
26
|
+
"mongodb": "6.7.0",
|
|
27
|
+
"openspan": "1.0.0-alpha.93",
|
|
28
28
|
"saslprep": "1.0.3"
|
|
29
29
|
},
|
|
30
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "73722f06d137283915ca8710ae09e5e53307e1f9"
|
|
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,26 @@ 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
|
INSTANCES[this.key] ??= this.createInstance(urls)
|
|
66
69
|
|
|
67
70
|
this.instance = await INSTANCES[this.key]
|
|
68
71
|
this.instance.count++
|
|
69
72
|
|
|
70
|
-
|
|
73
|
+
const db = this.instance.client.db(dbname)
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
this.collection = await db.createCollection(this.name)
|
|
77
|
+
} catch (e) {
|
|
78
|
+
if (e.code !== ALREADY_EXISTS) {
|
|
79
|
+
throw e
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.collection = db.collection(this.name)
|
|
83
|
+
}
|
|
71
84
|
}
|
|
72
85
|
|
|
73
86
|
/**
|
|
@@ -95,7 +108,7 @@ class Client extends Connector {
|
|
|
95
108
|
const client = new MongoClient(urls.join(','), OPTIONS)
|
|
96
109
|
const hosts = urls.map((str) => new URL(str).host)
|
|
97
110
|
|
|
98
|
-
console.info('Connecting to MongoDB
|
|
111
|
+
console.info('Connecting to MongoDB', { address: hosts.join(', ') })
|
|
99
112
|
|
|
100
113
|
await client.connect()
|
|
101
114
|
|
|
@@ -116,6 +129,22 @@ class Client extends Connector {
|
|
|
116
129
|
return await resolve(ID, this.locator.id)
|
|
117
130
|
}
|
|
118
131
|
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @private
|
|
135
|
+
* @return {string}
|
|
136
|
+
*/
|
|
137
|
+
resolveDB () {
|
|
138
|
+
if (process.env.TOA_CONTEXT !== undefined) {
|
|
139
|
+
return process.env.TOA_CONTEXT
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (process.env.TOA_DEV === '1') {
|
|
143
|
+
return 'toa-dev'
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
throw new Error('Environment variable TOA_CONTEXT is not defined')
|
|
147
|
+
}
|
|
119
148
|
}
|
|
120
149
|
|
|
121
150
|
function getKey (db, urls) {
|
|
@@ -123,9 +152,9 @@ function getKey (db, urls) {
|
|
|
123
152
|
}
|
|
124
153
|
|
|
125
154
|
const OPTIONS = {
|
|
126
|
-
ignoreUndefined: true
|
|
127
|
-
connectTimeoutMS: 0,
|
|
128
|
-
serverSelectionTimeoutMS: 0
|
|
155
|
+
ignoreUndefined: true
|
|
129
156
|
}
|
|
130
157
|
|
|
158
|
+
const ALREADY_EXISTS = 48
|
|
159
|
+
|
|
131
160
|
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,57 +1,77 @@
|
|
|
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
|
-
const {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
50
|
+
const { criteria, options } = translate(query)
|
|
51
|
+
|
|
52
|
+
criteria._deleted = null
|
|
53
|
+
|
|
54
|
+
this.debug('find', { criteria, options })
|
|
55
|
+
|
|
56
|
+
const recordset = await this.#collection.find(criteria, options).toArray()
|
|
48
57
|
|
|
49
58
|
return recordset.map((item) => from(item))
|
|
50
59
|
}
|
|
51
60
|
|
|
61
|
+
async stream (query = undefined) {
|
|
62
|
+
const { criteria, options } = translate(query)
|
|
63
|
+
|
|
64
|
+
this.debug('find (stream)', { criteria, options })
|
|
65
|
+
|
|
66
|
+
return this.#collection.find(criteria, options).stream({ transform: from })
|
|
67
|
+
}
|
|
68
|
+
|
|
52
69
|
async add (entity) {
|
|
53
70
|
const record = to(entity)
|
|
54
|
-
|
|
71
|
+
|
|
72
|
+
this.debug('insertOne', { record })
|
|
73
|
+
|
|
74
|
+
const result = await this.#collection.insertOne(record)
|
|
55
75
|
|
|
56
76
|
return result.acknowledged
|
|
57
77
|
}
|
|
@@ -61,51 +81,88 @@ class Storage extends Connector {
|
|
|
61
81
|
_id: entity.id,
|
|
62
82
|
_version: entity._version - 1
|
|
63
83
|
}
|
|
64
|
-
|
|
84
|
+
|
|
85
|
+
const record = to(entity)
|
|
86
|
+
|
|
87
|
+
this.debug('findOneAndReplace', { criteria, record })
|
|
88
|
+
|
|
89
|
+
const result = await this.#collection.findOneAndReplace(criteria, record)
|
|
65
90
|
|
|
66
91
|
return result !== null
|
|
67
92
|
}
|
|
68
93
|
|
|
69
|
-
async store (entity) {
|
|
94
|
+
async store (entity, attempt = 0) {
|
|
70
95
|
try {
|
|
71
|
-
if (entity._version === 1)
|
|
96
|
+
if (entity._version === 1)
|
|
72
97
|
return await this.add(entity)
|
|
73
|
-
|
|
98
|
+
else
|
|
74
99
|
return await this.set(entity)
|
|
75
|
-
}
|
|
76
100
|
} catch (error) {
|
|
77
101
|
if (error.code === ERR_DUPLICATE_KEY) {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
102
|
+
const id = error.keyPattern === undefined
|
|
103
|
+
? error.message.includes(' index: _id_ ') // AWS DocumentDB
|
|
104
|
+
: error.keyPattern._id === 1
|
|
105
|
+
|
|
106
|
+
if (id)
|
|
107
|
+
return false
|
|
108
|
+
else
|
|
109
|
+
throw new exceptions.DuplicateException(this.#client.name, entity)
|
|
110
|
+
} else if (error.cause?.code === 'ECONNREFUSED') {
|
|
111
|
+
// This is temporary and should be replaced with a class decorator.
|
|
112
|
+
if (attempt > 10)
|
|
113
|
+
throw error
|
|
114
|
+
|
|
115
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
116
|
+
|
|
117
|
+
return this.store(entity)
|
|
118
|
+
} else
|
|
86
119
|
throw error
|
|
87
|
-
}
|
|
88
120
|
}
|
|
89
121
|
}
|
|
90
122
|
|
|
91
123
|
async upsert (query, changeset) {
|
|
92
|
-
const {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
124
|
+
const { criteria, options } = translate(query)
|
|
125
|
+
|
|
126
|
+
if (!('_deleted' in changeset) || changeset._deleted === null) {
|
|
127
|
+
delete criteria._deleted
|
|
128
|
+
changeset._deleted = null
|
|
129
|
+
}
|
|
96
130
|
|
|
97
131
|
const update = {
|
|
98
132
|
$set: { ...changeset },
|
|
99
133
|
$inc: { _version: 1 }
|
|
100
134
|
}
|
|
101
135
|
|
|
102
|
-
options.returnDocument =
|
|
136
|
+
options.returnDocument = ReturnDocument.AFTER
|
|
103
137
|
|
|
104
|
-
|
|
138
|
+
this.debug('findOneAndUpdate', { criteria, update, options })
|
|
139
|
+
|
|
140
|
+
const result = await this.#collection.findOneAndUpdate(criteria, update, options)
|
|
105
141
|
|
|
106
142
|
return from(result)
|
|
107
143
|
}
|
|
108
144
|
|
|
145
|
+
async ensure (query, properties, state) {
|
|
146
|
+
let { criteria, options } = translate(query)
|
|
147
|
+
|
|
148
|
+
if (query === undefined)
|
|
149
|
+
criteria = properties
|
|
150
|
+
|
|
151
|
+
const update = { $setOnInsert: to(state) }
|
|
152
|
+
|
|
153
|
+
options.upsert = true
|
|
154
|
+
options.returnDocument = ReturnDocument.AFTER
|
|
155
|
+
|
|
156
|
+
this.#logs.debug('Database query', { method: 'findOneAndUpdate', criteria, update, options })
|
|
157
|
+
|
|
158
|
+
const result = await this.#collection.findOneAndUpdate(criteria, update, options)
|
|
159
|
+
|
|
160
|
+
if (result._deleted !== undefined && result._deleted !== null)
|
|
161
|
+
return null
|
|
162
|
+
else
|
|
163
|
+
return from(result)
|
|
164
|
+
}
|
|
165
|
+
|
|
109
166
|
async index () {
|
|
110
167
|
const indexes = []
|
|
111
168
|
|
|
@@ -126,16 +183,14 @@ class Storage extends Connector {
|
|
|
126
183
|
|
|
127
184
|
const sparse = this.checkFields(Object.keys(fields))
|
|
128
185
|
|
|
129
|
-
await this.#
|
|
130
|
-
name,
|
|
131
|
-
sparse
|
|
132
|
-
})
|
|
186
|
+
await this.#collection.createIndex(fields, { name, sparse })
|
|
187
|
+
.catch((e) => this.#logs.warn('Index creation failed', { name, fields, error: e }))
|
|
133
188
|
|
|
134
189
|
indexes.push(name)
|
|
135
190
|
}
|
|
136
191
|
}
|
|
137
192
|
|
|
138
|
-
await this.
|
|
193
|
+
await this.removeObsoleteIndexes(indexes)
|
|
139
194
|
}
|
|
140
195
|
|
|
141
196
|
async uniqueIndex (name, properties, sparse = false) {
|
|
@@ -146,23 +201,30 @@ class Storage extends Connector {
|
|
|
146
201
|
|
|
147
202
|
name = 'unique_' + name
|
|
148
203
|
|
|
149
|
-
await this.#
|
|
150
|
-
name,
|
|
151
|
-
unique: true,
|
|
152
|
-
sparse
|
|
153
|
-
})
|
|
204
|
+
await this.#collection.createIndex(fields, { name, unique: true, sparse })
|
|
205
|
+
.catch((e) => this.#logs.warn('Unique index creation failed', { name, fields, error: e }))
|
|
154
206
|
|
|
155
207
|
return name
|
|
156
208
|
}
|
|
157
209
|
|
|
158
|
-
async
|
|
159
|
-
const current = await this
|
|
210
|
+
async removeObsoleteIndexes (desired) {
|
|
211
|
+
const current = await this.getCurrentIndexes()
|
|
160
212
|
const obsolete = current.filter((name) => !desired.includes(name))
|
|
161
213
|
|
|
162
214
|
if (obsolete.length > 0) {
|
|
163
|
-
|
|
215
|
+
this.#logs.info('Removing obsolete indexes', { indexes: obsolete.join(', ') })
|
|
216
|
+
|
|
217
|
+
await Promise.all(obsolete.map((name) => this.#collection.dropIndex(name)))
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async getCurrentIndexes () {
|
|
222
|
+
try {
|
|
223
|
+
const array = await this.#collection.listIndexes().toArray()
|
|
164
224
|
|
|
165
|
-
|
|
225
|
+
return array.map(({ name }) => name).filter((name) => name !== '_id_')
|
|
226
|
+
} catch {
|
|
227
|
+
return []
|
|
166
228
|
}
|
|
167
229
|
}
|
|
168
230
|
|
|
@@ -170,24 +232,27 @@ class Storage extends Connector {
|
|
|
170
232
|
const optional = []
|
|
171
233
|
|
|
172
234
|
for (const field of fields) {
|
|
173
|
-
if (!(field in this.#entity.schema.properties))
|
|
235
|
+
if (!(field in this.#entity.schema.properties))
|
|
174
236
|
throw new Error(`Index field '${field}' is not defined.`)
|
|
175
|
-
}
|
|
176
237
|
|
|
177
|
-
if (!this.#entity.schema.required?.includes(field))
|
|
238
|
+
if (!this.#entity.schema.required?.includes(field))
|
|
178
239
|
optional.push(field)
|
|
179
|
-
}
|
|
180
240
|
}
|
|
181
241
|
|
|
182
242
|
if (optional.length > 0) {
|
|
183
|
-
|
|
243
|
+
this.#logs.info('Index fields are optional, creating sparse index', { fields: optional })
|
|
184
244
|
|
|
185
245
|
return true
|
|
186
|
-
} else
|
|
246
|
+
} else
|
|
187
247
|
return false
|
|
188
|
-
}
|
|
189
248
|
}
|
|
190
249
|
|
|
250
|
+
debug (method, attributes) {
|
|
251
|
+
this.#logs.debug('Database query', {
|
|
252
|
+
method,
|
|
253
|
+
...attributes
|
|
254
|
+
})
|
|
255
|
+
}
|
|
191
256
|
}
|
|
192
257
|
|
|
193
258
|
const INDEX_TYPES = {
|
package/src/translate.js
CHANGED
|
@@ -7,12 +7,16 @@ const parse = { ...require('./translate/criteria'), ...require('./translate/opti
|
|
|
7
7
|
* @returns {{criteria: Object, options: Object}}
|
|
8
8
|
*/
|
|
9
9
|
const translate = (query) => {
|
|
10
|
-
const result = {
|
|
10
|
+
const result = {
|
|
11
|
+
criteria: query?.criteria === undefined ? {} : parse.criteria(query.criteria),
|
|
12
|
+
options: query?.options === undefined ? {} : parse.options(query.options)
|
|
13
|
+
}
|
|
11
14
|
|
|
12
|
-
if (query
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (query
|
|
15
|
+
if (query?.id !== undefined)
|
|
16
|
+
result.criteria._id = query.id
|
|
17
|
+
|
|
18
|
+
if (query?.version !== undefined)
|
|
19
|
+
result.criteria._version = query.version
|
|
16
20
|
|
|
17
21
|
return result
|
|
18
22
|
}
|
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
|