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