@classytic/mongokit 1.0.2 → 2.1.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 +772 -151
- package/dist/actions/index.cjs +479 -0
- package/dist/actions/index.cjs.map +1 -0
- package/dist/actions/index.d.cts +3 -0
- package/dist/actions/index.d.ts +3 -0
- package/dist/actions/index.js +473 -0
- package/dist/actions/index.js.map +1 -0
- package/dist/index-BfVJZF-3.d.cts +337 -0
- package/dist/index-CgOJ2pqz.d.ts +337 -0
- package/dist/index.cjs +2142 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +239 -0
- package/dist/index.d.ts +239 -0
- package/dist/index.js +2108 -0
- package/dist/index.js.map +1 -0
- package/dist/memory-cache-DG2oSSbx.d.ts +142 -0
- package/dist/memory-cache-DqfFfKes.d.cts +142 -0
- package/dist/pagination/PaginationEngine.cjs +375 -0
- package/dist/pagination/PaginationEngine.cjs.map +1 -0
- package/dist/pagination/PaginationEngine.d.cts +117 -0
- package/dist/pagination/PaginationEngine.d.ts +117 -0
- package/dist/pagination/PaginationEngine.js +369 -0
- package/dist/pagination/PaginationEngine.js.map +1 -0
- package/dist/plugins/index.cjs +874 -0
- package/dist/plugins/index.cjs.map +1 -0
- package/dist/plugins/index.d.cts +275 -0
- package/dist/plugins/index.d.ts +275 -0
- package/dist/plugins/index.js +857 -0
- package/dist/plugins/index.js.map +1 -0
- package/dist/types-Nxhmi1aI.d.cts +510 -0
- package/dist/types-Nxhmi1aI.d.ts +510 -0
- package/dist/utils/index.cjs +667 -0
- package/dist/utils/index.cjs.map +1 -0
- package/dist/utils/index.d.cts +189 -0
- package/dist/utils/index.d.ts +189 -0
- package/dist/utils/index.js +643 -0
- package/dist/utils/index.js.map +1 -0
- package/package.json +54 -24
- package/src/Repository.js +0 -225
- package/src/actions/aggregate.js +0 -191
- package/src/actions/create.js +0 -59
- package/src/actions/delete.js +0 -88
- package/src/actions/index.js +0 -11
- package/src/actions/read.js +0 -156
- package/src/actions/update.js +0 -176
- package/src/hooks/lifecycle.js +0 -146
- package/src/index.js +0 -60
- package/src/plugins/aggregate-helpers.plugin.js +0 -71
- package/src/plugins/audit-log.plugin.js +0 -60
- package/src/plugins/batch-operations.plugin.js +0 -66
- package/src/plugins/field-filter.plugin.js +0 -27
- package/src/plugins/index.js +0 -19
- package/src/plugins/method-registry.plugin.js +0 -140
- package/src/plugins/mongo-operations.plugin.js +0 -313
- package/src/plugins/soft-delete.plugin.js +0 -46
- package/src/plugins/subdocument.plugin.js +0 -66
- package/src/plugins/timestamp.plugin.js +0 -19
- package/src/plugins/validation-chain.plugin.js +0 -145
- package/src/utils/field-selection.js +0 -156
- package/src/utils/index.js +0 -12
- package/types/actions/index.d.ts +0 -121
- package/types/index.d.ts +0 -104
- package/types/plugins/index.d.ts +0 -88
- package/types/utils/index.d.ts +0 -24
package/src/actions/aggregate.js
DELETED
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Aggregate Actions
|
|
3
|
-
* MongoDB aggregation pipeline operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Execute aggregation pipeline
|
|
8
|
-
*/
|
|
9
|
-
export async function aggregate(Model, pipeline, options = {}) {
|
|
10
|
-
const aggregation = Model.aggregate(pipeline);
|
|
11
|
-
|
|
12
|
-
if (options.session) {
|
|
13
|
-
aggregation.session(options.session);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return aggregation.exec();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Aggregate with pagination
|
|
21
|
-
*/
|
|
22
|
-
export async function aggregatePaginate(Model, pipeline, options = {}) {
|
|
23
|
-
const { page = 1, limit = 10 } = options;
|
|
24
|
-
|
|
25
|
-
return Model.aggregatePaginate(Model.aggregate(pipeline), {
|
|
26
|
-
page: parseInt(page, 10),
|
|
27
|
-
limit: parseInt(limit, 10),
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Group by field
|
|
33
|
-
*/
|
|
34
|
-
export async function groupBy(Model, field, options = {}) {
|
|
35
|
-
const pipeline = [
|
|
36
|
-
{ $group: { _id: `$${field}`, count: { $sum: 1 } } },
|
|
37
|
-
{ $sort: { count: -1 } },
|
|
38
|
-
];
|
|
39
|
-
|
|
40
|
-
if (options.limit) {
|
|
41
|
-
pipeline.push({ $limit: options.limit });
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return aggregate(Model, pipeline, options);
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Count by field values
|
|
49
|
-
*/
|
|
50
|
-
export async function countBy(Model, field, query = {}, options = {}) {
|
|
51
|
-
const pipeline = [];
|
|
52
|
-
|
|
53
|
-
if (Object.keys(query).length > 0) {
|
|
54
|
-
pipeline.push({ $match: query });
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
pipeline.push(
|
|
58
|
-
{ $group: { _id: `$${field}`, count: { $sum: 1 } } },
|
|
59
|
-
{ $sort: { count: -1 } }
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
return aggregate(Model, pipeline, options);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Lookup (join) with another collection
|
|
67
|
-
*/
|
|
68
|
-
export async function lookup(Model, {
|
|
69
|
-
from,
|
|
70
|
-
localField,
|
|
71
|
-
foreignField,
|
|
72
|
-
as,
|
|
73
|
-
pipeline = [],
|
|
74
|
-
query = {},
|
|
75
|
-
options = {}
|
|
76
|
-
}) {
|
|
77
|
-
const aggPipeline = [];
|
|
78
|
-
|
|
79
|
-
if (Object.keys(query).length > 0) {
|
|
80
|
-
aggPipeline.push({ $match: query });
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
aggPipeline.push({
|
|
84
|
-
$lookup: {
|
|
85
|
-
from,
|
|
86
|
-
localField,
|
|
87
|
-
foreignField,
|
|
88
|
-
as,
|
|
89
|
-
...(pipeline.length > 0 ? { pipeline } : {}),
|
|
90
|
-
},
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
return aggregate(Model, aggPipeline, options);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Unwind array field
|
|
98
|
-
*/
|
|
99
|
-
export async function unwind(Model, field, options = {}) {
|
|
100
|
-
const pipeline = [
|
|
101
|
-
{
|
|
102
|
-
$unwind: {
|
|
103
|
-
path: `$${field}`,
|
|
104
|
-
preserveNullAndEmptyArrays: options.preserveEmpty !== false,
|
|
105
|
-
},
|
|
106
|
-
},
|
|
107
|
-
];
|
|
108
|
-
|
|
109
|
-
return aggregate(Model, pipeline, options);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Facet search (multiple aggregations in one query)
|
|
114
|
-
*/
|
|
115
|
-
export async function facet(Model, facets, options = {}) {
|
|
116
|
-
const pipeline = [{ $facet: facets }];
|
|
117
|
-
|
|
118
|
-
return aggregate(Model, pipeline, options);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Get distinct values
|
|
123
|
-
*/
|
|
124
|
-
export async function distinct(Model, field, query = {}, options = {}) {
|
|
125
|
-
return Model.distinct(field, query).session(options.session);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Calculate sum
|
|
130
|
-
*/
|
|
131
|
-
export async function sum(Model, field, query = {}, options = {}) {
|
|
132
|
-
const pipeline = [];
|
|
133
|
-
|
|
134
|
-
if (Object.keys(query).length > 0) {
|
|
135
|
-
pipeline.push({ $match: query });
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
pipeline.push({
|
|
139
|
-
$group: {
|
|
140
|
-
_id: null,
|
|
141
|
-
total: { $sum: `$${field}` },
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
const result = await aggregate(Model, pipeline, options);
|
|
146
|
-
return result[0]?.total || 0;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Calculate average
|
|
151
|
-
*/
|
|
152
|
-
export async function average(Model, field, query = {}, options = {}) {
|
|
153
|
-
const pipeline = [];
|
|
154
|
-
|
|
155
|
-
if (Object.keys(query).length > 0) {
|
|
156
|
-
pipeline.push({ $match: query });
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
pipeline.push({
|
|
160
|
-
$group: {
|
|
161
|
-
_id: null,
|
|
162
|
-
average: { $avg: `$${field}` },
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
const result = await aggregate(Model, pipeline, options);
|
|
167
|
-
return result[0]?.average || 0;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Min/Max
|
|
172
|
-
*/
|
|
173
|
-
export async function minMax(Model, field, query = {}, options = {}) {
|
|
174
|
-
const pipeline = [];
|
|
175
|
-
|
|
176
|
-
if (Object.keys(query).length > 0) {
|
|
177
|
-
pipeline.push({ $match: query });
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
pipeline.push({
|
|
181
|
-
$group: {
|
|
182
|
-
_id: null,
|
|
183
|
-
min: { $min: `$${field}` },
|
|
184
|
-
max: { $max: `$${field}` },
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
const result = await aggregate(Model, pipeline, options);
|
|
189
|
-
return result[0] || { min: null, max: null };
|
|
190
|
-
}
|
|
191
|
-
|
package/src/actions/create.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
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
|
-
return Model.findOneAndUpdate(
|
|
48
|
-
query,
|
|
49
|
-
{ $setOnInsert: data },
|
|
50
|
-
{
|
|
51
|
-
upsert: true,
|
|
52
|
-
new: true,
|
|
53
|
-
runValidators: true,
|
|
54
|
-
session: options.session,
|
|
55
|
-
...(options.updatePipeline !== undefined ? { updatePipeline: options.updatePipeline } : {}),
|
|
56
|
-
}
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
|
package/src/actions/delete.js
DELETED
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Delete Actions
|
|
3
|
-
* Pure functions for document deletion
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import createError from 'http-errors';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Delete by ID
|
|
10
|
-
*/
|
|
11
|
-
export async function deleteById(Model, id, options = {}) {
|
|
12
|
-
const document = await Model.findByIdAndDelete(id).session(options.session);
|
|
13
|
-
|
|
14
|
-
if (!document) {
|
|
15
|
-
throw createError(404, 'Document not found');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
return { success: true, message: 'Deleted successfully' };
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Delete many documents
|
|
23
|
-
*/
|
|
24
|
-
export async function deleteMany(Model, query, options = {}) {
|
|
25
|
-
const result = await Model.deleteMany(query).session(options.session);
|
|
26
|
-
|
|
27
|
-
return {
|
|
28
|
-
success: true,
|
|
29
|
-
count: result.deletedCount,
|
|
30
|
-
message: 'Deleted successfully',
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Delete by query
|
|
36
|
-
*/
|
|
37
|
-
export async function deleteByQuery(Model, query, options = {}) {
|
|
38
|
-
const document = await Model.findOneAndDelete(query).session(options.session);
|
|
39
|
-
|
|
40
|
-
if (!document && options.throwOnNotFound !== false) {
|
|
41
|
-
throw createError(404, 'Document not found');
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return { success: true, message: 'Deleted successfully' };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Soft delete (set deleted flag)
|
|
49
|
-
*/
|
|
50
|
-
export async function softDelete(Model, id, options = {}) {
|
|
51
|
-
const document = await Model.findByIdAndUpdate(
|
|
52
|
-
id,
|
|
53
|
-
{
|
|
54
|
-
deleted: true,
|
|
55
|
-
deletedAt: new Date(),
|
|
56
|
-
deletedBy: options.userId,
|
|
57
|
-
},
|
|
58
|
-
{ new: true, session: options.session }
|
|
59
|
-
);
|
|
60
|
-
|
|
61
|
-
if (!document) {
|
|
62
|
-
throw createError(404, 'Document not found');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
return { success: true, message: 'Soft deleted successfully' };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Restore soft deleted document
|
|
70
|
-
*/
|
|
71
|
-
export async function restore(Model, id, options = {}) {
|
|
72
|
-
const document = await Model.findByIdAndUpdate(
|
|
73
|
-
id,
|
|
74
|
-
{
|
|
75
|
-
deleted: false,
|
|
76
|
-
deletedAt: null,
|
|
77
|
-
deletedBy: null,
|
|
78
|
-
},
|
|
79
|
-
{ new: true, session: options.session }
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
if (!document) {
|
|
83
|
-
throw createError(404, 'Document not found');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
return { success: true, message: 'Restored successfully' };
|
|
87
|
-
}
|
|
88
|
-
|
package/src/actions/index.js
DELETED
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Repository Actions
|
|
3
|
-
* Modular, composable data access operations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
export * as create from './create.js';
|
|
7
|
-
export * as read from './read.js';
|
|
8
|
-
export * as update from './update.js';
|
|
9
|
-
export * as deleteActions from './delete.js';
|
|
10
|
-
export * as aggregate from './aggregate.js';
|
|
11
|
-
|
package/src/actions/read.js
DELETED
|
@@ -1,156 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read Actions
|
|
3
|
-
* Pure functions for document retrieval
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import createError from 'http-errors';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Get by ID
|
|
10
|
-
*/
|
|
11
|
-
export async function getById(Model, id, options = {}) {
|
|
12
|
-
const query = Model.findById(id);
|
|
13
|
-
|
|
14
|
-
if (options.select) query.select(options.select);
|
|
15
|
-
if (options.populate) query.populate(parsePopulate(options.populate));
|
|
16
|
-
if (options.lean) query.lean();
|
|
17
|
-
if (options.session) query.session(options.session);
|
|
18
|
-
|
|
19
|
-
const document = await query.exec();
|
|
20
|
-
if (!document && options.throwOnNotFound !== false) {
|
|
21
|
-
throw createError(404, 'Document not found');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return document;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Get by query
|
|
29
|
-
*/
|
|
30
|
-
export async function getByQuery(Model, query, options = {}) {
|
|
31
|
-
const mongoQuery = Model.findOne(query);
|
|
32
|
-
|
|
33
|
-
if (options.select) mongoQuery.select(options.select);
|
|
34
|
-
if (options.populate) mongoQuery.populate(parsePopulate(options.populate));
|
|
35
|
-
if (options.lean) mongoQuery.lean();
|
|
36
|
-
if (options.session) mongoQuery.session(options.session);
|
|
37
|
-
|
|
38
|
-
const document = await mongoQuery.exec();
|
|
39
|
-
if (!document && options.throwOnNotFound !== false) {
|
|
40
|
-
throw createError(404, 'Document not found');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return document;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Get all with pagination
|
|
48
|
-
*/
|
|
49
|
-
export async function getAll(Model, queryParams, options = {}) {
|
|
50
|
-
const {
|
|
51
|
-
pagination = { page: 1, limit: 10 },
|
|
52
|
-
search,
|
|
53
|
-
sort = '-createdAt',
|
|
54
|
-
filters = {},
|
|
55
|
-
} = queryParams;
|
|
56
|
-
|
|
57
|
-
let query = {};
|
|
58
|
-
|
|
59
|
-
if (search) {
|
|
60
|
-
query.$text = { $search: search };
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
if (filters) {
|
|
64
|
-
query = { ...query, ...parseFilters(filters) };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const paginateOptions = {
|
|
68
|
-
page: parseInt(pagination.page, 10),
|
|
69
|
-
limit: parseInt(pagination.limit, 10),
|
|
70
|
-
sort: parseSort(sort),
|
|
71
|
-
populate: parsePopulate(options.populate),
|
|
72
|
-
select: options.select,
|
|
73
|
-
lean: options.lean !== false,
|
|
74
|
-
session: options.session,
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
return Model.paginate(query, paginateOptions);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Get or create
|
|
82
|
-
*/
|
|
83
|
-
export async function getOrCreate(Model, query, createData, options = {}) {
|
|
84
|
-
return Model.findOneAndUpdate(
|
|
85
|
-
query,
|
|
86
|
-
{ $setOnInsert: createData },
|
|
87
|
-
{
|
|
88
|
-
upsert: true,
|
|
89
|
-
new: true,
|
|
90
|
-
runValidators: true,
|
|
91
|
-
session: options.session,
|
|
92
|
-
...(options.updatePipeline !== undefined ? { updatePipeline: options.updatePipeline } : {}),
|
|
93
|
-
}
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Count documents
|
|
99
|
-
*/
|
|
100
|
-
export async function count(Model, query = {}, options = {}) {
|
|
101
|
-
return Model.countDocuments(query).session(options.session);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Check existence
|
|
106
|
-
*/
|
|
107
|
-
export async function exists(Model, query, options = {}) {
|
|
108
|
-
return Model.exists(query).session(options.session);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Utilities
|
|
112
|
-
function parsePopulate(populate) {
|
|
113
|
-
if (!populate) return [];
|
|
114
|
-
if (typeof populate === 'string') {
|
|
115
|
-
return populate.split(',').map(p => p.trim());
|
|
116
|
-
}
|
|
117
|
-
if (Array.isArray(populate)) {
|
|
118
|
-
return populate.map(p => typeof p === 'string' ? p.trim() : p);
|
|
119
|
-
}
|
|
120
|
-
return [populate];
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
function parseSort(sort) {
|
|
124
|
-
if (!sort) return { createdAt: -1 };
|
|
125
|
-
const sortOrder = sort.startsWith('-') ? -1 : 1;
|
|
126
|
-
const sortField = sort.startsWith('-') ? sort.substring(1) : sort;
|
|
127
|
-
return { [sortField]: sortOrder };
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
function parseFilters(filters) {
|
|
131
|
-
const parsed = {};
|
|
132
|
-
for (const [key, value] of Object.entries(filters)) {
|
|
133
|
-
parsed[key] = parseFilterValue(value);
|
|
134
|
-
}
|
|
135
|
-
return parsed;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function parseFilterValue(value) {
|
|
139
|
-
if (typeof value === 'string') return value;
|
|
140
|
-
|
|
141
|
-
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
|
142
|
-
const processed = {};
|
|
143
|
-
for (const [operator, operatorValue] of Object.entries(value)) {
|
|
144
|
-
if (operator === 'contains' || operator === 'like') {
|
|
145
|
-
processed.$regex = operatorValue;
|
|
146
|
-
processed.$options = 'i';
|
|
147
|
-
} else {
|
|
148
|
-
processed[operator.startsWith('$') ? operator : `$${operator}`] = operatorValue;
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
return processed;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return value;
|
|
155
|
-
}
|
|
156
|
-
|
package/src/actions/update.js
DELETED
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Update Actions
|
|
3
|
-
* Pure functions for document updates with optimizations
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import createError from 'http-errors';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Update by ID
|
|
10
|
-
*/
|
|
11
|
-
export async function update(Model, id, data, options = {}) {
|
|
12
|
-
const document = await Model.findByIdAndUpdate(id, data, {
|
|
13
|
-
new: true,
|
|
14
|
-
runValidators: true,
|
|
15
|
-
session: options.session,
|
|
16
|
-
...(options.updatePipeline !== undefined ? { updatePipeline: options.updatePipeline } : {}),
|
|
17
|
-
})
|
|
18
|
-
.select(options.select)
|
|
19
|
-
.populate(parsePopulate(options.populate))
|
|
20
|
-
.lean(options.lean);
|
|
21
|
-
|
|
22
|
-
if (!document) {
|
|
23
|
-
throw createError(404, 'Document not found');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return document;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Update with query constraints (optimized)
|
|
31
|
-
* Returns null if constraints not met (not an error)
|
|
32
|
-
*/
|
|
33
|
-
export async function updateWithConstraints(Model, id, data, constraints = {}, options = {}) {
|
|
34
|
-
const query = { _id: id, ...constraints };
|
|
35
|
-
|
|
36
|
-
const document = await Model.findOneAndUpdate(query, data, {
|
|
37
|
-
new: true,
|
|
38
|
-
runValidators: true,
|
|
39
|
-
session: options.session,
|
|
40
|
-
...(options.updatePipeline !== undefined ? { updatePipeline: options.updatePipeline } : {}),
|
|
41
|
-
})
|
|
42
|
-
.select(options.select)
|
|
43
|
-
.populate(parsePopulate(options.populate))
|
|
44
|
-
.lean(options.lean);
|
|
45
|
-
|
|
46
|
-
return document;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Update with validation (smart optimization)
|
|
51
|
-
* 1-query on success, 2-queries for detailed errors
|
|
52
|
-
*/
|
|
53
|
-
export async function updateWithValidation(
|
|
54
|
-
Model,
|
|
55
|
-
id,
|
|
56
|
-
data,
|
|
57
|
-
validationOptions = {},
|
|
58
|
-
options = {}
|
|
59
|
-
) {
|
|
60
|
-
const { buildConstraints, validateUpdate } = validationOptions;
|
|
61
|
-
|
|
62
|
-
// Try optimized update with constraints
|
|
63
|
-
if (buildConstraints) {
|
|
64
|
-
const constraints = buildConstraints(data);
|
|
65
|
-
const document = await updateWithConstraints(Model, id, data, constraints, options);
|
|
66
|
-
|
|
67
|
-
if (document) {
|
|
68
|
-
return { success: true, data: document };
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// Fetch for validation
|
|
73
|
-
const existing = await Model.findById(id)
|
|
74
|
-
.select(options.select)
|
|
75
|
-
.lean();
|
|
76
|
-
|
|
77
|
-
if (!existing) {
|
|
78
|
-
return {
|
|
79
|
-
success: false,
|
|
80
|
-
error: {
|
|
81
|
-
code: 404,
|
|
82
|
-
message: 'Document not found',
|
|
83
|
-
},
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// Run custom validation
|
|
88
|
-
if (validateUpdate) {
|
|
89
|
-
const validation = validateUpdate(existing, data);
|
|
90
|
-
if (!validation.valid) {
|
|
91
|
-
return {
|
|
92
|
-
success: false,
|
|
93
|
-
error: {
|
|
94
|
-
code: 403,
|
|
95
|
-
message: validation.message || 'Update not allowed',
|
|
96
|
-
violations: validation.violations,
|
|
97
|
-
},
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// Validation passed - perform update
|
|
103
|
-
const updated = await update(Model, id, data, options);
|
|
104
|
-
return { success: true, data: updated };
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Update many documents
|
|
109
|
-
*/
|
|
110
|
-
export async function updateMany(Model, query, data, options = {}) {
|
|
111
|
-
const result = await Model.updateMany(query, data, {
|
|
112
|
-
runValidators: true,
|
|
113
|
-
session: options.session,
|
|
114
|
-
...(options.updatePipeline !== undefined ? { updatePipeline: options.updatePipeline } : {}),
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
return {
|
|
118
|
-
matchedCount: result.matchedCount,
|
|
119
|
-
modifiedCount: result.modifiedCount,
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Update by query
|
|
125
|
-
*/
|
|
126
|
-
export async function updateByQuery(Model, query, data, options = {}) {
|
|
127
|
-
const document = await Model.findOneAndUpdate(query, data, {
|
|
128
|
-
new: true,
|
|
129
|
-
runValidators: true,
|
|
130
|
-
session: options.session,
|
|
131
|
-
...(options.updatePipeline !== undefined ? { updatePipeline: options.updatePipeline } : {}),
|
|
132
|
-
})
|
|
133
|
-
.select(options.select)
|
|
134
|
-
.populate(parsePopulate(options.populate))
|
|
135
|
-
.lean(options.lean);
|
|
136
|
-
|
|
137
|
-
if (!document && options.throwOnNotFound !== false) {
|
|
138
|
-
throw createError(404, 'Document not found');
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return document;
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Increment field
|
|
146
|
-
*/
|
|
147
|
-
export async function increment(Model, id, field, value = 1, options = {}) {
|
|
148
|
-
return update(Model, id, { $inc: { [field]: value } }, options);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Push to array
|
|
153
|
-
*/
|
|
154
|
-
export async function pushToArray(Model, id, field, value, options = {}) {
|
|
155
|
-
return update(Model, id, { $push: { [field]: value } }, options);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Pull from array
|
|
160
|
-
*/
|
|
161
|
-
export async function pullFromArray(Model, id, field, value, options = {}) {
|
|
162
|
-
return update(Model, id, { $pull: { [field]: value } }, options);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
// Utilities
|
|
166
|
-
function parsePopulate(populate) {
|
|
167
|
-
if (!populate) return [];
|
|
168
|
-
if (typeof populate === 'string') {
|
|
169
|
-
return populate.split(',').map(p => p.trim());
|
|
170
|
-
}
|
|
171
|
-
if (Array.isArray(populate)) {
|
|
172
|
-
return populate.map(p => typeof p === 'string' ? p.trim() : p);
|
|
173
|
-
}
|
|
174
|
-
return [populate];
|
|
175
|
-
}
|
|
176
|
-
|