@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
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MongoEntityService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const lodash_omit_1 = tslib_1.__importDefault(require("lodash.omit"));
|
|
6
|
+
const assert = tslib_1.__importStar(require("node:assert"));
|
|
7
|
+
const common_1 = require("@opra/common");
|
|
8
|
+
const mongo_adapter_js_1 = require("./mongo-adapter.js");
|
|
9
|
+
const mongo_service_js_1 = require("./mongo-service.js");
|
|
10
|
+
/**
|
|
11
|
+
* @class MongoEntityService
|
|
12
|
+
* @template T - The type of the documents in the collection.
|
|
13
|
+
*/
|
|
14
|
+
class MongoEntityService 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 {MongoEntityService.Options} [options] - The options for the array service.
|
|
20
|
+
* @constructor
|
|
21
|
+
*/
|
|
22
|
+
constructor(dataType, options) {
|
|
23
|
+
super(dataType, options);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Creates a new document in the MongoDB collection.
|
|
27
|
+
*
|
|
28
|
+
* @param {PartialDTO<T>} input
|
|
29
|
+
* @param {MongoEntityService.CreateOptions} options
|
|
30
|
+
* @protected
|
|
31
|
+
*/
|
|
32
|
+
async _create(input, options) {
|
|
33
|
+
const encode = this.getEncoder('create');
|
|
34
|
+
const doc = encode(input);
|
|
35
|
+
assert.ok(doc._id, 'You must provide the "_id" field');
|
|
36
|
+
const r = await this._dbInsertOne(doc, options);
|
|
37
|
+
if (r.insertedId) {
|
|
38
|
+
if (!options)
|
|
39
|
+
return doc;
|
|
40
|
+
const out = await this._findById(doc._id, (0, lodash_omit_1.default)(options, 'filter'));
|
|
41
|
+
if (out)
|
|
42
|
+
return out;
|
|
43
|
+
}
|
|
44
|
+
/* istanbul ignore next */
|
|
45
|
+
throw new common_1.InternalServerError(`Unknown error while creating document for "${this.getResourceName()}"`);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Returns the count of documents in the collection based on the provided options.
|
|
49
|
+
*
|
|
50
|
+
* @param {MongoEntityService.CountOptions<T>} options - The options for the count operation.
|
|
51
|
+
* @return {Promise<number>} - A promise that resolves to the count of documents in the collection.
|
|
52
|
+
*/
|
|
53
|
+
async _count(options) {
|
|
54
|
+
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
|
|
55
|
+
return this._dbCountDocuments(filter, (0, lodash_omit_1.default)(options, 'filter'));
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Deletes a document from the collection.
|
|
59
|
+
*
|
|
60
|
+
* @param {MongoAdapter.AnyId} id - The ID of the document to delete.
|
|
61
|
+
* @param {MongoEntityService.DeleteOptions<T>} [options] - Optional delete options.
|
|
62
|
+
* @return {Promise<number>} - A Promise that resolves to the number of documents deleted.
|
|
63
|
+
*/
|
|
64
|
+
async _delete(id, options) {
|
|
65
|
+
assert.ok(id, 'You must provide an id');
|
|
66
|
+
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, ['_id']), options?.filter]);
|
|
67
|
+
const r = await this._dbDeleteOne(filter, options);
|
|
68
|
+
return r.deletedCount;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Deletes multiple documents from the collection that meet the specified filter criteria.
|
|
72
|
+
*
|
|
73
|
+
* @param {MongoEntityService.DeleteManyOptions<T>} options - The options for the delete operation.
|
|
74
|
+
* @return {Promise<number>} - A promise that resolves to the number of documents deleted.
|
|
75
|
+
*/
|
|
76
|
+
async _deleteMany(options) {
|
|
77
|
+
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
|
|
78
|
+
const r = await this._dbDeleteMany(filter, (0, lodash_omit_1.default)(options, 'filter'));
|
|
79
|
+
return r.deletedCount;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* The distinct command returns a list of distinct values for the given key across a collection.
|
|
83
|
+
* @param {string} field
|
|
84
|
+
* @param {MongoEntityService.DistinctOptions<T>} options
|
|
85
|
+
* @protected
|
|
86
|
+
*/
|
|
87
|
+
async _distinct(field, options) {
|
|
88
|
+
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
|
|
89
|
+
return await this._dbDistinct(field, filter, (0, lodash_omit_1.default)(options, 'filter'));
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Finds a document by its ID.
|
|
93
|
+
*
|
|
94
|
+
* @param {MongoAdapter.AnyId} id - The ID of the document.
|
|
95
|
+
* @param {MongoEntityService.FindOneOptions<T>} [options] - The options for the find query.
|
|
96
|
+
* @return {Promise<PartialDTO<T | undefined>>} - A promise resolving to the found document, or undefined if not found.
|
|
97
|
+
*/
|
|
98
|
+
async _findById(id, options) {
|
|
99
|
+
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, ['_id']), options?.filter]);
|
|
100
|
+
const mongoOptions = {
|
|
101
|
+
...options,
|
|
102
|
+
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.getDataType(), options?.projection),
|
|
103
|
+
limit: undefined,
|
|
104
|
+
skip: undefined,
|
|
105
|
+
sort: undefined,
|
|
106
|
+
};
|
|
107
|
+
const decode = this.getDecoder();
|
|
108
|
+
const out = await this._dbFindOne(filter, mongoOptions);
|
|
109
|
+
return out ? decode(out) : undefined;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Finds a document in the collection that matches the specified options.
|
|
113
|
+
*
|
|
114
|
+
* @param {MongoEntityService.FindOneOptions} [options] - The options for the query.
|
|
115
|
+
* @return {Promise<PartialDTO<T> | undefined>} A promise that resolves with the found document or undefined if no document is found.
|
|
116
|
+
*/
|
|
117
|
+
async _findOne(options) {
|
|
118
|
+
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
|
|
119
|
+
const mongoOptions = {
|
|
120
|
+
...(0, lodash_omit_1.default)(options, 'filter'),
|
|
121
|
+
sort: options?.sort ? mongo_adapter_js_1.MongoAdapter.prepareSort(options.sort) : undefined,
|
|
122
|
+
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.getDataType(), options?.projection),
|
|
123
|
+
limit: undefined,
|
|
124
|
+
};
|
|
125
|
+
const decode = this.getDecoder();
|
|
126
|
+
const out = await this._dbFindOne(filter, mongoOptions);
|
|
127
|
+
return out ? decode(out, { coerce: true }) : undefined;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Finds multiple documents in the MongoDB collection.
|
|
131
|
+
*
|
|
132
|
+
* @param {MongoEntityService.FindManyOptions<T>} [options] - The options for the find operation.
|
|
133
|
+
* @return A Promise that resolves to an array of partial outputs of type T.
|
|
134
|
+
*/
|
|
135
|
+
async _findMany(options) {
|
|
136
|
+
const mongoOptions = {
|
|
137
|
+
...(0, lodash_omit_1.default)(options, ['projection', 'sort', 'skip', 'limit', 'filter']),
|
|
138
|
+
};
|
|
139
|
+
const limit = options?.limit || 10;
|
|
140
|
+
const stages = [];
|
|
141
|
+
let filter;
|
|
142
|
+
if (options?.filter)
|
|
143
|
+
filter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
|
|
144
|
+
if (filter)
|
|
145
|
+
stages.push({ $match: filter });
|
|
146
|
+
if (options?.skip)
|
|
147
|
+
stages.push({ $skip: options.skip });
|
|
148
|
+
if (options?.sort) {
|
|
149
|
+
const sort = mongo_adapter_js_1.MongoAdapter.prepareSort(options.sort);
|
|
150
|
+
if (sort)
|
|
151
|
+
stages.push({ $sort: sort });
|
|
152
|
+
}
|
|
153
|
+
stages.push({ $limit: limit });
|
|
154
|
+
const dataType = this.getDataType();
|
|
155
|
+
const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
156
|
+
if (projection)
|
|
157
|
+
stages.push({ $project: projection });
|
|
158
|
+
const decode = this.getDecoder();
|
|
159
|
+
const cursor = await this._dbAggregate(stages, mongoOptions);
|
|
160
|
+
/** Execute db command */
|
|
161
|
+
try {
|
|
162
|
+
/** Fetch the cursor and decode the result objects */
|
|
163
|
+
return (await cursor.toArray()).map((r) => decode(r));
|
|
164
|
+
}
|
|
165
|
+
finally {
|
|
166
|
+
if (!cursor.closed)
|
|
167
|
+
await cursor.close();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Finds multiple documents in the collection and returns both records (max limit)
|
|
172
|
+
* and total count that matched the given criteria
|
|
173
|
+
*
|
|
174
|
+
* @param {MongoEntityService.FindManyOptions<T>} [options] - The options for the find operation.
|
|
175
|
+
* @return A Promise that resolves to an array of partial outputs of type T.
|
|
176
|
+
*/
|
|
177
|
+
async _findManyWithCount(options) {
|
|
178
|
+
const mongoOptions = {
|
|
179
|
+
...(0, lodash_omit_1.default)(options, ['projection', 'sort', 'skip', 'limit', 'filter']),
|
|
180
|
+
};
|
|
181
|
+
const limit = options?.limit || 10;
|
|
182
|
+
let filter;
|
|
183
|
+
if (options?.filter)
|
|
184
|
+
filter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
|
|
185
|
+
const dataStages = [];
|
|
186
|
+
const countStages = [];
|
|
187
|
+
if (filter)
|
|
188
|
+
countStages.push({ $match: filter });
|
|
189
|
+
countStages.push({ $count: 'totalMatches' });
|
|
190
|
+
const stages = [
|
|
191
|
+
{
|
|
192
|
+
$facet: {
|
|
193
|
+
data: dataStages,
|
|
194
|
+
count: countStages,
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
];
|
|
198
|
+
if (filter)
|
|
199
|
+
dataStages.push({ $match: filter });
|
|
200
|
+
if (options?.skip)
|
|
201
|
+
dataStages.push({ $skip: options.skip });
|
|
202
|
+
if (options?.sort) {
|
|
203
|
+
const sort = mongo_adapter_js_1.MongoAdapter.prepareSort(options.sort);
|
|
204
|
+
if (sort)
|
|
205
|
+
dataStages.push({ $sort: sort });
|
|
206
|
+
}
|
|
207
|
+
dataStages.push({ $limit: limit });
|
|
208
|
+
const dataType = this.getDataType();
|
|
209
|
+
const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
210
|
+
if (projection)
|
|
211
|
+
dataStages.push({ $project: projection });
|
|
212
|
+
const decode = this.getDecoder();
|
|
213
|
+
/** Execute db command */
|
|
214
|
+
const cursor = await this._dbAggregate(stages, mongoOptions);
|
|
215
|
+
try {
|
|
216
|
+
/** Fetch the cursor and decode the result objects */
|
|
217
|
+
const facetResult = await cursor.toArray();
|
|
218
|
+
return {
|
|
219
|
+
count: facetResult[0].count[0]?.totalMatches || 0,
|
|
220
|
+
items: facetResult[0].data?.map((r) => decode(r)),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
finally {
|
|
224
|
+
if (!cursor.closed)
|
|
225
|
+
await cursor.close();
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Updates a document with the given id in the collection.
|
|
230
|
+
*
|
|
231
|
+
* @param {AnyId} id - The id of the document to update.
|
|
232
|
+
* @param {PatchDTO<T>|UpdateFilter<T>} input - The partial input object containing the fields to update.
|
|
233
|
+
* @param {MongoEntityService.UpdateOptions<T>} [options] - The options for the update operation.
|
|
234
|
+
* @returns {Promise<PartialDTO<T> | undefined>} A promise that resolves to the updated document or
|
|
235
|
+
* undefined if the document was not found.
|
|
236
|
+
*/
|
|
237
|
+
async _update(id, input, options) {
|
|
238
|
+
const isUpdateFilter = Array.isArray(input) || !!Object.keys(input).find(x => x.startsWith('$'));
|
|
239
|
+
const isDocument = !Array.isArray(input) && !!Object.keys(input).find(x => !x.startsWith('$'));
|
|
240
|
+
if (isUpdateFilter && isDocument)
|
|
241
|
+
throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
|
|
242
|
+
let update;
|
|
243
|
+
if (isDocument) {
|
|
244
|
+
const encode = this.getEncoder('update');
|
|
245
|
+
const doc = encode(input, { coerce: true });
|
|
246
|
+
delete doc._id;
|
|
247
|
+
update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc);
|
|
248
|
+
update.$set = update.$set || {};
|
|
249
|
+
}
|
|
250
|
+
else
|
|
251
|
+
update = input;
|
|
252
|
+
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, ['_id']), options?.filter]);
|
|
253
|
+
const mongoOptions = {
|
|
254
|
+
...options,
|
|
255
|
+
includeResultMetadata: false,
|
|
256
|
+
upsert: undefined,
|
|
257
|
+
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.getDataType(), options?.projection),
|
|
258
|
+
};
|
|
259
|
+
const decode = this.getDecoder();
|
|
260
|
+
const out = await this._dbFindOneAndUpdate(filter, update, mongoOptions);
|
|
261
|
+
return out ? decode(out, { coerce: true }) : undefined;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Updates a document in the collection with the specified ID.
|
|
265
|
+
*
|
|
266
|
+
* @param {MongoAdapter.AnyId} id - The ID of the document to update.
|
|
267
|
+
* @param {PatchDTO<T>|UpdateFilter<T>} input - The partial input data to update the document with.
|
|
268
|
+
* @param {MongoEntityService.UpdateOptions<T>} [options] - The options for updating the document.
|
|
269
|
+
* @returns {Promise<number>} - A promise that resolves to the number of documents modified.
|
|
270
|
+
*/
|
|
271
|
+
async _updateOnly(id, input, options) {
|
|
272
|
+
const isUpdateFilter = Array.isArray(input) || !!Object.keys(input).find(x => x.startsWith('$'));
|
|
273
|
+
const isDocument = !Array.isArray(input) && !!Object.keys(input).find(x => !x.startsWith('$'));
|
|
274
|
+
if (isUpdateFilter && isDocument)
|
|
275
|
+
throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
|
|
276
|
+
let update;
|
|
277
|
+
if (isDocument) {
|
|
278
|
+
const encode = this.getEncoder('update');
|
|
279
|
+
const doc = encode(input, { coerce: true });
|
|
280
|
+
delete doc._id;
|
|
281
|
+
update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc);
|
|
282
|
+
if (!Object.keys(doc).length)
|
|
283
|
+
return 0;
|
|
284
|
+
}
|
|
285
|
+
else
|
|
286
|
+
update = input;
|
|
287
|
+
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, ['_id']), options?.filter]);
|
|
288
|
+
const mongoOptions = {
|
|
289
|
+
...options,
|
|
290
|
+
includeResultMetadata: false,
|
|
291
|
+
upsert: undefined,
|
|
292
|
+
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.getDataType(), options?.projection),
|
|
293
|
+
};
|
|
294
|
+
const out = await this._dbUpdateOne(filter, update, mongoOptions);
|
|
295
|
+
return out.matchedCount;
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Updates multiple documents in the collection based on the specified input and options.
|
|
299
|
+
*
|
|
300
|
+
* @param {PatchDTO<T>|UpdateFilter<T>} input - The partial input to update the documents with.
|
|
301
|
+
* @param {MongoEntityService.UpdateManyOptions<T>} [options] - The options for updating the documents.
|
|
302
|
+
* @return {Promise<number>} - A promise that resolves to the number of documents matched and modified.
|
|
303
|
+
*/
|
|
304
|
+
async _updateMany(input, options) {
|
|
305
|
+
const isUpdateFilter = Array.isArray(input) || !!Object.keys(input).find(x => x.startsWith('$'));
|
|
306
|
+
const isDocument = !Array.isArray(input) && !!Object.keys(input).find(x => !x.startsWith('$'));
|
|
307
|
+
if (isUpdateFilter && isDocument)
|
|
308
|
+
throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
|
|
309
|
+
let update;
|
|
310
|
+
if (isDocument) {
|
|
311
|
+
const encode = this.getEncoder('update');
|
|
312
|
+
const doc = encode(input, { coerce: true });
|
|
313
|
+
delete doc._id;
|
|
314
|
+
update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc);
|
|
315
|
+
if (!Object.keys(doc).length)
|
|
316
|
+
return 0;
|
|
317
|
+
}
|
|
318
|
+
else
|
|
319
|
+
update = input;
|
|
320
|
+
const mongoOptions = {
|
|
321
|
+
...(0, lodash_omit_1.default)(options, 'filter'),
|
|
322
|
+
upsert: undefined,
|
|
323
|
+
};
|
|
324
|
+
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
|
|
325
|
+
const r = await this._dbUpdateMany(filter, update, mongoOptions);
|
|
326
|
+
return r.matchedCount;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
exports.MongoEntityService = MongoEntityService;
|