@opra/mongodb 0.33.13 → 1.0.0-alpha.2

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.
@@ -1,86 +1,86 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MongoArrayService = void 0;
3
+ exports.MongoNestedService = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const lodash_omit_1 = tslib_1.__importDefault(require("lodash.omit"));
6
- const mongodb_1 = require("mongodb");
7
6
  const common_1 = require("@opra/common");
8
7
  const mongo_adapter_js_1 = require("./mongo-adapter.js");
9
8
  const mongo_service_js_1 = require("./mongo-service.js");
10
9
  /**
11
10
  * A class that provides methods to perform operations on an array field in a MongoDB collection.
11
+ * @class MongoNestedService
12
12
  * @template T The type of the array item.
13
13
  */
14
- class MongoArrayService extends mongo_service_js_1.MongoService {
14
+ class MongoNestedService extends mongo_service_js_1.MongoService {
15
15
  /**
16
16
  * Constructs a new instance
17
17
  *
18
18
  * @param {Type | string} dataType - The data type of the array elements.
19
19
  * @param {string} fieldName - The name of the field in the document representing the array.
20
- * @param {MongoArrayService.Options} [options] - The options for the array service.
20
+ * @param {MongoNestedService.Options} [options] - The options for the array service.
21
21
  * @constructor
22
22
  */
23
23
  constructor(dataType, fieldName, options) {
24
24
  super(dataType, options);
25
25
  this.fieldName = fieldName;
26
+ this.nestedKey = options?.nestedKey || '_id';
26
27
  this.defaultLimit = options?.defaultLimit || 10;
27
- this.collectionKey = options?.collectionKey || '_id';
28
- this.arrayKey = options?.arrayKey || '_id';
29
- this.$documentFilter = options?.documentFilter;
30
- this.$arrayFilter = options?.arrayFilter;
31
- this.$interceptor = this.$interceptor || options?.interceptor;
28
+ this.$nestedFilter = options?.$nestedFilter;
32
29
  }
33
30
  /**
34
31
  * Asserts whether a resource with the specified parentId and id exists.
35
32
  * Throws a ResourceNotFoundError if the resource does not exist.
36
33
  *
37
- * @param {AnyId} documentId - The ID of the parent document.
38
- * @param {AnyId} id - The ID of the resource.
39
- * @param {MongoArrayService.ExistsOptions} [options] - Optional parameters for checking resource existence.
34
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
35
+ * @param {MongoAdapter.AnyId} id - The ID of the resource.
36
+ * @param {MongoNestedService.ExistsOptions<T>} [options] - Optional parameters for checking resource existence.
40
37
  * @return {Promise<void>} - A promise that resolves with no value upon success.
41
38
  * @throws {ResourceNotAvailableError} - If the resource does not exist.
42
39
  */
43
40
  async assert(documentId, id, options) {
44
41
  if (!(await this.exists(documentId, id, options)))
45
- throw new common_1.ResourceNotAvailableError(this.getResourceName() + '.' + this.arrayKey, documentId + '/' + id);
42
+ throw new common_1.ResourceNotAvailableError(this.getResourceName() + '.' + this.nestedKey, documentId + '/' + id);
46
43
  }
47
44
  /**
48
45
  * Adds a single item into the array field.
49
46
  *
50
- * @param {AnyId} documentId - The ID of the parent document.
47
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
51
48
  * @param {T} input - The item to be added to the array field.
52
- * @param {MongoArrayService.CreateOptions} [options] - Optional options for the create operation.
49
+ * @param {MongoNestedService.CreateOptions} [options] - Optional options for the create operation.
53
50
  * @return {Promise<PartialDTO<T>>} - A promise that resolves with the partial output of the created item.
54
51
  * @throws {ResourceNotAvailableError} - If the parent document is not found.
55
52
  */
56
53
  async create(documentId, input, options) {
54
+ const id = input._id || this._generateId();
55
+ if (id != null)
56
+ input._id = id;
57
57
  const info = {
58
58
  crud: 'create',
59
59
  method: 'create',
60
60
  byId: false,
61
61
  documentId,
62
- itemId: input._id,
62
+ nestedId: id,
63
63
  input,
64
- options
64
+ options,
65
65
  };
66
66
  return this._intercept(() => this._create(documentId, input, options), info);
67
67
  }
68
68
  async _create(documentId, input, options) {
69
69
  const encode = this.getEncoder('create');
70
- const doc = encode(input, { coerce: true });
70
+ const doc = encode(input);
71
71
  doc._id = doc._id || this._generateId();
72
- const docFilter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, [this.collectionKey]);
73
- const r = await this.__updateOne(docFilter, {
74
- $push: { [this.fieldName]: doc }
72
+ const docFilter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, ['_id']);
73
+ const r = await this._dbUpdateOne(docFilter, {
74
+ $push: { [this.fieldName]: doc },
75
75
  }, options);
76
76
  if (r.matchedCount) {
77
77
  if (!options)
78
78
  return doc;
79
- const id = doc[this.arrayKey];
79
+ const id = doc[this.nestedKey];
80
80
  const out = await this._findById(documentId, id, {
81
81
  ...options,
82
82
  filter: undefined,
83
- skip: undefined
83
+ skip: undefined,
84
84
  });
85
85
  if (out)
86
86
  return out;
@@ -90,9 +90,8 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
90
90
  /**
91
91
  * Counts the number of documents in the collection that match the specified parentId and options.
92
92
  *
93
- * @param {AnyId} documentId - The ID of the parent document.
94
- * @param {object} options - Optional parameters for counting.
95
- * @param {object} options.filter - The filter object to apply to the count operation.
93
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
94
+ * @param {MongoNestedService.CountOptions<T>} [options] - Optional parameters for counting.
96
95
  * @returns {Promise<number>} - A promise that resolves to the count of documents.
97
96
  */
98
97
  async count(documentId, options) {
@@ -101,35 +100,30 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
101
100
  method: 'count',
102
101
  byId: false,
103
102
  documentId,
104
- options
103
+ options,
105
104
  };
106
105
  return this._intercept(async () => {
107
- const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
108
- await this._getDocumentFilter(info)
109
- ]);
110
- const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
111
- await this._getArrayFilter(info),
112
- options?.filter
113
- ]);
106
+ const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getDocumentFilter(info)]);
107
+ const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getNestedFilter(info), options?.filter]);
114
108
  return this._count(documentId, { ...options, filter, documentFilter });
115
109
  }, info);
116
110
  }
117
111
  async _count(documentId, options) {
118
112
  const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
119
- mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, [this.collectionKey]),
120
- options?.documentFilter
113
+ mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, ['_id']),
114
+ options?.documentFilter,
121
115
  ]);
122
116
  const stages = [
123
117
  { $match: matchFilter },
124
- { $unwind: { path: "$" + this.fieldName } },
125
- { $replaceRoot: { newRoot: "$" + this.fieldName } }
118
+ { $unwind: { path: '$' + this.fieldName } },
119
+ { $replaceRoot: { newRoot: '$' + this.fieldName } },
126
120
  ];
127
121
  if (options?.filter) {
128
122
  const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
129
123
  stages.push({ $match: filter });
130
124
  }
131
125
  stages.push({ $count: '*' });
132
- const r = await this.__aggregate(stages, options);
126
+ const r = await this._dbAggregate(stages, options);
133
127
  try {
134
128
  const n = await r.next();
135
129
  return n?.['*'] || 0;
@@ -141,50 +135,42 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
141
135
  /**
142
136
  * Deletes an element from an array within a document in the MongoDB collection.
143
137
  *
144
- * @param {AnyId} documentId - The ID of the parent document.
145
- * @param {AnyId} id - The ID of the element to delete from the array.
146
- * @param {MongoArrayService.DeleteOptions<T>} [options] - Additional options for the delete operation.
138
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
139
+ * @param {MongoAdapter.AnyId} nestedId - The ID of the element to delete from the nested array.
140
+ * @param {MongoNestedService.DeleteOptions<T>} [options] - Additional options for the delete operation.
147
141
  * @return {Promise<number>} - A Promise that resolves to the number of elements deleted (1 if successful, 0 if not).
148
142
  */
149
- async delete(documentId, id, options) {
143
+ async delete(documentId, nestedId, options) {
150
144
  const info = {
151
145
  crud: 'delete',
152
146
  method: 'delete',
153
147
  byId: true,
154
148
  documentId,
155
- itemId: id,
156
- options
149
+ nestedId,
150
+ options,
157
151
  };
158
152
  return this._intercept(async () => {
159
- const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
160
- await this._getDocumentFilter(info)
161
- ]);
162
- const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
163
- await this._getArrayFilter(info),
164
- options?.filter
165
- ]);
166
- return this._delete(documentId, id, { ...options, filter, documentFilter });
153
+ const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getDocumentFilter(info)]);
154
+ const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getNestedFilter(info), options?.filter]);
155
+ return this._delete(documentId, nestedId, { ...options, filter, documentFilter });
167
156
  }, info);
168
157
  }
169
- async _delete(documentId, id, options) {
158
+ async _delete(documentId, nestedId, options) {
170
159
  const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
171
- mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, [this.collectionKey]),
172
- options?.documentFilter
160
+ mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, ['_id']),
161
+ options?.documentFilter,
173
162
  ]);
174
- const pullFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
175
- mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, [this.arrayKey]),
176
- options?.filter
177
- ]) || {};
178
- const r = await this.__updateOne(matchFilter, {
179
- $pull: { [this.fieldName]: pullFilter }
163
+ const pullFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([mongo_adapter_js_1.MongoAdapter.prepareKeyValues(nestedId, [this.nestedKey]), options?.filter]) || {};
164
+ const r = await this._dbUpdateOne(matchFilter, {
165
+ $pull: { [this.fieldName]: pullFilter },
180
166
  }, options);
181
167
  return r.modifiedCount ? 1 : 0;
182
168
  }
183
169
  /**
184
170
  * Deletes multiple items from a collection based on the parent ID and optional filter.
185
171
  *
186
- * @param {AnyId} documentId - The ID of the parent document.
187
- * @param {MongoArrayService.DeleteManyOptions<T>} options - Optional options to specify a filter.
172
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
173
+ * @param {MongoNestedService.DeleteManyOptions<T>} [options] - Optional options to specify a filter.
188
174
  * @returns {Promise<number>} - A Promise that resolves to the number of items deleted.
189
175
  */
190
176
  async deleteMany(documentId, options) {
@@ -193,92 +179,92 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
193
179
  method: 'deleteMany',
194
180
  byId: false,
195
181
  documentId,
196
- options
182
+ options,
197
183
  };
198
184
  return this._intercept(async () => {
199
- const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
200
- await this._getDocumentFilter(info)
201
- ]);
202
- const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
203
- await this._getArrayFilter(info),
204
- options?.filter
205
- ]);
185
+ const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getDocumentFilter(info)]);
186
+ const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getNestedFilter(info), options?.filter]);
206
187
  return this._deleteMany(documentId, { ...options, filter, documentFilter });
207
188
  }, info);
208
189
  }
209
190
  async _deleteMany(documentId, options) {
210
191
  const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
211
- mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, [this.collectionKey]),
212
- options?.documentFilter
192
+ mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, ['_id']),
193
+ options?.documentFilter,
213
194
  ]);
214
195
  // Count matching items, we will use this as result
215
196
  const matchCount = await this.count(documentId, options);
216
197
  const pullFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter) || {};
217
- const r = await this.__updateOne(matchFilter, {
218
- $pull: { [this.fieldName]: pullFilter }
198
+ const r = await this._dbUpdateOne(matchFilter, {
199
+ $pull: { [this.fieldName]: pullFilter },
219
200
  }, options);
220
- if (r.modifiedCount)
201
+ if (r.matchedCount)
221
202
  return matchCount;
222
203
  return 0;
223
204
  }
224
205
  /**
225
206
  * Checks if an array element with the given parentId and id exists.
226
207
  *
227
- * @param {AnyId} documentId - The ID of the parent document.
228
- * @param {AnyId} id - The id of the record.
229
- * @param {MongoArrayService.ExistsOptions} [options] - The options for the exists method.
208
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
209
+ * @param {MongoAdapter.AnyId} nestedId - The id of the record.
210
+ * @param {MongoNestedService.ExistsOptions<T>} [options] - The options for the exists method.
230
211
  * @returns {Promise<boolean>} - A promise that resolves to a boolean indicating if the record exists or not.
231
212
  */
232
- async exists(documentId, id, options) {
233
- return !!(await this.findById(documentId, id, { ...options, pick: ['_id'], omit: undefined, include: undefined }));
213
+ async exists(documentId, nestedId, options) {
214
+ return !!(await this.findById(documentId, nestedId, { ...options, projection: ['_id'] }));
215
+ }
216
+ /**
217
+ * Checks if an object with the given arguments exists.
218
+ *
219
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
220
+ * @param {MongoNestedService.ExistsOneOptions} [options] - The options for the query (optional).
221
+ * @return {Promise<boolean>} - A Promise that resolves to a boolean indicating whether the object exists or not.
222
+ */
223
+ async existsOne(documentId, options) {
224
+ return !!(await this.findOne(documentId, { ...options, projection: ['_id'] }));
234
225
  }
235
226
  /**
236
227
  * Finds an element in array field by its parent ID and ID.
237
228
  *
238
- * @param {AnyId} documentId - The ID of the document.
239
- * @param {AnyId} id - The ID of the document.
240
- * @param {MongoArrayService.FindOneOptions} [options] - The optional options for the operation.
229
+ * @param {MongoAdapter.AnyId} documentId - The ID of the document.
230
+ * @param {MongoAdapter.AnyId} nestedId - The ID of the document.
231
+ * @param {MongoNestedService.FindOneOptions<T>} [options] - The optional options for the operation.
241
232
  * @returns {Promise<PartialDTO<T> | undefined>} - A promise that resolves to the found document or undefined if not found.
242
233
  */
243
- async findById(documentId, id, options) {
234
+ async findById(documentId, nestedId, options) {
244
235
  const info = {
245
236
  crud: 'read',
246
237
  method: 'findById',
247
238
  byId: true,
248
239
  documentId,
249
- itemId: id,
250
- options
240
+ nestedId,
241
+ options,
251
242
  };
252
243
  return this._intercept(async () => {
253
- const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
254
- await this._getDocumentFilter(info)
255
- ]);
256
- const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
257
- await this._getArrayFilter(info),
258
- options?.filter
259
- ]);
260
- return this._findById(documentId, id, { ...options, filter, documentFilter });
244
+ const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getDocumentFilter(info)]);
245
+ const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getNestedFilter(info), options?.filter]);
246
+ return this._findById(documentId, nestedId, { ...options, filter, documentFilter });
261
247
  }, info);
262
248
  }
263
- async _findById(documentId, id, options) {
249
+ async _findById(documentId, nestedId, options) {
264
250
  const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
265
- mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, [this.arrayKey]),
266
- options?.filter
251
+ mongo_adapter_js_1.MongoAdapter.prepareKeyValues(nestedId, [this.nestedKey]),
252
+ options?.filter,
267
253
  ]);
268
254
  const rows = await this._findMany(documentId, {
269
255
  ...options,
270
256
  filter,
271
257
  limit: 1,
272
258
  skip: undefined,
273
- sort: undefined
259
+ sort: undefined,
274
260
  });
275
261
  return rows?.[0];
276
262
  }
277
263
  /**
278
264
  * Finds the first array element that matches the given parentId.
279
265
  *
280
- * @param {AnyId} documentId - The ID of the document.
281
- * @param {MongoArrayService.FindOneOptions} [options] - Optional options to customize the query.
266
+ * @param {MongoAdapter.AnyId} documentId - The ID of the document.
267
+ * @param {MongoNestedService.FindOneOptions<T>} [options] - Optional options to customize the query.
282
268
  * @returns {Promise<PartialDTO<T> | undefined>} A promise that resolves to the first matching document, or `undefined` if no match is found.
283
269
  */
284
270
  async findOne(documentId, options) {
@@ -287,31 +273,26 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
287
273
  method: 'findOne',
288
274
  byId: false,
289
275
  documentId,
290
- options
276
+ options,
291
277
  };
292
278
  return this._intercept(async () => {
293
- const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
294
- await this._getDocumentFilter(info)
295
- ]);
296
- const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
297
- await this._getArrayFilter(info),
298
- options?.filter
299
- ]);
279
+ const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getDocumentFilter(info)]);
280
+ const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getNestedFilter(info), options?.filter]);
300
281
  return this._findOne(documentId, { ...options, filter, documentFilter });
301
282
  }, info);
302
283
  }
303
284
  async _findOne(documentId, options) {
304
285
  const rows = await this._findMany(documentId, {
305
286
  ...options,
306
- limit: 1
287
+ limit: 1,
307
288
  });
308
289
  return rows?.[0];
309
290
  }
310
291
  /**
311
292
  * Finds multiple elements in an array field.
312
293
  *
313
- * @param {AnyId} documentId - The ID of the parent document.
314
- * @param {MongoArrayService.FindManyOptions<T>} [options] - The options for finding the documents.
294
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
295
+ * @param {MongoNestedService.FindManyOptions<T>} [options] - The options for finding the documents.
315
296
  * @returns {Promise<PartialDTO<T>[]>} - The found documents.
316
297
  */
317
298
  async findMany(documentId, options) {
@@ -320,43 +301,109 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
320
301
  method: 'findMany',
321
302
  byId: false,
322
303
  documentId,
323
- options
304
+ options,
324
305
  };
325
306
  return this._intercept(async () => {
326
307
  const documentFilter = await this._getDocumentFilter(args);
327
- const arrayFilter = await this._getArrayFilter(args);
328
- return this._findMany(documentId, { ...options, documentFilter, arrayFilter });
308
+ const nestedFilter = await this._getNestedFilter(args);
309
+ return this._findMany(documentId, {
310
+ ...options,
311
+ documentFilter,
312
+ nestedFilter,
313
+ limit: options?.limit || this.defaultLimit,
314
+ });
329
315
  }, args);
330
316
  }
331
317
  async _findMany(documentId, options) {
332
318
  const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
333
- mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, [this.collectionKey]),
334
- options.documentFilter
319
+ mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, ['_id']),
320
+ options.documentFilter,
335
321
  ]);
336
322
  const mongoOptions = {
337
- ...(0, lodash_omit_1.default)(options, ['pick', 'include', 'omit', 'sort', 'skip', 'limit', 'filter', 'count'])
323
+ ...(0, lodash_omit_1.default)(options, ['documentFilter', 'nestedFilter', 'projection', 'sort', 'skip', 'limit', 'filter', 'count']),
338
324
  };
339
325
  const limit = options?.limit || this.defaultLimit;
340
326
  const stages = [
341
327
  { $match: matchFilter },
342
- { $unwind: { path: "$" + this.fieldName } },
343
- { $replaceRoot: { newRoot: "$" + this.fieldName } }
328
+ { $unwind: { path: '$' + this.fieldName } },
329
+ { $replaceRoot: { newRoot: '$' + this.fieldName } },
344
330
  ];
345
- let dataStages = stages;
346
- if (options?.count) {
347
- dataStages = [];
348
- stages.push({
331
+ if (options?.filter || options.nestedFilter) {
332
+ const optionsFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([options?.filter, options.nestedFilter]);
333
+ stages.push({ $match: optionsFilter });
334
+ }
335
+ if (options?.skip)
336
+ stages.push({ $skip: options.skip });
337
+ if (options?.sort) {
338
+ const sort = mongo_adapter_js_1.MongoAdapter.prepareSort(options.sort);
339
+ if (sort)
340
+ stages.push({ $sort: sort });
341
+ }
342
+ stages.push({ $limit: limit });
343
+ const dataType = this.getDataType();
344
+ const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options?.projection);
345
+ if (projection)
346
+ stages.push({ $project: projection });
347
+ const decode = this.getDecoder();
348
+ const cursor = await this._dbAggregate(stages, mongoOptions);
349
+ try {
350
+ const out = await (await cursor.toArray()).map((r) => decode(r));
351
+ return out;
352
+ }
353
+ finally {
354
+ if (!cursor.closed)
355
+ await cursor.close();
356
+ }
357
+ }
358
+ /**
359
+ * Finds multiple elements in an array field.
360
+ *
361
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
362
+ * @param {MongoNestedService.FindManyOptions<T>} [options] - The options for finding the documents.
363
+ * @returns {Promise<PartialDTO<T>[]>} - The found documents.
364
+ */
365
+ async findManyWithCount(documentId, options) {
366
+ const args = {
367
+ crud: 'read',
368
+ method: 'findMany',
369
+ byId: false,
370
+ documentId,
371
+ options,
372
+ };
373
+ return this._intercept(async () => {
374
+ const documentFilter = await this._getDocumentFilter(args);
375
+ const nestedFilter = await this._getNestedFilter(args);
376
+ return this._findManyWithCount(documentId, {
377
+ ...options,
378
+ documentFilter,
379
+ nestedFilter,
380
+ limit: options?.limit || this.defaultLimit,
381
+ });
382
+ }, args);
383
+ }
384
+ async _findManyWithCount(documentId, options) {
385
+ const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
386
+ mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, ['_id']),
387
+ options.documentFilter,
388
+ ]);
389
+ const mongoOptions = {
390
+ ...(0, lodash_omit_1.default)(options, ['pick', 'include', 'omit', 'sort', 'skip', 'limit', 'filter', 'count']),
391
+ };
392
+ const limit = options?.limit || this.defaultLimit;
393
+ const dataStages = [];
394
+ const stages = [
395
+ { $match: matchFilter },
396
+ { $unwind: { path: '$' + this.fieldName } },
397
+ { $replaceRoot: { newRoot: '$' + this.fieldName } },
398
+ {
349
399
  $facet: {
350
400
  data: dataStages,
351
- count: [{ $count: 'totalMatches' }]
352
- }
353
- });
354
- }
355
- if (options?.filter || options.arrayFilter) {
356
- const optionsFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
357
- options?.filter,
358
- options.arrayFilter
359
- ]);
401
+ count: [{ $count: 'totalMatches' }],
402
+ },
403
+ },
404
+ ];
405
+ if (options?.filter || options.nestedFilter) {
406
+ const optionsFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([options?.filter, options.nestedFilter]);
360
407
  dataStages.push({ $match: optionsFilter });
361
408
  }
362
409
  if (options?.skip)
@@ -368,21 +415,19 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
368
415
  }
369
416
  dataStages.push({ $limit: limit });
370
417
  const dataType = this.getDataType();
371
- const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options);
418
+ const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options?.projection);
372
419
  if (projection)
373
420
  dataStages.push({ $project: projection });
374
421
  const decode = this.getDecoder();
375
- const cursor = await this.__aggregate(stages, {
376
- ...mongoOptions
422
+ const cursor = await this._dbAggregate(stages, {
423
+ ...mongoOptions,
377
424
  });
378
425
  try {
379
- if (options?.count) {
380
- const facetResult = await cursor.toArray();
381
- this.context.response.totalMatches = facetResult[0].count[0].totalMatches || 0;
382
- return facetResult[0].data.map((r) => decode(r, { coerce: true }));
383
- }
384
- else
385
- return await cursor.toArray();
426
+ const facetResult = await cursor.toArray();
427
+ return {
428
+ count: facetResult[0].count[0].totalMatches || 0,
429
+ items: facetResult[0].data.map((r) => decode(r)),
430
+ };
386
431
  }
387
432
  finally {
388
433
  if (!cursor.closed)
@@ -392,68 +437,66 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
392
437
  /**
393
438
  * Retrieves a specific item from the array of a document.
394
439
  *
395
- * @param {AnyId} documentId - The ID of the document.
396
- * @param {AnyId} id - The ID of the item.
397
- * @param {MongoArrayService.FindOneOptions<T>} [options] - The options for finding the item.
440
+ * @param {MongoAdapter.AnyId} documentId - The ID of the document.
441
+ * @param {MongoAdapter.AnyId} nestedId - The ID of the item.
442
+ * @param {MongoNestedService.FindOneOptions<T>} [options] - The options for finding the item.
398
443
  * @returns {Promise<PartialDTO<T>>} - The item found.
399
444
  * @throws {ResourceNotAvailableError} - If the item is not found.
400
445
  */
401
- async get(documentId, id, options) {
402
- const out = await this.findById(documentId, id, options);
446
+ async get(documentId, nestedId, options) {
447
+ const out = await this.findById(documentId, nestedId, options);
403
448
  if (!out)
404
- throw new common_1.ResourceNotAvailableError(this.getResourceName() + '.' + this.arrayKey, documentId + '/' + id);
449
+ throw new common_1.ResourceNotAvailableError(this.getResourceName() + '.' + this.nestedKey, documentId + '/' + nestedId);
405
450
  return out;
406
451
  }
407
452
  /**
408
453
  * Updates an array element with new data and returns the updated element
409
454
  *
410
455
  * @param {AnyId} documentId - The ID of the document to update.
411
- * @param {AnyId} id - The ID of the item to update within the document.
456
+ * @param {AnyId} nestedId - The ID of the item to update within the document.
412
457
  * @param {PatchDTO<T>} input - The new data to update the item with.
413
- * @param {MongoArrayService.UpdateOptions<T>} [options] - Additional update options.
458
+ * @param {MongoNestedService.UpdateOptions<T>} [options] - Additional update options.
414
459
  * @returns {Promise<PartialDTO<T> | undefined>} The updated item or undefined if it does not exist.
415
460
  * @throws {Error} If an error occurs while updating the item.
416
461
  */
417
- async update(documentId, id, input, options) {
418
- const r = await this.updateOnly(documentId, id, input, options);
462
+ async update(documentId, nestedId, input, options) {
463
+ const r = await this.updateOnly(documentId, nestedId, input, options);
419
464
  if (!r)
420
465
  return;
421
- const out = await this._findById(documentId, id, options);
466
+ const out = await this._findById(documentId, nestedId, {
467
+ ...options,
468
+ sort: undefined,
469
+ });
422
470
  if (out)
423
471
  return out;
424
- throw new common_1.ResourceNotAvailableError(this.getResourceName() + '.' + this.arrayKey, documentId + '/' + id);
472
+ throw new common_1.ResourceNotAvailableError(this.getResourceName() + '.' + this.nestedKey, documentId + '/' + nestedId);
425
473
  }
426
474
  /**
427
475
  * Update an array element with new data. Returns 1 if document updated 0 otherwise.
428
476
  *
429
- * @param {AnyId} documentId - The ID of the parent document.
430
- * @param {AnyId} id - The ID of the document to update.
477
+ * @param {MongoAdapter.AnyId} documentId - The ID of the parent document.
478
+ * @param {MongoAdapter.AnyId} nestedId - The ID of the document to update.
431
479
  * @param {PatchDTO<T>} input - The partial input object containing the fields to update.
432
- * @param {MongoArrayService.UpdateOptions<T>} [options] - Optional update options.
480
+ * @param {MongoNestedService.UpdateOptions<T>} [options] - Optional update options.
433
481
  * @returns {Promise<number>} - A promise that resolves to the number of elements updated.
434
482
  */
435
- async updateOnly(documentId, id, input, options) {
483
+ async updateOnly(documentId, nestedId, input, options) {
436
484
  const info = {
437
485
  crud: 'update',
438
486
  method: 'update',
439
487
  byId: true,
440
488
  documentId,
441
- itemId: id,
442
- options
489
+ nestedId,
490
+ options,
443
491
  };
444
492
  return this._intercept(async () => {
445
- const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
446
- await this._getDocumentFilter(info)
447
- ]);
448
- const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
449
- await this._getArrayFilter(info),
450
- options?.filter
451
- ]);
452
- return this._updateOnly(documentId, id, input, { ...options, filter, documentFilter });
493
+ const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getDocumentFilter(info)]);
494
+ const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getNestedFilter(info), options?.filter]);
495
+ return this._updateOnly(documentId, nestedId, input, { ...options, filter, documentFilter });
453
496
  }, info);
454
497
  }
455
- async _updateOnly(documentId, id, input, options) {
456
- let filter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, [this.arrayKey]);
498
+ async _updateOnly(documentId, nestedId, input, options) {
499
+ let filter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(nestedId, [this.nestedKey]);
457
500
  if (options?.filter)
458
501
  filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([filter, options?.filter]);
459
502
  return await this._updateMany(documentId, input, { ...options, filter });
@@ -461,9 +504,9 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
461
504
  /**
462
505
  * Updates multiple array elements in document
463
506
  *
464
- * @param {AnyId} documentId - The ID of the document to update.
507
+ * @param {MongoAdapter.AnyId} documentId - The ID of the document to update.
465
508
  * @param {PatchDTO<T>} input - The updated data for the document(s).
466
- * @param {MongoArrayService.UpdateManyOptions<T>} [options] - Additional options for the update operation.
509
+ * @param {MongoNestedService.UpdateManyOptions<T>} [options] - Additional options for the update operation.
467
510
  * @returns {Promise<number>} - A promise that resolves to the number of documents updated.
468
511
  */
469
512
  async updateMany(documentId, input, options) {
@@ -473,16 +516,11 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
473
516
  documentId,
474
517
  byId: false,
475
518
  input,
476
- options
519
+ options,
477
520
  };
478
521
  return this._intercept(async () => {
479
- const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
480
- await this._getDocumentFilter(info)
481
- ]);
482
- const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
483
- await this._getArrayFilter(info),
484
- options?.filter
485
- ]);
522
+ const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getDocumentFilter(info)]);
523
+ const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([await this._getNestedFilter(info), options?.filter]);
486
524
  return this._updateMany(documentId, input, { ...options, filter, documentFilter });
487
525
  }, info);
488
526
  }
@@ -492,9 +530,9 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
492
530
  if (!Object.keys(doc).length)
493
531
  return 0;
494
532
  const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
495
- mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, [this.collectionKey]),
533
+ mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, ['_id']),
496
534
  options?.documentFilter,
497
- { [this.fieldName]: { $exists: true } }
535
+ { [this.fieldName]: { $exists: true } },
498
536
  ]);
499
537
  if (options?.filter) {
500
538
  const elemMatch = mongo_adapter_js_1.MongoAdapter.prepareFilter([options?.filter], { fieldPrefix: 'elem.' });
@@ -502,15 +540,12 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
502
540
  options.arrayFilters = [elemMatch];
503
541
  }
504
542
  const update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc, {
505
- fieldPrefix: this.fieldName + (options?.filter ? '.$[elem].' : '.$[].')
543
+ fieldPrefix: this.fieldName + (options?.filter ? '.$[elem].' : '.$[].'),
506
544
  });
507
- const r = await this.__updateOne(matchFilter, update, options);
508
- if (!options?.count)
509
- return r.modifiedCount;
510
- return r.modifiedCount
511
- // Count matching items that fits filter criteria
512
- ? await this._count(documentId, options)
513
- : 0;
545
+ const r = await this._dbUpdateOne(matchFilter, update, options);
546
+ if (options?.count)
547
+ return await this._count(documentId, options);
548
+ return r.modifiedCount || 0;
514
549
  }
515
550
  /**
516
551
  * Retrieves the data type of the array field
@@ -519,34 +554,11 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
519
554
  * @throws {NotAcceptableError} If the data type is not a ComplexType.
520
555
  */
521
556
  getDataType() {
522
- const t = super.getDataType()
523
- .getField(this.fieldName).type;
557
+ const t = super.getDataType().getField(this.fieldName).type;
524
558
  if (!(t instanceof common_1.ComplexType))
525
559
  throw new common_1.NotAcceptableError(`Data type "${t.name}" is not a ComplexType`);
526
560
  return t;
527
561
  }
528
- /**
529
- * Generates an ID.
530
- *
531
- * @protected
532
- * @returns {AnyId} The generated ID.
533
- */
534
- _generateId() {
535
- return typeof this.$idGenerator === 'function' ?
536
- this.$idGenerator(this) : new mongodb_1.ObjectId();
537
- }
538
- /**
539
- * Retrieves the common filter used for querying documents.
540
- * This method is mostly used for security issues like securing multi-tenant applications.
541
- *
542
- * @protected
543
- * @returns {FilterInput | Promise<FilterInput> | undefined} The common filter or a Promise
544
- * that resolves to the common filter, or undefined if not available.
545
- */
546
- _getDocumentFilter(args) {
547
- return typeof this.$documentFilter === 'function' ?
548
- this.$documentFilter(args, this) : this.$documentFilter;
549
- }
550
562
  /**
551
563
  * Retrieves the common filter used for querying array elements.
552
564
  * This method is mostly used for security issues like securing multi-tenant applications.
@@ -555,14 +567,8 @@ class MongoArrayService extends mongo_service_js_1.MongoService {
555
567
  * @returns {FilterInput | Promise<FilterInput> | undefined} The common filter or a Promise
556
568
  * that resolves to the common filter, or undefined if not available.
557
569
  */
558
- _getArrayFilter(args) {
559
- return typeof this.$arrayFilter === 'function' ?
560
- this.$arrayFilter(args, this) : this.$arrayFilter;
561
- }
562
- async _intercept(callback, args) {
563
- if (this.$interceptor)
564
- return this.$interceptor(callback, args, this);
565
- return callback();
570
+ _getNestedFilter(args) {
571
+ return typeof this.$nestedFilter === 'function' ? this.$nestedFilter(args, this) : this.$nestedFilter;
566
572
  }
567
573
  }
568
- exports.MongoArrayService = MongoArrayService;
574
+ exports.MongoNestedService = MongoNestedService;