@toa.io/storages.mongodb 1.0.0-alpha.18 → 1.0.0-alpha.182
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 +164 -73
- 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.182",
|
|
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.173",
|
|
23
|
+
"@toa.io/core": "1.0.0-alpha.182",
|
|
24
|
+
"@toa.io/generic": "1.0.0-alpha.173",
|
|
25
|
+
"@toa.io/pointer": "1.0.0-alpha.182",
|
|
26
|
+
"mongodb": "6.7.0",
|
|
27
|
+
"openspan": "1.0.0-alpha.173",
|
|
28
28
|
"saslprep": "1.0.3"
|
|
29
29
|
},
|
|
30
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "99d0b79680455e8746274bcfa07a5674e8514bff"
|
|
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,89 @@
|
|
|
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
|
-
|
|
50
|
+
const { criteria, options, sample } = translate(query)
|
|
51
|
+
|
|
52
|
+
criteria._deleted = null
|
|
53
|
+
|
|
54
|
+
let cursor
|
|
55
|
+
|
|
56
|
+
if (sample === undefined) {
|
|
57
|
+
this.debug('find', { criteria, options })
|
|
58
|
+
|
|
59
|
+
cursor = this.#collection.find(criteria, options)
|
|
60
|
+
} else {
|
|
61
|
+
const pipeline = toPipeline(criteria, options, sample)
|
|
62
|
+
|
|
63
|
+
this.debug('aggregate', { pipeline })
|
|
64
|
+
|
|
65
|
+
cursor = this.#collection.aggregate(pipeline)
|
|
66
|
+
}
|
|
47
67
|
|
|
48
|
-
const recordset = await
|
|
68
|
+
const recordset = await cursor.toArray()
|
|
49
69
|
|
|
50
70
|
return recordset.map((item) => from(item))
|
|
51
71
|
}
|
|
52
72
|
|
|
73
|
+
async stream (query = undefined) {
|
|
74
|
+
const { criteria, options } = translate(query)
|
|
75
|
+
|
|
76
|
+
this.debug('find (stream)', { criteria, options })
|
|
77
|
+
|
|
78
|
+
return this.#collection.find(criteria, options).stream({ transform: from })
|
|
79
|
+
}
|
|
80
|
+
|
|
53
81
|
async add (entity) {
|
|
54
82
|
const record = to(entity)
|
|
55
|
-
|
|
83
|
+
|
|
84
|
+
this.debug('insertOne', { record })
|
|
85
|
+
|
|
86
|
+
const result = await this.#collection.insertOne(record)
|
|
56
87
|
|
|
57
88
|
return result.acknowledged
|
|
58
89
|
}
|
|
@@ -62,41 +93,47 @@ class Storage extends Connector {
|
|
|
62
93
|
_id: entity.id,
|
|
63
94
|
_version: entity._version - 1
|
|
64
95
|
}
|
|
65
|
-
|
|
96
|
+
|
|
97
|
+
const record = to(entity)
|
|
98
|
+
|
|
99
|
+
this.debug('findOneAndReplace', { criteria, record })
|
|
100
|
+
|
|
101
|
+
const result = await this.#collection.findOneAndReplace(criteria, record)
|
|
66
102
|
|
|
67
103
|
return result !== null
|
|
68
104
|
}
|
|
69
105
|
|
|
70
|
-
async store (entity) {
|
|
106
|
+
async store (entity, attempt = 0) {
|
|
71
107
|
try {
|
|
72
|
-
if (entity._version === 1)
|
|
108
|
+
if (entity._version === 1)
|
|
73
109
|
return await this.add(entity)
|
|
74
|
-
|
|
110
|
+
else
|
|
75
111
|
return await this.set(entity)
|
|
76
|
-
}
|
|
77
112
|
} catch (error) {
|
|
78
113
|
if (error.code === ERR_DUPLICATE_KEY) {
|
|
79
|
-
|
|
80
114
|
const id = error.keyPattern === undefined
|
|
81
115
|
? error.message.includes(' index: _id_ ') // AWS DocumentDB
|
|
82
116
|
: error.keyPattern._id === 1
|
|
83
117
|
|
|
84
|
-
if (id)
|
|
118
|
+
if (id)
|
|
85
119
|
return false
|
|
86
|
-
|
|
87
|
-
throw new exceptions.DuplicateException()
|
|
88
|
-
|
|
89
|
-
|
|
120
|
+
else
|
|
121
|
+
throw new exceptions.DuplicateException(this.#client.name, entity)
|
|
122
|
+
} else if (error.cause?.code === 'ECONNREFUSED') {
|
|
123
|
+
// This is temporary and should be replaced with a class decorator.
|
|
124
|
+
if (attempt > 10)
|
|
125
|
+
throw error
|
|
126
|
+
|
|
127
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
128
|
+
|
|
129
|
+
return this.store(entity)
|
|
130
|
+
} else
|
|
90
131
|
throw error
|
|
91
|
-
}
|
|
92
132
|
}
|
|
93
133
|
}
|
|
94
134
|
|
|
95
135
|
async upsert (query, changeset) {
|
|
96
|
-
const {
|
|
97
|
-
criteria,
|
|
98
|
-
options
|
|
99
|
-
} = translate(query)
|
|
136
|
+
const { criteria, options } = translate(query)
|
|
100
137
|
|
|
101
138
|
if (!('_deleted' in changeset) || changeset._deleted === null) {
|
|
102
139
|
delete criteria._deleted
|
|
@@ -108,20 +145,43 @@ class Storage extends Connector {
|
|
|
108
145
|
$inc: { _version: 1 }
|
|
109
146
|
}
|
|
110
147
|
|
|
111
|
-
options.returnDocument =
|
|
148
|
+
options.returnDocument = ReturnDocument.AFTER
|
|
112
149
|
|
|
113
|
-
|
|
150
|
+
this.debug('findOneAndUpdate', { criteria, update, options })
|
|
151
|
+
|
|
152
|
+
const result = await this.#collection.findOneAndUpdate(criteria, update, options)
|
|
114
153
|
|
|
115
154
|
return from(result)
|
|
116
155
|
}
|
|
117
156
|
|
|
157
|
+
async ensure (query, properties, state) {
|
|
158
|
+
let { criteria, options } = translate(query)
|
|
159
|
+
|
|
160
|
+
if (query === undefined)
|
|
161
|
+
criteria = properties
|
|
162
|
+
|
|
163
|
+
const update = { $setOnInsert: to(state) }
|
|
164
|
+
|
|
165
|
+
options.upsert = true
|
|
166
|
+
options.returnDocument = ReturnDocument.AFTER
|
|
167
|
+
|
|
168
|
+
this.#logs.debug('Database query', { method: 'findOneAndUpdate', criteria, update, options })
|
|
169
|
+
|
|
170
|
+
const result = await this.#collection.findOneAndUpdate(criteria, update, options)
|
|
171
|
+
|
|
172
|
+
if (result._deleted !== undefined && result._deleted !== null)
|
|
173
|
+
return null
|
|
174
|
+
else
|
|
175
|
+
return from(result)
|
|
176
|
+
}
|
|
177
|
+
|
|
118
178
|
async index () {
|
|
119
179
|
const indexes = []
|
|
120
180
|
|
|
121
181
|
if (this.#entity.unique !== undefined) {
|
|
122
182
|
for (const [name, fields] of Object.entries(this.#entity.unique)) {
|
|
123
|
-
const
|
|
124
|
-
const unique = await this.uniqueIndex(name, fields,
|
|
183
|
+
const optional = this.getOptional(fields)
|
|
184
|
+
const unique = await this.uniqueIndex(name, fields, optional)
|
|
125
185
|
|
|
126
186
|
indexes.push(unique)
|
|
127
187
|
}
|
|
@@ -131,23 +191,24 @@ class Storage extends Connector {
|
|
|
131
191
|
for (const [suffix, declaration] of Object.entries(this.#entity.index)) {
|
|
132
192
|
const name = 'index_' + suffix
|
|
133
193
|
const fields = Object.fromEntries(Object.entries(declaration)
|
|
134
|
-
.map(([name, type]) => [name, INDEX_TYPES[type]]))
|
|
194
|
+
.map(([name, type]) => [name, INDEX_TYPES[type] ?? type]))
|
|
195
|
+
|
|
196
|
+
const optional = this.getOptional(Object.keys(fields))
|
|
197
|
+
const options = { name, sparse: optional.length > 0 }
|
|
135
198
|
|
|
136
|
-
|
|
199
|
+
console.info('Creating index', { fields, options })
|
|
137
200
|
|
|
138
|
-
await this.#
|
|
139
|
-
name,
|
|
140
|
-
sparse
|
|
141
|
-
})
|
|
201
|
+
await this.#collection.createIndex(fields, options)
|
|
202
|
+
.catch((e) => this.#logs.warn('Index creation failed', { name, fields, error: e }))
|
|
142
203
|
|
|
143
204
|
indexes.push(name)
|
|
144
205
|
}
|
|
145
206
|
}
|
|
146
207
|
|
|
147
|
-
await this.
|
|
208
|
+
await this.removeObsoleteIndexes(indexes)
|
|
148
209
|
}
|
|
149
210
|
|
|
150
|
-
async uniqueIndex (name, properties,
|
|
211
|
+
async uniqueIndex (name, properties, optional) {
|
|
151
212
|
const fields = properties.reduce((acc, property) => {
|
|
152
213
|
acc[property] = 1
|
|
153
214
|
return acc
|
|
@@ -155,48 +216,78 @@ class Storage extends Connector {
|
|
|
155
216
|
|
|
156
217
|
name = 'unique_' + name
|
|
157
218
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
219
|
+
const options = { name, unique: true }
|
|
220
|
+
|
|
221
|
+
if (optional.length > 0)
|
|
222
|
+
options.partialFilterExpression = Object.fromEntries(optional.map((field) => [field, { $exists: true }]))
|
|
223
|
+
|
|
224
|
+
console.info('Creating unique index', { name, fields, options })
|
|
225
|
+
|
|
226
|
+
await this.#collection.createIndex(fields, options)
|
|
227
|
+
.catch((e) => this.#logs.warn('Unique index creation failed', { name, fields, error: e }))
|
|
163
228
|
|
|
164
229
|
return name
|
|
165
230
|
}
|
|
166
231
|
|
|
167
|
-
async
|
|
168
|
-
const current = await this
|
|
232
|
+
async removeObsoleteIndexes (desired) {
|
|
233
|
+
const current = await this.getCurrentIndexes()
|
|
169
234
|
const obsolete = current.filter((name) => !desired.includes(name))
|
|
170
235
|
|
|
171
236
|
if (obsolete.length > 0) {
|
|
172
|
-
|
|
237
|
+
this.#logs.info('Removing obsolete indexes', { indexes: obsolete.join(', ') })
|
|
173
238
|
|
|
174
|
-
await this.#
|
|
239
|
+
await Promise.all(obsolete.map((name) => this.#collection.dropIndex(name)))
|
|
175
240
|
}
|
|
176
241
|
}
|
|
177
242
|
|
|
178
|
-
|
|
243
|
+
async getCurrentIndexes () {
|
|
244
|
+
try {
|
|
245
|
+
const array = await this.#collection.listIndexes().toArray()
|
|
246
|
+
|
|
247
|
+
return array.map(({ name }) => name).filter((name) => name !== '_id_')
|
|
248
|
+
} catch {
|
|
249
|
+
return []
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
getOptional (fields) {
|
|
179
254
|
const optional = []
|
|
180
255
|
|
|
181
|
-
for (const field of fields) {
|
|
182
|
-
if (!(field in this.#entity.schema.properties))
|
|
256
|
+
for (const field of fields) {
|
|
257
|
+
if (!field.includes('.') && !(field in this.#entity.schema.properties))
|
|
183
258
|
throw new Error(`Index field '${field}' is not defined.`)
|
|
184
|
-
}
|
|
185
259
|
|
|
186
|
-
if (!this.#entity.schema.required?.includes(field))
|
|
260
|
+
if (!this.#entity.schema.required?.includes(field))
|
|
187
261
|
optional.push(field)
|
|
188
|
-
}
|
|
189
262
|
}
|
|
190
263
|
|
|
191
|
-
|
|
192
|
-
|
|
264
|
+
return optional
|
|
265
|
+
}
|
|
193
266
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
267
|
+
debug (method, attributes) {
|
|
268
|
+
this.#logs.debug('Database query', {
|
|
269
|
+
method,
|
|
270
|
+
...attributes
|
|
271
|
+
})
|
|
198
272
|
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function toPipeline (criteria, options, sample) {
|
|
276
|
+
const pipeline = []
|
|
277
|
+
|
|
278
|
+
if (criteria !== undefined)
|
|
279
|
+
pipeline.push({ $match: criteria })
|
|
280
|
+
|
|
281
|
+
if (sample !== undefined)
|
|
282
|
+
pipeline.push({ $sample: { size: sample } })
|
|
283
|
+
|
|
284
|
+
if (options?.sort !== undefined)
|
|
285
|
+
pipeline.push({ $sort: options.sort })
|
|
286
|
+
|
|
287
|
+
if (options?.projection !== undefined)
|
|
288
|
+
pipeline.push({ $project: options.projection })
|
|
199
289
|
|
|
290
|
+
return pipeline
|
|
200
291
|
}
|
|
201
292
|
|
|
202
293
|
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
|