@opra/mongodb 0.31.13 → 0.32.1
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 +65 -10
- package/cjs/adapter-utils/prepare-patch.js +1 -0
- package/cjs/adapter-utils/prepare-projection.js +40 -40
- package/cjs/index.js +2 -0
- package/cjs/mongo-array-service.js +344 -0
- package/cjs/mongo-collection-service.js +243 -81
- package/cjs/mongo-service.js +119 -13
- package/cjs/mongo-singleton-service.js +61 -48
- package/cjs/types.js +2 -0
- package/esm/adapter-utils/prepare-filter.js +65 -10
- package/esm/adapter-utils/prepare-patch.js +1 -0
- package/esm/adapter-utils/prepare-projection.js +38 -37
- package/esm/index.js +2 -0
- package/esm/mongo-array-service.js +339 -0
- package/esm/mongo-collection-service.js +243 -81
- package/esm/mongo-service.js +119 -13
- package/esm/mongo-singleton-service.js +61 -47
- package/esm/types.js +1 -0
- package/package.json +4 -4
- package/types/adapter-utils/prepare-filter.d.ts +10 -2
- package/types/adapter-utils/prepare-projection.d.ts +7 -3
- package/types/index.d.ts +2 -0
- package/types/mongo-array-service.d.ts +193 -0
- package/types/mongo-collection-service.d.ts +120 -18
- package/types/mongo-service.d.ts +91 -11
- package/types/mongo-singleton-service.d.ts +42 -7
- package/types/types.d.ts +2 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
require("@opra/core");
|
|
4
3
|
const common_1 = require("@opra/common");
|
|
5
4
|
const opMap = {
|
|
6
5
|
'=': '$eq',
|
|
@@ -12,17 +11,73 @@ const opMap = {
|
|
|
12
11
|
'in': '$in',
|
|
13
12
|
'!in': '$nin',
|
|
14
13
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
return
|
|
14
|
+
/**
|
|
15
|
+
* Convert filter expressions to MongoDB filter objects.
|
|
16
|
+
* This method also merges multiple expressions into one single filter object
|
|
17
|
+
* @param filters
|
|
18
|
+
*/
|
|
19
|
+
function prepareFilter(filters, options) {
|
|
20
|
+
const filtersArray = Array.isArray(filters) ? filters : [filters];
|
|
21
|
+
if (!filtersArray.length)
|
|
22
|
+
return {};
|
|
23
|
+
let i = 0;
|
|
24
|
+
const out = {};
|
|
25
|
+
for (const filter of filtersArray) {
|
|
26
|
+
if (!filter)
|
|
27
|
+
continue;
|
|
28
|
+
let x;
|
|
29
|
+
if (typeof filter === 'string')
|
|
30
|
+
x = prepareFilterAst(common_1.OpraFilter.parse(filter));
|
|
31
|
+
else if (filter instanceof common_1.OpraFilter.Expression)
|
|
32
|
+
x = prepareFilterAst(filter);
|
|
33
|
+
else
|
|
34
|
+
x = filter;
|
|
35
|
+
if (Array.isArray(x))
|
|
36
|
+
x = { $and: out };
|
|
37
|
+
// Merge $and arrays
|
|
38
|
+
if (x.$and) {
|
|
39
|
+
out.$and = out.$and || [];
|
|
40
|
+
out.$and.push(...x.$and);
|
|
41
|
+
delete x.$and;
|
|
42
|
+
}
|
|
43
|
+
// Merge $or arrays
|
|
44
|
+
if (x.$or) {
|
|
45
|
+
out.$or = out.$or || [];
|
|
46
|
+
out.$or.push(...x.$or);
|
|
47
|
+
delete x.$or;
|
|
48
|
+
}
|
|
49
|
+
for (const k of Object.keys(x)) {
|
|
50
|
+
// If result object has filter field we convert it to $and
|
|
51
|
+
if (out[k]) {
|
|
52
|
+
out.$and = out.$and || [];
|
|
53
|
+
out.$and.push({ [k]: out[k] });
|
|
54
|
+
out.$and.push({ [k]: x[k] });
|
|
55
|
+
delete out[k];
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
out[k] = x[k];
|
|
59
|
+
}
|
|
60
|
+
i++;
|
|
61
|
+
}
|
|
62
|
+
return i
|
|
63
|
+
? (options?.fieldPrefix ? addPrefix(out, options.fieldPrefix) : out)
|
|
64
|
+
: undefined;
|
|
24
65
|
}
|
|
25
66
|
exports.default = prepareFilter;
|
|
67
|
+
function addPrefix(source, prefix) {
|
|
68
|
+
if (typeof source !== 'object')
|
|
69
|
+
return source;
|
|
70
|
+
if (Array.isArray(source))
|
|
71
|
+
return source.map(v => addPrefix(v, prefix));
|
|
72
|
+
const out = {};
|
|
73
|
+
for (const [k, v] of Object.entries(source)) {
|
|
74
|
+
if (k.startsWith('$'))
|
|
75
|
+
out[k] = addPrefix(v, prefix);
|
|
76
|
+
else
|
|
77
|
+
out[prefix + k] = addPrefix(v, prefix);
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
26
81
|
function prepareFilterAst(ast, negative) {
|
|
27
82
|
if (!ast)
|
|
28
83
|
return;
|
|
@@ -1,52 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports._prepareProjection = void 0;
|
|
4
4
|
const common_1 = require("@opra/common");
|
|
5
|
-
function prepareProjection(dataType,
|
|
5
|
+
function prepareProjection(dataType, options) {
|
|
6
6
|
const out = {};
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
7
|
+
const pick = options?.pick && (0, common_1.pathToObjectTree)(options.pick);
|
|
8
|
+
const include = options?.include && (0, common_1.pathToObjectTree)(options.include);
|
|
9
|
+
const omit = options?.omit && (0, common_1.pathToObjectTree)(options.omit);
|
|
10
|
+
// const exclusionProjection = !pick && !!omit;
|
|
11
|
+
_prepareProjection(dataType, out, {
|
|
12
|
+
pickActivated: !!pick,
|
|
13
|
+
pick,
|
|
14
|
+
include,
|
|
15
|
+
omit
|
|
16
|
+
});
|
|
16
17
|
return Object.keys(out).length ? out : undefined;
|
|
17
18
|
}
|
|
18
19
|
exports.default = prepareProjection;
|
|
19
|
-
function
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
function _prepareProjection(dataType, target,
|
|
21
|
+
// exclusionProjection: boolean,
|
|
22
|
+
options) {
|
|
23
|
+
// const defaultFields = options?.defaultFields ?? !options?.pick;
|
|
24
|
+
const optionsOmit = options?.omit;
|
|
25
|
+
const optionsPick = options?.pick;
|
|
26
|
+
const optionsInclude = options?.include;
|
|
27
|
+
const pickActivated = options?.pickActivated;
|
|
22
28
|
for (const [k, f] of dataType.fields.entries()) {
|
|
23
|
-
|
|
29
|
+
const fieldOmit = optionsOmit?.[k];
|
|
30
|
+
const fieldInclude = optionsInclude?.[k];
|
|
31
|
+
const fieldPick = optionsPick?.[k];
|
|
32
|
+
if (fieldOmit === true ||
|
|
33
|
+
!((pickActivated && fieldPick) ||
|
|
34
|
+
(!pickActivated && (!f.exclusive || fieldInclude))))
|
|
35
|
+
continue;
|
|
36
|
+
if (f.type instanceof common_1.ComplexType &&
|
|
37
|
+
(typeof fieldInclude === 'object' ||
|
|
38
|
+
typeof fieldPick === 'object' ||
|
|
39
|
+
typeof fieldOmit === 'object')) {
|
|
40
|
+
target[k] = {};
|
|
41
|
+
_prepareProjection(f.type, target[k], {
|
|
42
|
+
pickActivated: fieldPick != null && fieldPick !== true,
|
|
43
|
+
include: typeof fieldInclude === 'object' ? fieldInclude : undefined,
|
|
44
|
+
pick: typeof fieldPick === 'object' ? fieldPick : undefined,
|
|
45
|
+
omit: typeof fieldOmit === 'object' ? fieldOmit : undefined
|
|
46
|
+
});
|
|
24
47
|
continue;
|
|
25
|
-
n = (defaultFields && !f.exclusive) ||
|
|
26
|
-
pick === true || pick?.[k] || include === true || include?.[k];
|
|
27
|
-
if (n) {
|
|
28
|
-
if (f.type instanceof common_1.ComplexType && (typeof n === 'object' || typeof omit?.[k] === 'object')) {
|
|
29
|
-
target[k] = {};
|
|
30
|
-
_prepareInclusionProjection(f.type, target[k], pick?.[k] || include?.[k], undefined, omit?.[k], defaultFields);
|
|
31
|
-
continue;
|
|
32
|
-
}
|
|
33
|
-
target[k] = 1;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
exports._prepareInclusionProjection = _prepareInclusionProjection;
|
|
38
|
-
function _prepareExclusionProjection(dataType, target, omit, omitExclusiveFields) {
|
|
39
|
-
let n;
|
|
40
|
-
for (const [k, f] of dataType.fields.entries()) {
|
|
41
|
-
n = omit?.[k] || (omitExclusiveFields && f.exclusive);
|
|
42
|
-
if (n) {
|
|
43
|
-
if (f.type instanceof common_1.ComplexType && typeof n === 'object') {
|
|
44
|
-
target[k] = {};
|
|
45
|
-
_prepareExclusionProjection(f.type, target[k], omit?.[k], omitExclusiveFields);
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
target[k] = 0;
|
|
49
48
|
}
|
|
49
|
+
target[k] = 1;
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
|
-
exports.
|
|
52
|
+
exports._prepareProjection = _prepareProjection;
|
package/cjs/index.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
tslib_1.__exportStar(require("./mongo-adapter.js"), exports);
|
|
5
|
+
tslib_1.__exportStar(require("./mongo-array-service.js"), exports);
|
|
5
6
|
tslib_1.__exportStar(require("./mongo-collection-service.js"), exports);
|
|
6
7
|
tslib_1.__exportStar(require("./mongo-service.js"), exports);
|
|
7
8
|
tslib_1.__exportStar(require("./mongo-singleton-service.js"), exports);
|
|
9
|
+
tslib_1.__exportStar(require("./types.js"), exports);
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MongoArrayService = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const lodash_omit_1 = tslib_1.__importDefault(require("lodash.omit"));
|
|
6
|
+
const mongodb_1 = require("mongodb");
|
|
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
|
+
*
|
|
12
|
+
* @class MongoArrayService
|
|
13
|
+
*/
|
|
14
|
+
class MongoArrayService extends mongo_service_js_1.MongoService {
|
|
15
|
+
constructor(dataType, fieldName, options) {
|
|
16
|
+
super(dataType, options);
|
|
17
|
+
this._encoders = {};
|
|
18
|
+
this.fieldName = fieldName;
|
|
19
|
+
this.defaultLimit = options?.defaultLimit || 10;
|
|
20
|
+
this.collectionKey = options?.collectionKey || '_id';
|
|
21
|
+
this.arrayKey = options?.arrayKey || '_id';
|
|
22
|
+
}
|
|
23
|
+
getArrayDataType() {
|
|
24
|
+
const t = this.getDataType()
|
|
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
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Checks if array item exists. Throws error if not.
|
|
32
|
+
*
|
|
33
|
+
* @param parentId
|
|
34
|
+
* @param id
|
|
35
|
+
*/
|
|
36
|
+
async assert(parentId, id) {
|
|
37
|
+
if (!(await this.exists(parentId, id)))
|
|
38
|
+
throw new common_1.ResourceNotFoundError((this.resourceName || this.getCollectionName()) + '.' + this.arrayKey, parentId + '/' + id);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Adds a single item into array field.
|
|
42
|
+
*
|
|
43
|
+
* @param parentId
|
|
44
|
+
* @param input
|
|
45
|
+
* @param options
|
|
46
|
+
*/
|
|
47
|
+
async create(parentId, input, options) {
|
|
48
|
+
const encode = this._getEncoder('create');
|
|
49
|
+
const doc = encode(input);
|
|
50
|
+
doc[this.arrayKey] = doc[this.arrayKey] || this._generateId();
|
|
51
|
+
const docFilter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(parentId, [this.collectionKey]);
|
|
52
|
+
const r = await this.__updateOne(docFilter, {
|
|
53
|
+
$push: { [this.fieldName]: doc }
|
|
54
|
+
}, options);
|
|
55
|
+
if (r.modifiedCount)
|
|
56
|
+
try {
|
|
57
|
+
return this.get(parentId, doc[this.arrayKey], { ...options, filter: undefined, skip: undefined });
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
Error.captureStackTrace(e);
|
|
61
|
+
throw e;
|
|
62
|
+
}
|
|
63
|
+
throw new common_1.ResourceNotFoundError(this.resourceName || this.getCollectionName(), parentId);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Gets the number of array items matching the filter.
|
|
67
|
+
* @param parentId
|
|
68
|
+
* @param options
|
|
69
|
+
*/
|
|
70
|
+
async count(parentId, options) {
|
|
71
|
+
const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(parentId, [this.collectionKey]);
|
|
72
|
+
const stages = [
|
|
73
|
+
{ $match: matchFilter },
|
|
74
|
+
{ $unwind: { path: "$" + this.fieldName } },
|
|
75
|
+
{ $replaceRoot: { newRoot: "$" + this.fieldName } }
|
|
76
|
+
];
|
|
77
|
+
if (options?.filter) {
|
|
78
|
+
const optionsFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options.filter);
|
|
79
|
+
stages.push({ $match: optionsFilter });
|
|
80
|
+
}
|
|
81
|
+
stages.push({ $count: '*' });
|
|
82
|
+
const r = await this.__aggregate(stages, options);
|
|
83
|
+
try {
|
|
84
|
+
const n = await r.next();
|
|
85
|
+
return n?.['*'] || 0;
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
await r.close();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Removes one item from an array field
|
|
93
|
+
*
|
|
94
|
+
* @param parentId
|
|
95
|
+
* @param id
|
|
96
|
+
* @param options
|
|
97
|
+
*/
|
|
98
|
+
async delete(parentId, id, options) {
|
|
99
|
+
const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(parentId, [this.collectionKey]);
|
|
100
|
+
const pullFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
|
|
101
|
+
mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, [this.arrayKey]),
|
|
102
|
+
options?.filter
|
|
103
|
+
]);
|
|
104
|
+
const r = await this.__updateOne(matchFilter, {
|
|
105
|
+
$pull: { [this.fieldName]: pullFilter }
|
|
106
|
+
}, options);
|
|
107
|
+
return r.modifiedCount ? 1 : 0;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Removes multiple items from an array field
|
|
111
|
+
*
|
|
112
|
+
* @param parentId
|
|
113
|
+
* @param options
|
|
114
|
+
*/
|
|
115
|
+
async deleteMany(parentId, options) {
|
|
116
|
+
const docFilter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(parentId, [this.collectionKey]);
|
|
117
|
+
// Count matching items, we will use this as result
|
|
118
|
+
const matchCount = await this.count(parentId, options);
|
|
119
|
+
const pullFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter) || {};
|
|
120
|
+
const r = await this.__updateOne(docFilter, {
|
|
121
|
+
$pull: { [this.fieldName]: pullFilter }
|
|
122
|
+
}, options);
|
|
123
|
+
if (r.modifiedCount)
|
|
124
|
+
return matchCount;
|
|
125
|
+
return 0;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Returns true if item exists, false otherwise
|
|
129
|
+
*
|
|
130
|
+
* @param parentId
|
|
131
|
+
* @param id
|
|
132
|
+
*/
|
|
133
|
+
async exists(parentId, id) {
|
|
134
|
+
return !!(await this.findById(parentId, id, { pick: ['_id'] }));
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Fetches the first item in an array field that matches by id.
|
|
138
|
+
*
|
|
139
|
+
* @param parentId
|
|
140
|
+
* @param id
|
|
141
|
+
* @param options
|
|
142
|
+
*/
|
|
143
|
+
async findById(parentId, id, options) {
|
|
144
|
+
let filter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, [this.arrayKey]);
|
|
145
|
+
if (options?.filter)
|
|
146
|
+
filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([filter, options?.filter]);
|
|
147
|
+
return await this.findOne(parentId, { ...options, filter });
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Fetches the first item in an array field that matches the filter. Returns undefined if not found.
|
|
151
|
+
*
|
|
152
|
+
* @param parentId
|
|
153
|
+
* @param options
|
|
154
|
+
*/
|
|
155
|
+
async findOne(parentId, options) {
|
|
156
|
+
const rows = await this.findMany(parentId, {
|
|
157
|
+
...options,
|
|
158
|
+
limit: 1
|
|
159
|
+
});
|
|
160
|
+
return rows?.[0];
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Fetches all items in an array field that matches the filter
|
|
164
|
+
*
|
|
165
|
+
* @param parentId
|
|
166
|
+
* @param options
|
|
167
|
+
*/
|
|
168
|
+
async findMany(parentId, options) {
|
|
169
|
+
const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(parentId, [this.collectionKey]);
|
|
170
|
+
const mongoOptions = {
|
|
171
|
+
...(0, lodash_omit_1.default)(options, ['pick', 'include', 'omit', 'sort', 'skip', 'limit', 'filter', 'count'])
|
|
172
|
+
};
|
|
173
|
+
const limit = options?.limit || this.defaultLimit;
|
|
174
|
+
const stages = [
|
|
175
|
+
{ $match: matchFilter },
|
|
176
|
+
{ $unwind: { path: "$" + this.fieldName } },
|
|
177
|
+
{ $replaceRoot: { newRoot: "$" + this.fieldName } }
|
|
178
|
+
];
|
|
179
|
+
let dataStages = stages;
|
|
180
|
+
if (options?.count) {
|
|
181
|
+
dataStages = [];
|
|
182
|
+
stages.push({
|
|
183
|
+
$facet: {
|
|
184
|
+
data: dataStages,
|
|
185
|
+
count: [{ $count: 'totalMatches' }]
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
if (options?.filter) {
|
|
190
|
+
const optionsFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter);
|
|
191
|
+
dataStages.push({ $match: optionsFilter });
|
|
192
|
+
}
|
|
193
|
+
if (options?.skip)
|
|
194
|
+
dataStages.push({ $skip: options.skip });
|
|
195
|
+
if (options?.sort) {
|
|
196
|
+
const sort = mongo_adapter_js_1.MongoAdapter.prepareSort(options.sort);
|
|
197
|
+
if (sort)
|
|
198
|
+
dataStages.push({ $sort: sort });
|
|
199
|
+
}
|
|
200
|
+
dataStages.push({ $limit: limit });
|
|
201
|
+
const dataType = this.getArrayDataType();
|
|
202
|
+
const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options);
|
|
203
|
+
if (projection)
|
|
204
|
+
dataStages.push({ $project: projection });
|
|
205
|
+
const cursor = await this.__aggregate(stages, {
|
|
206
|
+
...mongoOptions
|
|
207
|
+
});
|
|
208
|
+
try {
|
|
209
|
+
if (options?.count) {
|
|
210
|
+
const facetResult = await cursor.toArray();
|
|
211
|
+
this.context.response.totalMatches = facetResult[0].count[0].totalMatches || 0;
|
|
212
|
+
return facetResult[0].data;
|
|
213
|
+
}
|
|
214
|
+
else
|
|
215
|
+
return await cursor.toArray();
|
|
216
|
+
}
|
|
217
|
+
finally {
|
|
218
|
+
if (!cursor.closed)
|
|
219
|
+
await cursor.close();
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Fetches the first item in an array field that matches the item id. Throws error undefined if not found.
|
|
224
|
+
*
|
|
225
|
+
* @param parentId
|
|
226
|
+
* @param id
|
|
227
|
+
* @param options
|
|
228
|
+
*/
|
|
229
|
+
async get(parentId, id, options) {
|
|
230
|
+
const out = await this.findById(parentId, id, options);
|
|
231
|
+
if (!out)
|
|
232
|
+
throw new common_1.ResourceNotFoundError((this.resourceName || this.getCollectionName()) + '.' + this.arrayKey, parentId + '/' + id);
|
|
233
|
+
return out;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Update a single item in array field
|
|
237
|
+
*
|
|
238
|
+
* @param parentId
|
|
239
|
+
* @param id
|
|
240
|
+
* @param input
|
|
241
|
+
* @param options
|
|
242
|
+
*/
|
|
243
|
+
async update(parentId, id, input, options) {
|
|
244
|
+
await this.updateOnly(parentId, id, input, options);
|
|
245
|
+
try {
|
|
246
|
+
return await this.findById(parentId, id, options);
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
Error.captureStackTrace(e);
|
|
250
|
+
throw e;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Update a single item in array field
|
|
255
|
+
* Returns how many master documents updated (not array items)
|
|
256
|
+
*
|
|
257
|
+
* @param parentId
|
|
258
|
+
* @param id
|
|
259
|
+
* @param doc
|
|
260
|
+
* @param options
|
|
261
|
+
*/
|
|
262
|
+
async updateOnly(parentId, id, doc, options) {
|
|
263
|
+
let filter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, [this.arrayKey]);
|
|
264
|
+
if (options?.filter)
|
|
265
|
+
filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([filter, options?.filter]);
|
|
266
|
+
return await this.updateMany(parentId, doc, { ...options, filter });
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Update multiple items in array field, returns how many master documents updated (not array items)
|
|
270
|
+
*
|
|
271
|
+
* @param parentId
|
|
272
|
+
* @param input
|
|
273
|
+
* @param options
|
|
274
|
+
*/
|
|
275
|
+
async updateMany(parentId, input, options) {
|
|
276
|
+
const encode = this._getEncoder('update');
|
|
277
|
+
const doc = encode(input);
|
|
278
|
+
const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
|
|
279
|
+
mongo_adapter_js_1.MongoAdapter.prepareKeyValues(parentId, [this.collectionKey]),
|
|
280
|
+
{ [this.fieldName]: { $exists: true } }
|
|
281
|
+
]);
|
|
282
|
+
if (options?.filter) {
|
|
283
|
+
const elemMatch = mongo_adapter_js_1.MongoAdapter.prepareFilter(options?.filter, { fieldPrefix: 'elem.' });
|
|
284
|
+
options = options || {};
|
|
285
|
+
options.arrayFilters = [elemMatch];
|
|
286
|
+
}
|
|
287
|
+
const update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc, {
|
|
288
|
+
fieldPrefix: this.fieldName + (options?.filter ? '.$[elem].' : '.$[].')
|
|
289
|
+
});
|
|
290
|
+
const r = await this.__updateOne(matchFilter, update, options);
|
|
291
|
+
return r.modifiedCount;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Update multiple items in array field and returns number of updated array items
|
|
295
|
+
*
|
|
296
|
+
* @param parentId
|
|
297
|
+
* @param doc
|
|
298
|
+
* @param options
|
|
299
|
+
*/
|
|
300
|
+
async updateManyReturnCount(parentId, doc, options) {
|
|
301
|
+
const r = await this.updateMany(parentId, doc, options);
|
|
302
|
+
return r
|
|
303
|
+
// Count matching items that fits filter criteria
|
|
304
|
+
? await this.count(parentId, options)
|
|
305
|
+
: 0;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Generates Id value
|
|
309
|
+
*
|
|
310
|
+
* @protected
|
|
311
|
+
*/
|
|
312
|
+
_generateId() {
|
|
313
|
+
return new mongodb_1.ObjectId();
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Generates a new Validator for encoding or returns from cache if already generated before
|
|
317
|
+
* @param operation
|
|
318
|
+
* @protected
|
|
319
|
+
*/
|
|
320
|
+
_getEncoder(operation) {
|
|
321
|
+
let encoder = this._encoders[operation];
|
|
322
|
+
if (encoder)
|
|
323
|
+
return encoder;
|
|
324
|
+
encoder = this._generateEncoder(operation);
|
|
325
|
+
this._encoders[operation] = encoder;
|
|
326
|
+
return encoder;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Generates a new Valgen Validator for encode operation
|
|
330
|
+
*
|
|
331
|
+
* @param operation
|
|
332
|
+
* @protected
|
|
333
|
+
*/
|
|
334
|
+
_generateEncoder(operation) {
|
|
335
|
+
const dataType = this.getArrayDataType();
|
|
336
|
+
const options = {};
|
|
337
|
+
if (operation === 'update') {
|
|
338
|
+
options.omit = ['_id'];
|
|
339
|
+
options.partial = true;
|
|
340
|
+
}
|
|
341
|
+
return dataType.generateCodec('encode', options);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
exports.MongoArrayService = MongoArrayService;
|