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