@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/delete.js
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Delete Actions
|
|
3
|
-
* Pure functions for document deletion
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import createError from '
|
|
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
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Delete Actions
|
|
3
|
+
* Pure functions for document deletion
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createError } from '../utils/error.js';
|
|
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
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
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
|
-
|
|
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
CHANGED
|
@@ -1,86 +1,135 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Read Actions
|
|
3
|
-
* Pure functions for document retrieval
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import createError from '
|
|
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
|
-
if (options.
|
|
34
|
-
if (options.
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
*
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
if (
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
*
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Read Actions
|
|
3
|
+
* Pure functions for document retrieval
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { createError } from '../utils/error.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {import('mongoose').Model} Model
|
|
10
|
+
* @typedef {import('mongoose').PopulateOptions} PopulateOptions
|
|
11
|
+
* @typedef {import('mongoose').ClientSession} ClientSession
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Get document by ID
|
|
16
|
+
*
|
|
17
|
+
* @param {Model} Model - Mongoose model
|
|
18
|
+
* @param {string} id - Document ID
|
|
19
|
+
* @param {Object} [options={}] - Query options
|
|
20
|
+
* @param {string|string[]} [options.select] - Fields to select
|
|
21
|
+
* @param {string|string[]|PopulateOptions|PopulateOptions[]} [options.populate] - Fields to populate
|
|
22
|
+
* @param {boolean} [options.lean] - Return plain JavaScript object
|
|
23
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
24
|
+
* @param {boolean} [options.throwOnNotFound=true] - Throw error if not found
|
|
25
|
+
* @returns {Promise<any>} Document or null
|
|
26
|
+
* @throws {Error} If document not found and throwOnNotFound is true
|
|
27
|
+
*/
|
|
28
|
+
export async function getById(Model, id, options = {}) {
|
|
29
|
+
const query = Model.findById(id);
|
|
30
|
+
|
|
31
|
+
if (options.select) query.select(options.select);
|
|
32
|
+
if (options.populate) query.populate(parsePopulate(options.populate));
|
|
33
|
+
if (options.lean) query.lean();
|
|
34
|
+
if (options.session) query.session(options.session);
|
|
35
|
+
|
|
36
|
+
const document = await query.exec();
|
|
37
|
+
if (!document && options.throwOnNotFound !== false) {
|
|
38
|
+
throw createError(404, 'Document not found');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return document;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Get document by query
|
|
46
|
+
*
|
|
47
|
+
* @param {Model} Model - Mongoose model
|
|
48
|
+
* @param {Record<string, any>} query - MongoDB query
|
|
49
|
+
* @param {Object} [options={}] - Query options
|
|
50
|
+
* @param {string|string[]} [options.select] - Fields to select
|
|
51
|
+
* @param {string|string[]|PopulateOptions|PopulateOptions[]} [options.populate] - Fields to populate
|
|
52
|
+
* @param {boolean} [options.lean] - Return plain JavaScript object
|
|
53
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
54
|
+
* @param {boolean} [options.throwOnNotFound=true] - Throw error if not found
|
|
55
|
+
* @returns {Promise<any>} Document or null
|
|
56
|
+
* @throws {Error} If document not found and throwOnNotFound is true
|
|
57
|
+
*/
|
|
58
|
+
export async function getByQuery(Model, query, options = {}) {
|
|
59
|
+
const mongoQuery = Model.findOne(query);
|
|
60
|
+
|
|
61
|
+
if (options.select) mongoQuery.select(options.select);
|
|
62
|
+
if (options.populate) mongoQuery.populate(parsePopulate(options.populate));
|
|
63
|
+
if (options.lean) mongoQuery.lean();
|
|
64
|
+
if (options.session) mongoQuery.session(options.session);
|
|
65
|
+
|
|
66
|
+
const document = await mongoQuery.exec();
|
|
67
|
+
if (!document && options.throwOnNotFound !== false) {
|
|
68
|
+
throw createError(404, 'Document not found');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return document;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get document by query without throwing (returns null if not found)
|
|
76
|
+
*
|
|
77
|
+
* @param {Model} Model - Mongoose model
|
|
78
|
+
* @param {Record<string, any>} query - MongoDB query
|
|
79
|
+
* @param {Object} [options={}] - Query options
|
|
80
|
+
* @param {string|string[]} [options.select] - Fields to select
|
|
81
|
+
* @param {string|string[]|PopulateOptions|PopulateOptions[]} [options.populate] - Fields to populate
|
|
82
|
+
* @param {boolean} [options.lean] - Return plain JavaScript object
|
|
83
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
84
|
+
* @returns {Promise<any|null>} Document or null
|
|
85
|
+
*/
|
|
86
|
+
export async function tryGetByQuery(Model, query, options = {}) {
|
|
87
|
+
return getByQuery(Model, query, { ...options, throwOnNotFound: false });
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get all documents (basic query without pagination)
|
|
92
|
+
* For pagination, use Repository.paginate() or Repository.stream()
|
|
93
|
+
*
|
|
94
|
+
* @param {Model} Model - Mongoose model
|
|
95
|
+
* @param {Record<string, any>} [query={}] - MongoDB query
|
|
96
|
+
* @param {Object} [options={}] - Query options
|
|
97
|
+
* @param {string|string[]} [options.select] - Fields to select
|
|
98
|
+
* @param {string|string[]|PopulateOptions|PopulateOptions[]} [options.populate] - Fields to populate
|
|
99
|
+
* @param {Record<string, 1|-1>} [options.sort] - Sort specification
|
|
100
|
+
* @param {number} [options.limit] - Maximum documents to return
|
|
101
|
+
* @param {number} [options.skip] - Documents to skip
|
|
102
|
+
* @param {boolean} [options.lean=true] - Return plain JavaScript objects
|
|
103
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
104
|
+
* @returns {Promise<any[]>} Array of documents
|
|
105
|
+
*/
|
|
106
|
+
export async function getAll(Model, query = {}, options = {}) {
|
|
107
|
+
let mongoQuery = Model.find(query);
|
|
108
|
+
|
|
109
|
+
if (options.select) mongoQuery = mongoQuery.select(options.select);
|
|
110
|
+
if (options.populate) mongoQuery = mongoQuery.populate(parsePopulate(options.populate));
|
|
111
|
+
if (options.sort) mongoQuery = mongoQuery.sort(options.sort);
|
|
112
|
+
if (options.limit) mongoQuery = mongoQuery.limit(options.limit);
|
|
113
|
+
if (options.skip) mongoQuery = mongoQuery.skip(options.skip);
|
|
114
|
+
|
|
115
|
+
mongoQuery = mongoQuery.lean(options.lean !== false);
|
|
116
|
+
if (options.session) mongoQuery = mongoQuery.session(options.session);
|
|
117
|
+
|
|
118
|
+
return mongoQuery.exec();
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get or create document (upsert)
|
|
123
|
+
*
|
|
124
|
+
* @param {Model} Model - Mongoose model
|
|
125
|
+
* @param {Record<string, any>} query - Query to find document
|
|
126
|
+
* @param {Record<string, any>} createData - Data to insert if not found
|
|
127
|
+
* @param {Object} [options={}] - Query options
|
|
128
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
129
|
+
* @param {boolean} [options.updatePipeline] - Use update pipeline
|
|
130
|
+
* @returns {Promise<any>} Created or found document
|
|
131
|
+
*/
|
|
132
|
+
export async function getOrCreate(Model, query, createData, options = {}) {
|
|
84
133
|
return Model.findOneAndUpdate(
|
|
85
134
|
query,
|
|
86
135
|
{ $setOnInsert: createData },
|
|
@@ -93,64 +142,47 @@ export async function getOrCreate(Model, query, createData, options = {}) {
|
|
|
93
142
|
}
|
|
94
143
|
);
|
|
95
144
|
}
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Count documents
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
return
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Count documents matching query
|
|
148
|
+
*
|
|
149
|
+
* @param {Model} Model - Mongoose model
|
|
150
|
+
* @param {Record<string, any>} [query={}] - MongoDB query
|
|
151
|
+
* @param {Object} [options={}] - Query options
|
|
152
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
153
|
+
* @returns {Promise<number>} Document count
|
|
154
|
+
*/
|
|
155
|
+
export async function count(Model, query = {}, options = {}) {
|
|
156
|
+
return Model.countDocuments(query).session(options.session);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Check if document exists
|
|
161
|
+
*
|
|
162
|
+
* @param {Model} Model - Mongoose model
|
|
163
|
+
* @param {Record<string, any>} query - MongoDB query
|
|
164
|
+
* @param {Object} [options={}] - Query options
|
|
165
|
+
* @param {ClientSession} [options.session] - MongoDB session
|
|
166
|
+
* @returns {Promise<{_id: any} | null>} Document ID if exists, null otherwise
|
|
167
|
+
*/
|
|
168
|
+
export async function exists(Model, query, options = {}) {
|
|
169
|
+
return Model.exists(query).session(options.session);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Parses populate parameter into Mongoose-compatible format
|
|
174
|
+
*
|
|
175
|
+
* @param {string|string[]|PopulateOptions|PopulateOptions[]} populate - Populate specification
|
|
176
|
+
* @returns {(string|PopulateOptions)[]} Normalized populate array
|
|
177
|
+
*/
|
|
178
|
+
function parsePopulate(populate) {
|
|
179
|
+
if (!populate) return [];
|
|
180
|
+
if (typeof populate === 'string') {
|
|
181
|
+
return populate.split(',').map(/** @param {string} p */ (p) => p.trim());
|
|
182
|
+
}
|
|
183
|
+
if (Array.isArray(populate)) {
|
|
184
|
+
return populate.map(/** @param {string|PopulateOptions} p */ (p) => typeof p === 'string' ? p.trim() : p);
|
|
185
|
+
}
|
|
186
|
+
return [populate];
|
|
187
|
+
}
|
|
188
|
+
|