@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.
- package/cjs/adapter-utils/prepare-filter.js +20 -28
- package/cjs/adapter-utils/prepare-projection.js +40 -39
- package/cjs/index.js +1 -2
- package/cjs/mongo-adapter.js +66 -1
- package/cjs/mongo-collection-service.js +72 -313
- package/cjs/mongo-entity-service.js +329 -0
- package/cjs/{mongo-array-service.js → mongo-nested-service.js} +231 -225
- package/cjs/mongo-service.js +124 -183
- package/cjs/mongo-singleton-service.js +28 -124
- package/esm/adapter-utils/prepare-filter.js +20 -28
- package/esm/adapter-utils/prepare-projection.js +39 -38
- package/esm/index.js +1 -2
- package/esm/mongo-adapter.js +66 -1
- package/esm/mongo-collection-service.js +73 -313
- package/esm/mongo-entity-service.js +324 -0
- package/esm/mongo-nested-service.js +569 -0
- package/esm/mongo-service.js +125 -184
- package/esm/mongo-singleton-service.js +29 -125
- package/package.json +9 -8
- package/types/adapter-utils/prepare-filter.d.ts +2 -3
- package/types/adapter-utils/prepare-projection.d.ts +4 -13
- package/types/index.d.ts +1 -2
- package/types/mongo-adapter.d.ts +14 -1
- package/types/mongo-collection-service.d.ts +88 -251
- package/types/mongo-entity-service.d.ts +149 -0
- package/types/mongo-nested-service.d.ts +258 -0
- package/types/mongo-service.d.ts +208 -82
- package/types/mongo-singleton-service.d.ts +39 -148
- package/cjs/types.js +0 -2
- package/esm/mongo-array-service.js +0 -563
- package/esm/types.js +0 -1
- package/types/mongo-array-service.d.ts +0 -409
- package/types/types.d.ts +0 -3
|
@@ -1,86 +1,86 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
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
|
|
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 {
|
|
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
|
|
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 {
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
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
|
|
70
|
+
const doc = encode(input);
|
|
71
71
|
doc._id = doc._id || this._generateId();
|
|
72
|
-
const docFilter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, [
|
|
73
|
-
const r = await this.
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
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, [
|
|
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:
|
|
125
|
-
{ $replaceRoot: { newRoot:
|
|
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.
|
|
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}
|
|
146
|
-
* @param {
|
|
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,
|
|
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
|
-
|
|
156
|
-
options
|
|
149
|
+
nestedId,
|
|
150
|
+
options,
|
|
157
151
|
};
|
|
158
152
|
return this._intercept(async () => {
|
|
159
|
-
const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
|
|
160
|
-
|
|
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,
|
|
158
|
+
async _delete(documentId, nestedId, options) {
|
|
170
159
|
const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
|
|
171
|
-
mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, [
|
|
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
|
-
|
|
176
|
-
|
|
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 {
|
|
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
|
-
|
|
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, [
|
|
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.
|
|
218
|
-
$pull: { [this.fieldName]: pullFilter }
|
|
198
|
+
const r = await this._dbUpdateOne(matchFilter, {
|
|
199
|
+
$pull: { [this.fieldName]: pullFilter },
|
|
219
200
|
}, options);
|
|
220
|
-
if (r.
|
|
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}
|
|
229
|
-
* @param {
|
|
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,
|
|
233
|
-
return !!(await this.findById(documentId,
|
|
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}
|
|
240
|
-
* @param {
|
|
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,
|
|
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
|
-
|
|
250
|
-
options
|
|
240
|
+
nestedId,
|
|
241
|
+
options,
|
|
251
242
|
};
|
|
252
243
|
return this._intercept(async () => {
|
|
253
|
-
const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
|
|
254
|
-
|
|
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,
|
|
249
|
+
async _findById(documentId, nestedId, options) {
|
|
264
250
|
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
|
|
265
|
-
mongo_adapter_js_1.MongoAdapter.prepareKeyValues(
|
|
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 {
|
|
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
|
-
|
|
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 {
|
|
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
|
|
328
|
-
return this._findMany(documentId, {
|
|
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, [
|
|
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, ['
|
|
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:
|
|
343
|
-
{ $replaceRoot: { newRoot:
|
|
328
|
+
{ $unwind: { path: '$' + this.fieldName } },
|
|
329
|
+
{ $replaceRoot: { newRoot: '$' + this.fieldName } },
|
|
344
330
|
];
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
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.
|
|
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.
|
|
376
|
-
...mongoOptions
|
|
422
|
+
const cursor = await this._dbAggregate(stages, {
|
|
423
|
+
...mongoOptions,
|
|
377
424
|
});
|
|
378
425
|
try {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
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}
|
|
397
|
-
* @param {
|
|
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,
|
|
402
|
-
const out = await this.findById(documentId,
|
|
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.
|
|
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}
|
|
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 {
|
|
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,
|
|
418
|
-
const r = await this.updateOnly(documentId,
|
|
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,
|
|
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.
|
|
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}
|
|
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 {
|
|
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,
|
|
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
|
-
|
|
442
|
-
options
|
|
489
|
+
nestedId,
|
|
490
|
+
options,
|
|
443
491
|
};
|
|
444
492
|
return this._intercept(async () => {
|
|
445
|
-
const documentFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
|
|
446
|
-
|
|
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,
|
|
456
|
-
let filter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(
|
|
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 {
|
|
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
|
-
|
|
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, [
|
|
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.
|
|
508
|
-
if (
|
|
509
|
-
return
|
|
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
|
-
|
|
559
|
-
return typeof this.$
|
|
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.
|
|
574
|
+
exports.MongoNestedService = MongoNestedService;
|