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