@classytic/mongokit 1.0.2 → 2.0.0
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/README.md +562 -155
- package/package.json +17 -10
- package/src/Repository.js +296 -225
- package/src/actions/aggregate.js +266 -191
- package/src/actions/create.js +47 -47
- package/src/actions/delete.js +88 -88
- package/src/actions/index.js +11 -11
- package/src/actions/read.js +176 -144
- package/src/actions/update.js +144 -144
- package/src/hooks/lifecycle.js +146 -146
- package/src/index.js +71 -60
- package/src/pagination/PaginationEngine.js +348 -0
- package/src/pagination/utils/cursor.js +119 -0
- package/src/pagination/utils/filter.js +42 -0
- package/src/pagination/utils/limits.js +82 -0
- package/src/pagination/utils/sort.js +101 -0
- package/src/plugins/aggregate-helpers.plugin.js +71 -71
- package/src/plugins/audit-log.plugin.js +60 -60
- package/src/plugins/batch-operations.plugin.js +66 -66
- package/src/plugins/field-filter.plugin.js +27 -27
- package/src/plugins/index.js +19 -19
- package/src/plugins/method-registry.plugin.js +140 -140
- package/src/plugins/mongo-operations.plugin.js +317 -313
- package/src/plugins/soft-delete.plugin.js +46 -46
- package/src/plugins/subdocument.plugin.js +66 -66
- package/src/plugins/timestamp.plugin.js +19 -19
- package/src/plugins/validation-chain.plugin.js +145 -145
- package/src/types.d.ts +87 -0
- package/src/utils/error.js +12 -0
- package/src/utils/field-selection.js +156 -156
- package/src/utils/index.js +12 -12
- package/types/Repository.d.ts +95 -0
- package/types/Repository.d.ts.map +1 -0
- package/types/actions/aggregate.d.ts +112 -0
- package/types/actions/aggregate.d.ts.map +1 -0
- package/types/actions/create.d.ts +21 -0
- package/types/actions/create.d.ts.map +1 -0
- package/types/actions/delete.d.ts +37 -0
- package/types/actions/delete.d.ts.map +1 -0
- package/types/actions/index.d.ts +6 -121
- package/types/actions/index.d.ts.map +1 -0
- package/types/actions/read.d.ts +135 -0
- package/types/actions/read.d.ts.map +1 -0
- package/types/actions/update.d.ts +58 -0
- package/types/actions/update.d.ts.map +1 -0
- package/types/hooks/lifecycle.d.ts +44 -0
- package/types/hooks/lifecycle.d.ts.map +1 -0
- package/types/index.d.ts +25 -104
- package/types/index.d.ts.map +1 -0
- package/types/pagination/PaginationEngine.d.ts +386 -0
- package/types/pagination/PaginationEngine.d.ts.map +1 -0
- package/types/pagination/utils/cursor.d.ts +40 -0
- package/types/pagination/utils/cursor.d.ts.map +1 -0
- package/types/pagination/utils/filter.d.ts +28 -0
- package/types/pagination/utils/filter.d.ts.map +1 -0
- package/types/pagination/utils/limits.d.ts +64 -0
- package/types/pagination/utils/limits.d.ts.map +1 -0
- package/types/pagination/utils/sort.d.ts +41 -0
- package/types/pagination/utils/sort.d.ts.map +1 -0
- package/types/plugins/aggregate-helpers.plugin.d.ts +6 -0
- package/types/plugins/aggregate-helpers.plugin.d.ts.map +1 -0
- package/types/plugins/audit-log.plugin.d.ts +6 -0
- package/types/plugins/audit-log.plugin.d.ts.map +1 -0
- package/types/plugins/batch-operations.plugin.d.ts +6 -0
- package/types/plugins/batch-operations.plugin.d.ts.map +1 -0
- package/types/plugins/field-filter.plugin.d.ts +6 -0
- package/types/plugins/field-filter.plugin.d.ts.map +1 -0
- package/types/plugins/index.d.ts +11 -88
- package/types/plugins/index.d.ts.map +1 -0
- package/types/plugins/method-registry.plugin.d.ts +3 -0
- package/types/plugins/method-registry.plugin.d.ts.map +1 -0
- package/types/plugins/mongo-operations.plugin.d.ts +4 -0
- package/types/plugins/mongo-operations.plugin.d.ts.map +1 -0
- package/types/plugins/soft-delete.plugin.d.ts +6 -0
- package/types/plugins/soft-delete.plugin.d.ts.map +1 -0
- package/types/plugins/subdocument.plugin.d.ts +6 -0
- package/types/plugins/subdocument.plugin.d.ts.map +1 -0
- package/types/plugins/timestamp.plugin.d.ts +6 -0
- package/types/plugins/timestamp.plugin.d.ts.map +1 -0
- package/types/plugins/validation-chain.plugin.d.ts +31 -0
- package/types/plugins/validation-chain.plugin.d.ts.map +1 -0
- package/types/utils/error.d.ts +11 -0
- package/types/utils/error.d.ts.map +1 -0
- package/types/utils/field-selection.d.ts +9 -0
- package/types/utils/field-selection.d.ts.map +1 -0
- package/types/utils/index.d.ts +2 -24
- package/types/utils/index.d.ts.map +1 -0
package/src/actions/aggregate.js
CHANGED
|
@@ -1,191 +1,266 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Aggregate Actions
|
|
3
|
-
* MongoDB aggregation pipeline operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
*
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Aggregate Actions
|
|
3
|
+
* MongoDB aggregation pipeline operations
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @typedef {import('mongoose').Model} Model
|
|
8
|
+
* @typedef {import('mongoose').ClientSession} ClientSession
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Execute aggregation pipeline
|
|
13
|
+
*
|
|
14
|
+
* @param {Model} Model - Mongoose model
|
|
15
|
+
* @param {any[]} pipeline - Aggregation pipeline stages
|
|
16
|
+
* @param {Object} [options={}] - Aggregation options
|
|
17
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
18
|
+
* @returns {Promise<any[]>} Aggregation results
|
|
19
|
+
*/
|
|
20
|
+
export async function aggregate(Model, pipeline, options = {}) {
|
|
21
|
+
const aggregation = Model.aggregate(pipeline);
|
|
22
|
+
|
|
23
|
+
if (options.session) {
|
|
24
|
+
aggregation.session(options.session);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return aggregation.exec();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Aggregate with pagination using native MongoDB $facet
|
|
32
|
+
* WARNING: $facet results must be <16MB. For larger results (limit >1000),
|
|
33
|
+
* consider using Repository.aggregatePaginate() or splitting into separate queries.
|
|
34
|
+
*
|
|
35
|
+
* @param {Model} Model - Mongoose model
|
|
36
|
+
* @param {any[]} pipeline - Aggregation pipeline stages (before pagination)
|
|
37
|
+
* @param {Object} [options={}] - Pagination options
|
|
38
|
+
* @param {number} [options.page=1] - Page number (1-indexed)
|
|
39
|
+
* @param {number} [options.limit=10] - Documents per page
|
|
40
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
41
|
+
* @returns {Promise<{docs: any[], total: number, page: number, limit: number, pages: number, hasNext: boolean, hasPrev: boolean}>} Paginated results
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* const result = await aggregatePaginate(UserModel, [
|
|
45
|
+
* { $match: { status: 'active' } },
|
|
46
|
+
* { $group: { _id: '$category', count: { $sum: 1 } } }
|
|
47
|
+
* ], { page: 1, limit: 20 });
|
|
48
|
+
*/
|
|
49
|
+
export async function aggregatePaginate(Model, pipeline, options = {}) {
|
|
50
|
+
const page = parseInt(String(options.page || 1), 10);
|
|
51
|
+
const limit = parseInt(String(options.limit || 10), 10);
|
|
52
|
+
const skip = (page - 1) * limit;
|
|
53
|
+
|
|
54
|
+
// 16MB MongoDB document size limit safety check
|
|
55
|
+
const SAFE_LIMIT = 1000;
|
|
56
|
+
if (limit > SAFE_LIMIT) {
|
|
57
|
+
console.warn(
|
|
58
|
+
`[mongokit] Large aggregation limit (${limit}). $facet results must be <16MB. ` +
|
|
59
|
+
`Consider using Repository.aggregatePaginate() for safer handling of large datasets.`
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const facetPipeline = [
|
|
64
|
+
...pipeline,
|
|
65
|
+
{
|
|
66
|
+
$facet: {
|
|
67
|
+
docs: [
|
|
68
|
+
{ $skip: skip },
|
|
69
|
+
{ $limit: limit }
|
|
70
|
+
],
|
|
71
|
+
total: [
|
|
72
|
+
{ $count: 'count' }
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
const aggregation = Model.aggregate(facetPipeline);
|
|
79
|
+
if (options.session) {
|
|
80
|
+
aggregation.session(options.session);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const [result] = await aggregation.exec();
|
|
84
|
+
const docs = result.docs || [];
|
|
85
|
+
const total = result.total[0]?.count || 0;
|
|
86
|
+
const pages = Math.ceil(total / limit);
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
docs,
|
|
90
|
+
total,
|
|
91
|
+
page,
|
|
92
|
+
limit,
|
|
93
|
+
pages,
|
|
94
|
+
hasNext: page < pages,
|
|
95
|
+
hasPrev: page > 1
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Group documents by field value
|
|
101
|
+
*
|
|
102
|
+
* @param {Model} Model - Mongoose model
|
|
103
|
+
* @param {string} field - Field name to group by
|
|
104
|
+
* @param {Object} [options={}] - Options
|
|
105
|
+
* @param {number} [options.limit] - Maximum groups to return
|
|
106
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
107
|
+
* @returns {Promise<Array<{_id: any, count: number}>>} Grouped results
|
|
108
|
+
*/
|
|
109
|
+
export async function groupBy(Model, field, options = {}) {
|
|
110
|
+
const pipeline = [
|
|
111
|
+
{ $group: { _id: `$${field}`, count: { $sum: 1 } } },
|
|
112
|
+
{ $sort: { count: -1 } },
|
|
113
|
+
];
|
|
114
|
+
|
|
115
|
+
if (options.limit) {
|
|
116
|
+
pipeline.push(/** @type {any} */({ $limit: options.limit }));
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return aggregate(Model, pipeline, options);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Count by field values
|
|
124
|
+
*/
|
|
125
|
+
export async function countBy(Model, field, query = {}, options = {}) {
|
|
126
|
+
const pipeline = [];
|
|
127
|
+
|
|
128
|
+
if (Object.keys(query).length > 0) {
|
|
129
|
+
pipeline.push({ $match: query });
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
pipeline.push(
|
|
133
|
+
{ $group: { _id: `$${field}`, count: { $sum: 1 } } },
|
|
134
|
+
{ $sort: { count: -1 } }
|
|
135
|
+
);
|
|
136
|
+
|
|
137
|
+
return aggregate(Model, pipeline, options);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Lookup (join) with another collection
|
|
142
|
+
*/
|
|
143
|
+
export async function lookup(Model, {
|
|
144
|
+
from,
|
|
145
|
+
localField,
|
|
146
|
+
foreignField,
|
|
147
|
+
as,
|
|
148
|
+
pipeline = [],
|
|
149
|
+
query = {},
|
|
150
|
+
options = {}
|
|
151
|
+
}) {
|
|
152
|
+
const aggPipeline = [];
|
|
153
|
+
|
|
154
|
+
if (Object.keys(query).length > 0) {
|
|
155
|
+
aggPipeline.push({ $match: query });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
aggPipeline.push({
|
|
159
|
+
$lookup: {
|
|
160
|
+
from,
|
|
161
|
+
localField,
|
|
162
|
+
foreignField,
|
|
163
|
+
as,
|
|
164
|
+
...(pipeline.length > 0 ? { pipeline } : {}),
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return aggregate(Model, aggPipeline, options);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Unwind array field
|
|
173
|
+
*/
|
|
174
|
+
export async function unwind(Model, field, options = {}) {
|
|
175
|
+
const pipeline = [
|
|
176
|
+
{
|
|
177
|
+
$unwind: {
|
|
178
|
+
path: `$${field}`,
|
|
179
|
+
preserveNullAndEmptyArrays: options.preserveEmpty !== false,
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
];
|
|
183
|
+
|
|
184
|
+
return aggregate(Model, pipeline, options);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Facet search (multiple aggregations in one query)
|
|
189
|
+
*/
|
|
190
|
+
export async function facet(Model, facets, options = {}) {
|
|
191
|
+
const pipeline = [{ $facet: facets }];
|
|
192
|
+
|
|
193
|
+
return aggregate(Model, pipeline, options);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Get distinct values
|
|
198
|
+
*/
|
|
199
|
+
export async function distinct(Model, field, query = {}, options = {}) {
|
|
200
|
+
return Model.distinct(field, query).session(options.session);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Calculate sum
|
|
205
|
+
*/
|
|
206
|
+
export async function sum(Model, field, query = {}, options = {}) {
|
|
207
|
+
const pipeline = [];
|
|
208
|
+
|
|
209
|
+
if (Object.keys(query).length > 0) {
|
|
210
|
+
pipeline.push({ $match: query });
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
pipeline.push({
|
|
214
|
+
$group: {
|
|
215
|
+
_id: null,
|
|
216
|
+
total: { $sum: `$${field}` },
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
const result = await aggregate(Model, pipeline, options);
|
|
221
|
+
return result[0]?.total || 0;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Calculate average
|
|
226
|
+
*/
|
|
227
|
+
export async function average(Model, field, query = {}, options = {}) {
|
|
228
|
+
const pipeline = [];
|
|
229
|
+
|
|
230
|
+
if (Object.keys(query).length > 0) {
|
|
231
|
+
pipeline.push({ $match: query });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
pipeline.push({
|
|
235
|
+
$group: {
|
|
236
|
+
_id: null,
|
|
237
|
+
average: { $avg: `$${field}` },
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
const result = await aggregate(Model, pipeline, options);
|
|
242
|
+
return result[0]?.average || 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Min/Max
|
|
247
|
+
*/
|
|
248
|
+
export async function minMax(Model, field, query = {}, options = {}) {
|
|
249
|
+
const pipeline = [];
|
|
250
|
+
|
|
251
|
+
if (Object.keys(query).length > 0) {
|
|
252
|
+
pipeline.push({ $match: query });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
pipeline.push({
|
|
256
|
+
$group: {
|
|
257
|
+
_id: null,
|
|
258
|
+
min: { $min: `$${field}` },
|
|
259
|
+
max: { $max: `$${field}` },
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const result = await aggregate(Model, pipeline, options);
|
|
264
|
+
return result[0] || { min: null, max: null };
|
|
265
|
+
}
|
|
266
|
+
|
package/src/actions/create.js
CHANGED
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Create Actions
|
|
3
|
-
* Pure functions for document creation
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Create single document
|
|
8
|
-
*/
|
|
9
|
-
export async function create(Model, data, options = {}) {
|
|
10
|
-
const document = new Model(data);
|
|
11
|
-
await document.save({ session: options.session });
|
|
12
|
-
return document;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Create multiple documents
|
|
17
|
-
*/
|
|
18
|
-
export async function createMany(Model, dataArray, options = {}) {
|
|
19
|
-
return Model.insertMany(dataArray, {
|
|
20
|
-
session: options.session,
|
|
21
|
-
ordered: options.ordered !== false,
|
|
22
|
-
});
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Create with defaults (useful for initialization)
|
|
27
|
-
*/
|
|
28
|
-
export async function createDefault(Model, overrides = {}, options = {}) {
|
|
29
|
-
const defaults = {};
|
|
30
|
-
|
|
31
|
-
// Extract defaults from schema
|
|
32
|
-
Model.schema.eachPath((path, schemaType) => {
|
|
33
|
-
if (schemaType.options.default !== undefined && path !== '_id') {
|
|
34
|
-
defaults[path] = typeof schemaType.options.default === 'function'
|
|
35
|
-
? schemaType.options.default()
|
|
36
|
-
: schemaType.options.default;
|
|
37
|
-
}
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return create(Model, { ...defaults, ...overrides }, options);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Upsert (create or update)
|
|
45
|
-
*/
|
|
46
|
-
export async function upsert(Model, query, data, options = {}) {
|
|
1
|
+
/**
|
|
2
|
+
* Create Actions
|
|
3
|
+
* Pure functions for document creation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Create single document
|
|
8
|
+
*/
|
|
9
|
+
export async function create(Model, data, options = {}) {
|
|
10
|
+
const document = new Model(data);
|
|
11
|
+
await document.save({ session: options.session });
|
|
12
|
+
return document;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create multiple documents
|
|
17
|
+
*/
|
|
18
|
+
export async function createMany(Model, dataArray, options = {}) {
|
|
19
|
+
return Model.insertMany(dataArray, {
|
|
20
|
+
session: options.session,
|
|
21
|
+
ordered: options.ordered !== false,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Create with defaults (useful for initialization)
|
|
27
|
+
*/
|
|
28
|
+
export async function createDefault(Model, overrides = {}, options = {}) {
|
|
29
|
+
const defaults = {};
|
|
30
|
+
|
|
31
|
+
// Extract defaults from schema
|
|
32
|
+
Model.schema.eachPath((path, schemaType) => {
|
|
33
|
+
if (schemaType.options.default !== undefined && path !== '_id') {
|
|
34
|
+
defaults[path] = typeof schemaType.options.default === 'function'
|
|
35
|
+
? schemaType.options.default()
|
|
36
|
+
: schemaType.options.default;
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return create(Model, { ...defaults, ...overrides }, options);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Upsert (create or update)
|
|
45
|
+
*/
|
|
46
|
+
export async function upsert(Model, query, data, options = {}) {
|
|
47
47
|
return Model.findOneAndUpdate(
|
|
48
48
|
query,
|
|
49
49
|
{ $setOnInsert: data },
|
|
@@ -56,4 +56,4 @@ export async function upsert(Model, query, data, options = {}) {
|
|
|
56
56
|
}
|
|
57
57
|
);
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
|