@opra/mongodb 1.0.0-alpha.2 → 1.0.0-alpha.21
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/cjs/adapter-utils/prepare-filter.js +3 -1
- package/cjs/adapter-utils/prepare-key-values.js +5 -4
- package/cjs/adapter-utils/prepare-patch.js +3 -2
- package/cjs/adapter-utils/prepare-projection.js +2 -3
- package/cjs/adapter-utils/prepare-sort.js +1 -1
- package/cjs/index.js +1 -1
- package/cjs/mongo-adapter.js +9 -4
- package/cjs/mongo-collection-service.js +111 -94
- package/cjs/mongo-entity-service.js +116 -93
- package/cjs/mongo-nested-service.js +262 -144
- package/cjs/mongo-service.js +62 -50
- package/cjs/mongo-singleton-service.js +62 -42
- package/esm/adapter-utils/prepare-filter.js +2 -0
- package/esm/adapter-utils/prepare-key-values.js +4 -3
- package/esm/adapter-utils/prepare-patch.js +2 -1
- package/esm/index.js +1 -1
- package/esm/mongo-adapter.js +9 -4
- package/esm/mongo-collection-service.js +111 -94
- package/esm/mongo-entity-service.js +116 -93
- package/esm/mongo-nested-service.js +262 -144
- package/esm/mongo-service.js +62 -50
- package/esm/mongo-singleton-service.js +62 -42
- package/package.json +12 -6
- package/types/adapter-utils/prepare-projection.d.ts +1 -1
- package/types/index.d.ts +1 -1
- package/types/mongo-adapter.d.ts +1 -1
- package/types/mongo-collection-service.d.ts +32 -62
- package/types/mongo-entity-service.d.ts +73 -47
- package/types/mongo-nested-service.d.ts +63 -28
- package/types/mongo-service.d.ts +44 -41
- package/types/mongo-singleton-service.d.ts +19 -29
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import omit from 'lodash.omit';
|
|
2
1
|
import { ComplexType, NotAcceptableError, ResourceNotAvailableError } from '@opra/common';
|
|
2
|
+
import omit from 'lodash.omit';
|
|
3
|
+
import { isNotNullish } from 'valgen';
|
|
3
4
|
import { MongoAdapter } from './mongo-adapter.js';
|
|
4
5
|
import { MongoService } from './mongo-service.js';
|
|
5
6
|
/**
|
|
@@ -21,7 +22,19 @@ export class MongoNestedService extends MongoService {
|
|
|
21
22
|
this.fieldName = fieldName;
|
|
22
23
|
this.nestedKey = options?.nestedKey || '_id';
|
|
23
24
|
this.defaultLimit = options?.defaultLimit || 10;
|
|
24
|
-
this
|
|
25
|
+
this.nestedFilter = options?.nestedFilter;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Retrieves the data type of the array field
|
|
29
|
+
*
|
|
30
|
+
* @returns {ComplexType} The complex data type of the field.
|
|
31
|
+
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
32
|
+
*/
|
|
33
|
+
get dataType() {
|
|
34
|
+
const t = super.dataType.getField(this.fieldName).type;
|
|
35
|
+
if (!(t instanceof ComplexType))
|
|
36
|
+
throw new NotAcceptableError(`Data type "${t.name}" is not a ComplexType`);
|
|
37
|
+
return t;
|
|
25
38
|
}
|
|
26
39
|
/**
|
|
27
40
|
* Asserts whether a resource with the specified parentId and id exists.
|
|
@@ -34,8 +47,9 @@ export class MongoNestedService extends MongoService {
|
|
|
34
47
|
* @throws {ResourceNotAvailableError} - If the resource does not exist.
|
|
35
48
|
*/
|
|
36
49
|
async assert(documentId, id, options) {
|
|
37
|
-
if (!(await this.exists(documentId, id, options)))
|
|
50
|
+
if (!(await this.exists(documentId, id, options))) {
|
|
38
51
|
throw new ResourceNotAvailableError(this.getResourceName() + '.' + this.nestedKey, documentId + '/' + id);
|
|
52
|
+
}
|
|
39
53
|
}
|
|
40
54
|
/**
|
|
41
55
|
* Adds a single item into the array field.
|
|
@@ -47,24 +61,21 @@ export class MongoNestedService extends MongoService {
|
|
|
47
61
|
* @throws {ResourceNotAvailableError} - If the parent document is not found.
|
|
48
62
|
*/
|
|
49
63
|
async create(documentId, input, options) {
|
|
50
|
-
const
|
|
51
|
-
if (id != null)
|
|
52
|
-
input._id = id;
|
|
53
|
-
const info = {
|
|
64
|
+
const command = {
|
|
54
65
|
crud: 'create',
|
|
55
66
|
method: 'create',
|
|
56
67
|
byId: false,
|
|
57
68
|
documentId,
|
|
58
|
-
nestedId: id,
|
|
59
69
|
input,
|
|
60
70
|
options,
|
|
61
71
|
};
|
|
62
|
-
return this.
|
|
72
|
+
return this._executeCommand(command, () => this._create(command));
|
|
63
73
|
}
|
|
64
|
-
async _create(
|
|
65
|
-
const
|
|
66
|
-
const
|
|
67
|
-
doc
|
|
74
|
+
async _create(command) {
|
|
75
|
+
const inputCodec = this.getInputCodec('create');
|
|
76
|
+
const { documentId, options } = command;
|
|
77
|
+
const doc = inputCodec(command.input);
|
|
78
|
+
doc._id = doc._id || this._generateId(command);
|
|
68
79
|
const docFilter = MongoAdapter.prepareKeyValues(documentId, ['_id']);
|
|
69
80
|
const r = await this._dbUpdateOne(docFilter, {
|
|
70
81
|
$push: { [this.fieldName]: doc },
|
|
@@ -72,12 +83,20 @@ export class MongoNestedService extends MongoService {
|
|
|
72
83
|
if (r.matchedCount) {
|
|
73
84
|
if (!options)
|
|
74
85
|
return doc;
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
86
|
+
const findCommand = {
|
|
87
|
+
crud: 'read',
|
|
88
|
+
method: 'findById',
|
|
89
|
+
byId: true,
|
|
90
|
+
documentId,
|
|
91
|
+
nestedId: doc[this.nestedKey],
|
|
92
|
+
options: {
|
|
93
|
+
...options,
|
|
94
|
+
sort: undefined,
|
|
95
|
+
filter: undefined,
|
|
96
|
+
skip: undefined,
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
const out = await this._findById(findCommand);
|
|
81
100
|
if (out)
|
|
82
101
|
return out;
|
|
83
102
|
}
|
|
@@ -91,20 +110,22 @@ export class MongoNestedService extends MongoService {
|
|
|
91
110
|
* @returns {Promise<number>} - A promise that resolves to the count of documents.
|
|
92
111
|
*/
|
|
93
112
|
async count(documentId, options) {
|
|
94
|
-
const
|
|
113
|
+
const command = {
|
|
95
114
|
crud: 'read',
|
|
96
115
|
method: 'count',
|
|
97
116
|
byId: false,
|
|
98
117
|
documentId,
|
|
99
118
|
options,
|
|
100
119
|
};
|
|
101
|
-
return this.
|
|
102
|
-
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(
|
|
103
|
-
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(
|
|
104
|
-
|
|
105
|
-
|
|
120
|
+
return this._executeCommand(command, async () => {
|
|
121
|
+
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(command)]);
|
|
122
|
+
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(command), command.options?.filter]);
|
|
123
|
+
command.options = { ...command.options, filter, documentFilter };
|
|
124
|
+
return this._count(command);
|
|
125
|
+
});
|
|
106
126
|
}
|
|
107
|
-
async _count(
|
|
127
|
+
async _count(command) {
|
|
128
|
+
const { documentId, options } = command;
|
|
108
129
|
const matchFilter = MongoAdapter.prepareFilter([
|
|
109
130
|
MongoAdapter.prepareKeyValues(documentId, ['_id']),
|
|
110
131
|
options?.documentFilter,
|
|
@@ -137,7 +158,7 @@ export class MongoNestedService extends MongoService {
|
|
|
137
158
|
* @return {Promise<number>} - A Promise that resolves to the number of elements deleted (1 if successful, 0 if not).
|
|
138
159
|
*/
|
|
139
160
|
async delete(documentId, nestedId, options) {
|
|
140
|
-
const
|
|
161
|
+
const command = {
|
|
141
162
|
crud: 'delete',
|
|
142
163
|
method: 'delete',
|
|
143
164
|
byId: true,
|
|
@@ -145,13 +166,17 @@ export class MongoNestedService extends MongoService {
|
|
|
145
166
|
nestedId,
|
|
146
167
|
options,
|
|
147
168
|
};
|
|
148
|
-
return this.
|
|
149
|
-
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(
|
|
150
|
-
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(
|
|
151
|
-
|
|
152
|
-
|
|
169
|
+
return this._executeCommand(command, async () => {
|
|
170
|
+
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(command)]);
|
|
171
|
+
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(command), command.options?.filter]);
|
|
172
|
+
command.options = { ...command.options, filter, documentFilter };
|
|
173
|
+
return this._delete(command);
|
|
174
|
+
});
|
|
153
175
|
}
|
|
154
|
-
async _delete(
|
|
176
|
+
async _delete(command) {
|
|
177
|
+
const { documentId, nestedId, options } = command;
|
|
178
|
+
isNotNullish(documentId, { label: 'documentId' });
|
|
179
|
+
isNotNullish(documentId, { label: 'nestedId' });
|
|
155
180
|
const matchFilter = MongoAdapter.prepareFilter([
|
|
156
181
|
MongoAdapter.prepareKeyValues(documentId, ['_id']),
|
|
157
182
|
options?.documentFilter,
|
|
@@ -170,20 +195,22 @@ export class MongoNestedService extends MongoService {
|
|
|
170
195
|
* @returns {Promise<number>} - A Promise that resolves to the number of items deleted.
|
|
171
196
|
*/
|
|
172
197
|
async deleteMany(documentId, options) {
|
|
173
|
-
const
|
|
198
|
+
const command = {
|
|
174
199
|
crud: 'delete',
|
|
175
200
|
method: 'deleteMany',
|
|
176
201
|
byId: false,
|
|
177
202
|
documentId,
|
|
178
203
|
options,
|
|
179
204
|
};
|
|
180
|
-
return this.
|
|
181
|
-
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(
|
|
182
|
-
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(
|
|
183
|
-
|
|
184
|
-
|
|
205
|
+
return this._executeCommand(command, async () => {
|
|
206
|
+
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(command)]);
|
|
207
|
+
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(command), command.options?.filter]);
|
|
208
|
+
command.options = { ...command.options, filter, documentFilter };
|
|
209
|
+
return this._deleteMany(command);
|
|
210
|
+
});
|
|
185
211
|
}
|
|
186
|
-
async _deleteMany(
|
|
212
|
+
async _deleteMany(command) {
|
|
213
|
+
const { documentId, options } = command;
|
|
187
214
|
const matchFilter = MongoAdapter.prepareFilter([
|
|
188
215
|
MongoAdapter.prepareKeyValues(documentId, ['_id']),
|
|
189
216
|
options?.documentFilter,
|
|
@@ -207,7 +234,24 @@ export class MongoNestedService extends MongoService {
|
|
|
207
234
|
* @returns {Promise<boolean>} - A promise that resolves to a boolean indicating if the record exists or not.
|
|
208
235
|
*/
|
|
209
236
|
async exists(documentId, nestedId, options) {
|
|
210
|
-
|
|
237
|
+
const command = {
|
|
238
|
+
crud: 'read',
|
|
239
|
+
method: 'exists',
|
|
240
|
+
byId: true,
|
|
241
|
+
documentId,
|
|
242
|
+
nestedId,
|
|
243
|
+
options,
|
|
244
|
+
};
|
|
245
|
+
return this._executeCommand(command, async () => {
|
|
246
|
+
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(command)]);
|
|
247
|
+
const filter = MongoAdapter.prepareFilter([
|
|
248
|
+
await this._getNestedFilter(command),
|
|
249
|
+
documentFilter,
|
|
250
|
+
command.options?.filter,
|
|
251
|
+
]);
|
|
252
|
+
command.options = { ...command.options, filter };
|
|
253
|
+
return !!(await this._findById(command));
|
|
254
|
+
});
|
|
211
255
|
}
|
|
212
256
|
/**
|
|
213
257
|
* Checks if an object with the given arguments exists.
|
|
@@ -217,7 +261,20 @@ export class MongoNestedService extends MongoService {
|
|
|
217
261
|
* @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the object exists or not.
|
|
218
262
|
*/
|
|
219
263
|
async existsOne(documentId, options) {
|
|
220
|
-
|
|
264
|
+
const command = {
|
|
265
|
+
crud: 'read',
|
|
266
|
+
method: 'exists',
|
|
267
|
+
byId: false,
|
|
268
|
+
documentId,
|
|
269
|
+
options,
|
|
270
|
+
};
|
|
271
|
+
return this._executeCommand(command, async () => {
|
|
272
|
+
const documentFilter = await this._getDocumentFilter(command);
|
|
273
|
+
const filter = MongoAdapter.prepareFilter([documentFilter, command.options?.filter]);
|
|
274
|
+
const findCommand = command;
|
|
275
|
+
findCommand.options = { ...command.options, filter, documentFilter, projection: ['_id'] };
|
|
276
|
+
return !!(await this._findOne(findCommand));
|
|
277
|
+
});
|
|
221
278
|
}
|
|
222
279
|
/**
|
|
223
280
|
* Finds an element in array field by its parent ID and ID.
|
|
@@ -228,7 +285,7 @@ export class MongoNestedService extends MongoService {
|
|
|
228
285
|
* @returns {Promise<PartialDTO<T> | undefined>} - A promise that resolves to the found document or undefined if not found.
|
|
229
286
|
*/
|
|
230
287
|
async findById(documentId, nestedId, options) {
|
|
231
|
-
const
|
|
288
|
+
const command = {
|
|
232
289
|
crud: 'read',
|
|
233
290
|
method: 'findById',
|
|
234
291
|
byId: true,
|
|
@@ -236,24 +293,32 @@ export class MongoNestedService extends MongoService {
|
|
|
236
293
|
nestedId,
|
|
237
294
|
options,
|
|
238
295
|
};
|
|
239
|
-
return this.
|
|
240
|
-
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(
|
|
241
|
-
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(
|
|
242
|
-
|
|
243
|
-
|
|
296
|
+
return this._executeCommand(command, async () => {
|
|
297
|
+
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(command)]);
|
|
298
|
+
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(command), command.options?.filter]);
|
|
299
|
+
command.options = { ...command.options, filter, documentFilter };
|
|
300
|
+
return this._findById(command);
|
|
301
|
+
});
|
|
244
302
|
}
|
|
245
|
-
async _findById(
|
|
303
|
+
async _findById(command) {
|
|
304
|
+
const { documentId, nestedId, options } = command;
|
|
305
|
+
isNotNullish(documentId, { label: 'documentId' });
|
|
306
|
+
isNotNullish(nestedId, { label: 'nestedId' });
|
|
246
307
|
const filter = MongoAdapter.prepareFilter([
|
|
247
308
|
MongoAdapter.prepareKeyValues(nestedId, [this.nestedKey]),
|
|
248
309
|
options?.filter,
|
|
249
310
|
]);
|
|
250
|
-
const
|
|
251
|
-
...
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
311
|
+
const findManyCommand = {
|
|
312
|
+
...command,
|
|
313
|
+
options: {
|
|
314
|
+
...options,
|
|
315
|
+
filter,
|
|
316
|
+
limit: 1,
|
|
317
|
+
skip: undefined,
|
|
318
|
+
sort: undefined,
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
const rows = await this._findMany(findManyCommand);
|
|
257
322
|
return rows?.[0];
|
|
258
323
|
}
|
|
259
324
|
/**
|
|
@@ -264,24 +329,31 @@ export class MongoNestedService extends MongoService {
|
|
|
264
329
|
* @returns {Promise<PartialDTO<T> | undefined>} A promise that resolves to the first matching document, or `undefined` if no match is found.
|
|
265
330
|
*/
|
|
266
331
|
async findOne(documentId, options) {
|
|
267
|
-
const
|
|
332
|
+
const command = {
|
|
268
333
|
crud: 'read',
|
|
269
334
|
method: 'findOne',
|
|
270
335
|
byId: false,
|
|
271
336
|
documentId,
|
|
272
337
|
options,
|
|
273
338
|
};
|
|
274
|
-
return this.
|
|
275
|
-
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(
|
|
276
|
-
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
async _findOne(documentId, options) {
|
|
281
|
-
const rows = await this._findMany(documentId, {
|
|
282
|
-
...options,
|
|
283
|
-
limit: 1,
|
|
339
|
+
return this._executeCommand(command, async () => {
|
|
340
|
+
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(command)]);
|
|
341
|
+
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(command), command.options?.filter]);
|
|
342
|
+
command.options = { ...command.options, filter, documentFilter };
|
|
343
|
+
return this._findOne(command);
|
|
284
344
|
});
|
|
345
|
+
}
|
|
346
|
+
async _findOne(command) {
|
|
347
|
+
const { documentId, options } = command;
|
|
348
|
+
isNotNullish(documentId, { label: 'documentId' });
|
|
349
|
+
const findManyCommand = {
|
|
350
|
+
...command,
|
|
351
|
+
options: {
|
|
352
|
+
...options,
|
|
353
|
+
limit: 1,
|
|
354
|
+
},
|
|
355
|
+
};
|
|
356
|
+
const rows = await this._findMany(findManyCommand);
|
|
285
357
|
return rows?.[0];
|
|
286
358
|
}
|
|
287
359
|
/**
|
|
@@ -292,28 +364,31 @@ export class MongoNestedService extends MongoService {
|
|
|
292
364
|
* @returns {Promise<PartialDTO<T>[]>} - The found documents.
|
|
293
365
|
*/
|
|
294
366
|
async findMany(documentId, options) {
|
|
295
|
-
const
|
|
367
|
+
const command = {
|
|
296
368
|
crud: 'read',
|
|
297
369
|
method: 'findMany',
|
|
298
370
|
byId: false,
|
|
299
371
|
documentId,
|
|
300
372
|
options,
|
|
301
373
|
};
|
|
302
|
-
return this.
|
|
303
|
-
const documentFilter = await this._getDocumentFilter(
|
|
304
|
-
const nestedFilter = await this._getNestedFilter(
|
|
305
|
-
|
|
306
|
-
...options,
|
|
307
|
-
documentFilter,
|
|
374
|
+
return this._executeCommand(command, async () => {
|
|
375
|
+
const documentFilter = await this._getDocumentFilter(command);
|
|
376
|
+
const nestedFilter = await this._getNestedFilter(command);
|
|
377
|
+
command.options = {
|
|
378
|
+
...command.options,
|
|
308
379
|
nestedFilter,
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
380
|
+
documentFilter,
|
|
381
|
+
limit: command.options?.limit || this.defaultLimit,
|
|
382
|
+
};
|
|
383
|
+
return this._findMany(command);
|
|
384
|
+
});
|
|
312
385
|
}
|
|
313
|
-
async _findMany(
|
|
386
|
+
async _findMany(command) {
|
|
387
|
+
const { documentId, options } = command;
|
|
388
|
+
isNotNullish(documentId, { label: 'documentId' });
|
|
314
389
|
const matchFilter = MongoAdapter.prepareFilter([
|
|
315
390
|
MongoAdapter.prepareKeyValues(documentId, ['_id']),
|
|
316
|
-
options
|
|
391
|
+
options?.documentFilter,
|
|
317
392
|
]);
|
|
318
393
|
const mongoOptions = {
|
|
319
394
|
...omit(options, ['documentFilter', 'nestedFilter', 'projection', 'sort', 'skip', 'limit', 'filter', 'count']),
|
|
@@ -324,7 +399,7 @@ export class MongoNestedService extends MongoService {
|
|
|
324
399
|
{ $unwind: { path: '$' + this.fieldName } },
|
|
325
400
|
{ $replaceRoot: { newRoot: '$' + this.fieldName } },
|
|
326
401
|
];
|
|
327
|
-
if (options?.filter || options
|
|
402
|
+
if (options?.filter || options?.nestedFilter) {
|
|
328
403
|
const optionsFilter = MongoAdapter.prepareFilter([options?.filter, options.nestedFilter]);
|
|
329
404
|
stages.push({ $match: optionsFilter });
|
|
330
405
|
}
|
|
@@ -336,14 +411,14 @@ export class MongoNestedService extends MongoService {
|
|
|
336
411
|
stages.push({ $sort: sort });
|
|
337
412
|
}
|
|
338
413
|
stages.push({ $limit: limit });
|
|
339
|
-
const dataType = this.
|
|
414
|
+
const dataType = this.dataType;
|
|
340
415
|
const projection = MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
341
416
|
if (projection)
|
|
342
417
|
stages.push({ $project: projection });
|
|
343
|
-
const decode = this.getDecoder();
|
|
344
418
|
const cursor = await this._dbAggregate(stages, mongoOptions);
|
|
345
419
|
try {
|
|
346
|
-
const
|
|
420
|
+
const outputCodec = this.getOutputCodec('find');
|
|
421
|
+
const out = await (await cursor.toArray()).map((r) => outputCodec(r));
|
|
347
422
|
return out;
|
|
348
423
|
}
|
|
349
424
|
finally {
|
|
@@ -359,28 +434,31 @@ export class MongoNestedService extends MongoService {
|
|
|
359
434
|
* @returns {Promise<PartialDTO<T>[]>} - The found documents.
|
|
360
435
|
*/
|
|
361
436
|
async findManyWithCount(documentId, options) {
|
|
362
|
-
const
|
|
437
|
+
const command = {
|
|
363
438
|
crud: 'read',
|
|
364
439
|
method: 'findMany',
|
|
365
440
|
byId: false,
|
|
366
441
|
documentId,
|
|
367
442
|
options,
|
|
368
443
|
};
|
|
369
|
-
return this.
|
|
370
|
-
const documentFilter = await this._getDocumentFilter(
|
|
371
|
-
const nestedFilter = await this._getNestedFilter(
|
|
372
|
-
|
|
373
|
-
...options,
|
|
374
|
-
documentFilter,
|
|
444
|
+
return this._executeCommand(command, async () => {
|
|
445
|
+
const documentFilter = await this._getDocumentFilter(command);
|
|
446
|
+
const nestedFilter = await this._getNestedFilter(command);
|
|
447
|
+
command.options = {
|
|
448
|
+
...command.options,
|
|
375
449
|
nestedFilter,
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
450
|
+
documentFilter,
|
|
451
|
+
limit: command.options?.limit || this.defaultLimit,
|
|
452
|
+
};
|
|
453
|
+
return this._findManyWithCount(command);
|
|
454
|
+
});
|
|
379
455
|
}
|
|
380
|
-
async _findManyWithCount(
|
|
456
|
+
async _findManyWithCount(command) {
|
|
457
|
+
const { documentId, options } = command;
|
|
458
|
+
isNotNullish(documentId, { label: 'documentId' });
|
|
381
459
|
const matchFilter = MongoAdapter.prepareFilter([
|
|
382
460
|
MongoAdapter.prepareKeyValues(documentId, ['_id']),
|
|
383
|
-
options
|
|
461
|
+
options?.documentFilter,
|
|
384
462
|
]);
|
|
385
463
|
const mongoOptions = {
|
|
386
464
|
...omit(options, ['pick', 'include', 'omit', 'sort', 'skip', 'limit', 'filter', 'count']),
|
|
@@ -398,8 +476,8 @@ export class MongoNestedService extends MongoService {
|
|
|
398
476
|
},
|
|
399
477
|
},
|
|
400
478
|
];
|
|
401
|
-
if (options?.filter || options
|
|
402
|
-
const optionsFilter = MongoAdapter.prepareFilter([options?.filter, options
|
|
479
|
+
if (options?.filter || options?.nestedFilter) {
|
|
480
|
+
const optionsFilter = MongoAdapter.prepareFilter([options?.filter, options?.nestedFilter]);
|
|
403
481
|
dataStages.push({ $match: optionsFilter });
|
|
404
482
|
}
|
|
405
483
|
if (options?.skip)
|
|
@@ -410,19 +488,19 @@ export class MongoNestedService extends MongoService {
|
|
|
410
488
|
dataStages.push({ $sort: sort });
|
|
411
489
|
}
|
|
412
490
|
dataStages.push({ $limit: limit });
|
|
413
|
-
const dataType = this.
|
|
491
|
+
const dataType = this.dataType;
|
|
414
492
|
const projection = MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
415
493
|
if (projection)
|
|
416
494
|
dataStages.push({ $project: projection });
|
|
417
|
-
const decode = this.getDecoder();
|
|
418
495
|
const cursor = await this._dbAggregate(stages, {
|
|
419
496
|
...mongoOptions,
|
|
420
497
|
});
|
|
421
498
|
try {
|
|
422
499
|
const facetResult = await cursor.toArray();
|
|
500
|
+
const outputCodec = this.getOutputCodec('find');
|
|
423
501
|
return {
|
|
424
502
|
count: facetResult[0].count[0].totalMatches || 0,
|
|
425
|
-
items: facetResult[0].data.map((r) =>
|
|
503
|
+
items: facetResult[0].data.map((r) => outputCodec(r)),
|
|
426
504
|
};
|
|
427
505
|
}
|
|
428
506
|
finally {
|
|
@@ -441,8 +519,9 @@ export class MongoNestedService extends MongoService {
|
|
|
441
519
|
*/
|
|
442
520
|
async get(documentId, nestedId, options) {
|
|
443
521
|
const out = await this.findById(documentId, nestedId, options);
|
|
444
|
-
if (!out)
|
|
522
|
+
if (!out) {
|
|
445
523
|
throw new ResourceNotAvailableError(this.getResourceName() + '.' + this.nestedKey, documentId + '/' + nestedId);
|
|
524
|
+
}
|
|
446
525
|
return out;
|
|
447
526
|
}
|
|
448
527
|
/**
|
|
@@ -451,21 +530,44 @@ export class MongoNestedService extends MongoService {
|
|
|
451
530
|
* @param {AnyId} documentId - The ID of the document to update.
|
|
452
531
|
* @param {AnyId} nestedId - The ID of the item to update within the document.
|
|
453
532
|
* @param {PatchDTO<T>} input - The new data to update the item with.
|
|
454
|
-
* @param {MongoNestedService.
|
|
533
|
+
* @param {MongoNestedService.UpdateOneOptions<T>} [options] - Additional update options.
|
|
455
534
|
* @returns {Promise<PartialDTO<T> | undefined>} The updated item or undefined if it does not exist.
|
|
456
535
|
* @throws {Error} If an error occurs while updating the item.
|
|
457
536
|
*/
|
|
458
537
|
async update(documentId, nestedId, input, options) {
|
|
459
|
-
const
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
538
|
+
const command = {
|
|
539
|
+
crud: 'update',
|
|
540
|
+
method: 'update',
|
|
541
|
+
byId: true,
|
|
542
|
+
documentId,
|
|
543
|
+
nestedId,
|
|
544
|
+
input,
|
|
545
|
+
options,
|
|
546
|
+
};
|
|
547
|
+
return this._executeCommand(command, async () => {
|
|
548
|
+
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(command)]);
|
|
549
|
+
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(command), command.options?.filter]);
|
|
550
|
+
command.options = {
|
|
551
|
+
...command.options,
|
|
552
|
+
filter,
|
|
553
|
+
documentFilter,
|
|
554
|
+
};
|
|
555
|
+
const r = await this._updateOnly(command);
|
|
556
|
+
if (r) {
|
|
557
|
+
const findCommand = {
|
|
558
|
+
crud: 'read',
|
|
559
|
+
method: 'findById',
|
|
560
|
+
byId: true,
|
|
561
|
+
documentId,
|
|
562
|
+
nestedId,
|
|
563
|
+
options: { ...options, sort: undefined },
|
|
564
|
+
};
|
|
565
|
+
const out = this._findById(findCommand);
|
|
566
|
+
if (out)
|
|
567
|
+
return out;
|
|
568
|
+
}
|
|
569
|
+
throw new ResourceNotAvailableError(this.getResourceName() + '.' + this.nestedKey, documentId + '/' + nestedId);
|
|
465
570
|
});
|
|
466
|
-
if (out)
|
|
467
|
-
return out;
|
|
468
|
-
throw new ResourceNotAvailableError(this.getResourceName() + '.' + this.nestedKey, documentId + '/' + nestedId);
|
|
469
571
|
}
|
|
470
572
|
/**
|
|
471
573
|
* Update an array element with new data. Returns 1 if document updated 0 otherwise.
|
|
@@ -473,29 +575,45 @@ export class MongoNestedService extends MongoService {
|
|
|
473
575
|
* @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
|
|
474
576
|
* @param {MongoAdapter.AnyId} nestedId - The ID of the document to update.
|
|
475
577
|
* @param {PatchDTO<T>} input - The partial input object containing the fields to update.
|
|
476
|
-
* @param {MongoNestedService.
|
|
578
|
+
* @param {MongoNestedService.UpdateOneOptions<T>} [options] - Optional update options.
|
|
477
579
|
* @returns {Promise<number>} - A promise that resolves to the number of elements updated.
|
|
478
580
|
*/
|
|
479
581
|
async updateOnly(documentId, nestedId, input, options) {
|
|
480
|
-
const
|
|
582
|
+
const command = {
|
|
481
583
|
crud: 'update',
|
|
482
584
|
method: 'update',
|
|
483
585
|
byId: true,
|
|
484
586
|
documentId,
|
|
485
587
|
nestedId,
|
|
588
|
+
input,
|
|
486
589
|
options,
|
|
487
590
|
};
|
|
488
|
-
return this.
|
|
489
|
-
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(
|
|
490
|
-
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(
|
|
491
|
-
|
|
492
|
-
|
|
591
|
+
return this._executeCommand(command, async () => {
|
|
592
|
+
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(command)]);
|
|
593
|
+
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(command), command.options?.filter]);
|
|
594
|
+
command.options = {
|
|
595
|
+
...command.options,
|
|
596
|
+
filter,
|
|
597
|
+
documentFilter,
|
|
598
|
+
};
|
|
599
|
+
return await this._updateOnly(command);
|
|
600
|
+
});
|
|
493
601
|
}
|
|
494
|
-
async _updateOnly(
|
|
602
|
+
async _updateOnly(command) {
|
|
603
|
+
const { documentId, nestedId, options } = command;
|
|
604
|
+
isNotNullish(documentId, { label: 'documentId' });
|
|
605
|
+
isNotNullish(nestedId, { label: 'nestedId' });
|
|
495
606
|
let filter = MongoAdapter.prepareKeyValues(nestedId, [this.nestedKey]);
|
|
496
607
|
if (options?.filter)
|
|
497
608
|
filter = MongoAdapter.prepareFilter([filter, options?.filter]);
|
|
498
|
-
|
|
609
|
+
const updateManyCommand = {
|
|
610
|
+
...command,
|
|
611
|
+
options: {
|
|
612
|
+
...command.options,
|
|
613
|
+
filter,
|
|
614
|
+
},
|
|
615
|
+
};
|
|
616
|
+
return await this._updateMany(updateManyCommand);
|
|
499
617
|
}
|
|
500
618
|
/**
|
|
501
619
|
* Updates multiple array elements in document
|
|
@@ -506,7 +624,7 @@ export class MongoNestedService extends MongoService {
|
|
|
506
624
|
* @returns {Promise<number>} - A promise that resolves to the number of documents updated.
|
|
507
625
|
*/
|
|
508
626
|
async updateMany(documentId, input, options) {
|
|
509
|
-
const
|
|
627
|
+
const command = {
|
|
510
628
|
crud: 'update',
|
|
511
629
|
method: 'updateMany',
|
|
512
630
|
documentId,
|
|
@@ -514,15 +632,19 @@ export class MongoNestedService extends MongoService {
|
|
|
514
632
|
input,
|
|
515
633
|
options,
|
|
516
634
|
};
|
|
517
|
-
return this.
|
|
518
|
-
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(
|
|
519
|
-
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(
|
|
520
|
-
|
|
521
|
-
|
|
635
|
+
return this._executeCommand(command, async () => {
|
|
636
|
+
const documentFilter = MongoAdapter.prepareFilter([await this._getDocumentFilter(command)]);
|
|
637
|
+
const filter = MongoAdapter.prepareFilter([await this._getNestedFilter(command), command.options?.filter]);
|
|
638
|
+
command.options = { ...command.options, filter, documentFilter };
|
|
639
|
+
return this._updateMany(command);
|
|
640
|
+
});
|
|
522
641
|
}
|
|
523
|
-
async _updateMany(
|
|
524
|
-
const
|
|
525
|
-
|
|
642
|
+
async _updateMany(command) {
|
|
643
|
+
const { documentId, input } = command;
|
|
644
|
+
isNotNullish(documentId, { label: 'documentId' });
|
|
645
|
+
let options = command.options;
|
|
646
|
+
const inputCodec = this.getInputCodec('update');
|
|
647
|
+
const doc = inputCodec(input);
|
|
526
648
|
if (!Object.keys(doc).length)
|
|
527
649
|
return 0;
|
|
528
650
|
const matchFilter = MongoAdapter.prepareFilter([
|
|
@@ -539,22 +661,18 @@ export class MongoNestedService extends MongoService {
|
|
|
539
661
|
fieldPrefix: this.fieldName + (options?.filter ? '.$[elem].' : '.$[].'),
|
|
540
662
|
});
|
|
541
663
|
const r = await this._dbUpdateOne(matchFilter, update, options);
|
|
542
|
-
if (options?.count)
|
|
543
|
-
|
|
664
|
+
if (options?.count) {
|
|
665
|
+
const countCommand = {
|
|
666
|
+
crud: 'read',
|
|
667
|
+
method: 'count',
|
|
668
|
+
byId: false,
|
|
669
|
+
documentId,
|
|
670
|
+
options,
|
|
671
|
+
};
|
|
672
|
+
return await this._count(countCommand);
|
|
673
|
+
}
|
|
544
674
|
return r.modifiedCount || 0;
|
|
545
675
|
}
|
|
546
|
-
/**
|
|
547
|
-
* Retrieves the data type of the array field
|
|
548
|
-
*
|
|
549
|
-
* @returns {ComplexType} The complex data type of the field.
|
|
550
|
-
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
551
|
-
*/
|
|
552
|
-
getDataType() {
|
|
553
|
-
const t = super.getDataType().getField(this.fieldName).type;
|
|
554
|
-
if (!(t instanceof ComplexType))
|
|
555
|
-
throw new NotAcceptableError(`Data type "${t.name}" is not a ComplexType`);
|
|
556
|
-
return t;
|
|
557
|
-
}
|
|
558
676
|
/**
|
|
559
677
|
* Retrieves the common filter used for querying array elements.
|
|
560
678
|
* This method is mostly used for security issues like securing multi-tenant applications.
|
|
@@ -564,6 +682,6 @@ export class MongoNestedService extends MongoService {
|
|
|
564
682
|
* that resolves to the common filter, or undefined if not available.
|
|
565
683
|
*/
|
|
566
684
|
_getNestedFilter(args) {
|
|
567
|
-
return typeof this
|
|
685
|
+
return typeof this.nestedFilter === 'function' ? this.nestedFilter(args, this) : this.nestedFilter;
|
|
568
686
|
}
|
|
569
687
|
}
|