@atscript/db-mongo 0.1.39 → 0.1.41
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 +11 -11
- package/dist/agg.cjs +26 -65
- package/dist/{agg.d.ts → agg.d.cts} +5 -12
- package/dist/agg.d.mts +18 -0
- package/dist/agg.mjs +19 -35
- package/dist/index.cjs +374 -313
- package/dist/index.d.cts +344 -0
- package/dist/index.d.mts +344 -0
- package/dist/index.mjs +364 -301
- package/dist/mongo-filter-B5ZINZPF.cjs +40 -0
- package/dist/{mongo-filter-CL69Yhcm.mjs → mongo-filter-CcQO-sEh.mjs} +9 -4
- package/dist/plugin.cjs +37 -47
- package/dist/plugin.d.cts +6 -0
- package/dist/plugin.d.mts +6 -0
- package/dist/plugin.mjs +22 -11
- package/package.json +25 -44
- package/scripts/setup-skills.js +53 -43
- package/skills/atscript-db-mongo/SKILL.md +11 -11
- package/skills/atscript-db-mongo/collections.md +29 -28
- package/skills/atscript-db-mongo/core.md +6 -5
- package/skills/atscript-db-mongo/patches.md +17 -16
- package/LICENSE +0 -21
- package/dist/agg-CUX5Jb_A.mjs +0 -75
- package/dist/agg-FBVtOv9k.cjs +0 -77
- package/dist/index.d.ts +0 -336
- package/dist/mongo-filter-C8w5by9H.cjs +0 -65
- package/dist/plugin.d.ts +0 -5
|
@@ -7,16 +7,16 @@
|
|
|
7
7
|
Entry point for MongoDB operations. Wraps a `MongoClient` and provides a collection registry.
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
|
-
import { AsMongo } from
|
|
10
|
+
import { AsMongo } from "@atscript/db-mongo";
|
|
11
11
|
|
|
12
12
|
// From connection string
|
|
13
|
-
const asMongo = new AsMongo(
|
|
13
|
+
const asMongo = new AsMongo("mongodb://localhost:27017/mydb");
|
|
14
14
|
|
|
15
15
|
// From existing MongoClient
|
|
16
|
-
const asMongo = new AsMongo(existingClient)
|
|
16
|
+
const asMongo = new AsMongo(existingClient);
|
|
17
17
|
|
|
18
18
|
// With logger
|
|
19
|
-
const asMongo = new AsMongo(connectionString, myLogger)
|
|
19
|
+
const asMongo = new AsMongo(connectionString, myLogger);
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
### `getCollection<T>(type, logger?)`
|
|
@@ -24,9 +24,9 @@ const asMongo = new AsMongo(connectionString, myLogger)
|
|
|
24
24
|
Returns an `AsCollection<T>` for the given Atscript annotated type. Collections are cached per type.
|
|
25
25
|
|
|
26
26
|
```typescript
|
|
27
|
-
import { User } from
|
|
27
|
+
import { User } from "./user.as";
|
|
28
28
|
|
|
29
|
-
const users = asMongo.getCollection(User)
|
|
29
|
+
const users = asMongo.getCollection(User);
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
## AsCollection
|
|
@@ -44,16 +44,16 @@ Core collection abstraction providing validation, CRUD operations, and index man
|
|
|
44
44
|
```typescript
|
|
45
45
|
// Insert one
|
|
46
46
|
const result = await users.insert({
|
|
47
|
-
email:
|
|
48
|
-
name:
|
|
47
|
+
email: "alice@example.com",
|
|
48
|
+
name: "Alice",
|
|
49
49
|
isActive: true,
|
|
50
|
-
})
|
|
50
|
+
});
|
|
51
51
|
|
|
52
52
|
// Insert many
|
|
53
53
|
const result = await users.insert([
|
|
54
|
-
{ email:
|
|
55
|
-
{ email:
|
|
56
|
-
])
|
|
54
|
+
{ email: "alice@example.com", name: "Alice", isActive: true },
|
|
55
|
+
{ email: "bob@example.com", name: "Bob", isActive: false },
|
|
56
|
+
]);
|
|
57
57
|
```
|
|
58
58
|
|
|
59
59
|
Validates the payload before inserting. Auto-generates `ObjectId` for `_id` if type is `mongo.objectId`.
|
|
@@ -62,11 +62,11 @@ Validates the payload before inserting. Auto-generates `ObjectId` for `_id` if t
|
|
|
62
62
|
|
|
63
63
|
```typescript
|
|
64
64
|
await users.replace({
|
|
65
|
-
_id:
|
|
66
|
-
email:
|
|
67
|
-
name:
|
|
65
|
+
_id: "507f1f77bcf86cd799439011",
|
|
66
|
+
email: "alice@new.com",
|
|
67
|
+
name: "Alice Updated",
|
|
68
68
|
isActive: true,
|
|
69
|
-
})
|
|
69
|
+
});
|
|
70
70
|
```
|
|
71
71
|
|
|
72
72
|
Validates the full document and replaces by `_id`.
|
|
@@ -75,10 +75,10 @@ Validates the full document and replaces by `_id`.
|
|
|
75
75
|
|
|
76
76
|
```typescript
|
|
77
77
|
await users.update({
|
|
78
|
-
_id:
|
|
79
|
-
name:
|
|
78
|
+
_id: "507f1f77bcf86cd799439011",
|
|
79
|
+
name: "New Name",
|
|
80
80
|
// Only updates specified fields
|
|
81
|
-
})
|
|
81
|
+
});
|
|
82
82
|
```
|
|
83
83
|
|
|
84
84
|
Uses `CollectionPatcher` internally to build MongoDB aggregation pipelines. See [patches.md](patches.md) for array patch operations.
|
|
@@ -87,28 +87,29 @@ Uses `CollectionPatcher` internally to build MongoDB aggregation pipelines. See
|
|
|
87
87
|
|
|
88
88
|
```typescript
|
|
89
89
|
// Get a validator for different contexts
|
|
90
|
-
const insertValidator = users.getValidator(
|
|
91
|
-
const updateValidator = users.getValidator(
|
|
92
|
-
const patchValidator = users.getValidator(
|
|
90
|
+
const insertValidator = users.getValidator("insert");
|
|
91
|
+
const updateValidator = users.getValidator("update");
|
|
92
|
+
const patchValidator = users.getValidator("patch");
|
|
93
93
|
|
|
94
94
|
// Create a custom validator
|
|
95
95
|
const validator = users.createValidator({
|
|
96
96
|
partial: true,
|
|
97
97
|
plugins: [myPlugin],
|
|
98
|
-
skipList: new Set([
|
|
99
|
-
})
|
|
98
|
+
skipList: new Set(["internalField"]),
|
|
99
|
+
});
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
### Index Management
|
|
103
103
|
|
|
104
104
|
```typescript
|
|
105
105
|
// Sync indexes — creates/drops to match .as definitions
|
|
106
|
-
await users.syncIndexes()
|
|
106
|
+
await users.syncIndexes();
|
|
107
107
|
```
|
|
108
108
|
|
|
109
109
|
Only manages indexes prefixed with `atscript__`. User-created indexes are not touched.
|
|
110
110
|
|
|
111
111
|
Reads index definitions from:
|
|
112
|
+
|
|
112
113
|
- `@db.index.plain` → standard indexes
|
|
113
114
|
- `@db.index.unique` → unique indexes
|
|
114
115
|
- `@db.index.fulltext` → text indexes (weight 1)
|
|
@@ -119,10 +120,10 @@ Reads index definitions from:
|
|
|
119
120
|
|
|
120
121
|
```typescript
|
|
121
122
|
// Find documents
|
|
122
|
-
const cursor = users.collection.find({ isActive: true })
|
|
123
|
+
const cursor = users.collection.find({ isActive: true });
|
|
123
124
|
|
|
124
125
|
// Use the raw MongoDB collection for queries
|
|
125
|
-
const doc = await users.collection.findOne({ _id: users.prepareId(id) })
|
|
126
|
+
const doc = await users.collection.findOne({ _id: users.prepareId(id) });
|
|
126
127
|
```
|
|
127
128
|
|
|
128
129
|
### `prepareId(id)`
|
|
@@ -130,7 +131,7 @@ const doc = await users.collection.findOne({ _id: users.prepareId(id) })
|
|
|
130
131
|
Converts a string ID to `ObjectId` if the collection uses `mongo.objectId` type for `_id`.
|
|
131
132
|
|
|
132
133
|
```typescript
|
|
133
|
-
const objectId = users.prepareId(
|
|
134
|
+
const objectId = users.prepareId("507f1f77bcf86cd799439011");
|
|
134
135
|
```
|
|
135
136
|
|
|
136
137
|
## Best Practices
|
|
@@ -15,17 +15,18 @@ npm install @atscript/core @atscript/typescript mongodb
|
|
|
15
15
|
Add `MongoPlugin()` to your `atscript.config.ts`:
|
|
16
16
|
|
|
17
17
|
```typescript
|
|
18
|
-
import { defineConfig } from
|
|
19
|
-
import { ts } from
|
|
20
|
-
import MongoPlugin from
|
|
18
|
+
import { defineConfig } from "@atscript/core";
|
|
19
|
+
import { ts } from "@atscript/typescript";
|
|
20
|
+
import MongoPlugin from "@atscript/db-mongo/plugin";
|
|
21
21
|
|
|
22
22
|
export default defineConfig({
|
|
23
|
-
rootDir:
|
|
23
|
+
rootDir: "src",
|
|
24
24
|
plugins: [ts(), MongoPlugin()],
|
|
25
|
-
})
|
|
25
|
+
});
|
|
26
26
|
```
|
|
27
27
|
|
|
28
28
|
The plugin registers:
|
|
29
|
+
|
|
29
30
|
- **Primitives**: `mongo.objectId` (24-char hex string), `mongo.vector` (number array)
|
|
30
31
|
- **Annotations**: All `@db.mongo.*` annotations (collection, indexes, search, patch, array)
|
|
31
32
|
|
|
@@ -30,8 +30,8 @@ export interface User {
|
|
|
30
30
|
// This replaces the entire address object
|
|
31
31
|
await users.update({
|
|
32
32
|
_id: id,
|
|
33
|
-
address: { line1:
|
|
34
|
-
})
|
|
33
|
+
address: { line1: "123 Main St", city: "NYC", zip: "10001" },
|
|
34
|
+
});
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
### `@db.mongo.patch.strategy 'replace'`
|
|
@@ -57,8 +57,8 @@ export interface User {
|
|
|
57
57
|
// Only updates phone, email is preserved
|
|
58
58
|
await users.update({
|
|
59
59
|
_id: id,
|
|
60
|
-
contacts: { phone:
|
|
61
|
-
})
|
|
60
|
+
contacts: { phone: "+1-555-0100" },
|
|
61
|
+
});
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
### Nested strategies
|
|
@@ -90,8 +90,8 @@ Replaces the entire array:
|
|
|
90
90
|
```typescript
|
|
91
91
|
await collection.update({
|
|
92
92
|
_id: id,
|
|
93
|
-
tags: { $replace: [
|
|
94
|
-
})
|
|
93
|
+
tags: { $replace: ["new", "tags", "only"] },
|
|
94
|
+
});
|
|
95
95
|
```
|
|
96
96
|
|
|
97
97
|
### `$insert`
|
|
@@ -101,8 +101,8 @@ Appends items to the array:
|
|
|
101
101
|
```typescript
|
|
102
102
|
await collection.update({
|
|
103
103
|
_id: id,
|
|
104
|
-
tags: { $insert: [
|
|
105
|
-
})
|
|
104
|
+
tags: { $insert: ["newTag1", "newTag2"] },
|
|
105
|
+
});
|
|
106
106
|
```
|
|
107
107
|
|
|
108
108
|
If `@db.mongo.array.uniqueItems` is set, duplicates are silently dropped (uses `$setUnion`).
|
|
@@ -128,10 +128,10 @@ await products.update({
|
|
|
128
128
|
_id: id,
|
|
129
129
|
items: {
|
|
130
130
|
$upsert: [
|
|
131
|
-
{ sku:
|
|
131
|
+
{ sku: "ABC", quantity: 10, price: 9.99 }, // replaces existing ABC or inserts
|
|
132
132
|
],
|
|
133
133
|
},
|
|
134
|
-
})
|
|
134
|
+
});
|
|
135
135
|
```
|
|
136
136
|
|
|
137
137
|
For non-keyed arrays, behaves like `$addToSet` (deep equality).
|
|
@@ -145,10 +145,10 @@ await products.update({
|
|
|
145
145
|
_id: id,
|
|
146
146
|
items: {
|
|
147
147
|
$update: [
|
|
148
|
-
{ sku:
|
|
148
|
+
{ sku: "ABC", quantity: 20 }, // updates only quantity for sku=ABC
|
|
149
149
|
],
|
|
150
150
|
},
|
|
151
|
-
})
|
|
151
|
+
});
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
With `@db.mongo.patch.strategy 'merge'` on the array field, uses `$mergeObjects` to merge into the matched element. Without it, replaces the matched element entirely.
|
|
@@ -162,17 +162,17 @@ Removes array elements:
|
|
|
162
162
|
await products.update({
|
|
163
163
|
_id: id,
|
|
164
164
|
items: {
|
|
165
|
-
$remove: [{ sku:
|
|
165
|
+
$remove: [{ sku: "ABC" }],
|
|
166
166
|
},
|
|
167
|
-
})
|
|
167
|
+
});
|
|
168
168
|
|
|
169
169
|
// Non-keyed — removes by deep equality
|
|
170
170
|
await collection.update({
|
|
171
171
|
_id: id,
|
|
172
172
|
tags: {
|
|
173
|
-
$remove: [
|
|
173
|
+
$remove: ["obsoleteTag"],
|
|
174
174
|
},
|
|
175
|
-
})
|
|
175
|
+
});
|
|
176
176
|
```
|
|
177
177
|
|
|
178
178
|
## Array Keys
|
|
@@ -196,6 +196,7 @@ Multiple key fields form a composite key — elements are matched when ALL key f
|
|
|
196
196
|
## Implementation Details
|
|
197
197
|
|
|
198
198
|
The `CollectionPatcher` converts patch payloads into MongoDB aggregation pipeline stages using:
|
|
199
|
+
|
|
199
200
|
- `$reduce` + `$filter` + `$concatArrays` for keyed upsert/remove
|
|
200
201
|
- `$map` + `$cond` + `$mergeObjects` for keyed update with merge
|
|
201
202
|
- `$setUnion` for unique/non-keyed insert
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2025-present Artem Maltsev
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/dist/agg-CUX5Jb_A.mjs
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { buildMongoFilter } from "./mongo-filter-CL69Yhcm.mjs";
|
|
2
|
-
import { resolveAlias } from "@atscript/db/agg";
|
|
3
|
-
|
|
4
|
-
//#region packages/db-mongo/src/agg.ts
|
|
5
|
-
/** Simple accumulators that map directly to `{ $<fn>: '$field' }`. */ const SIMPLE_ACCUMULATORS = {
|
|
6
|
-
sum: "$sum",
|
|
7
|
-
avg: "$avg",
|
|
8
|
-
min: "$min",
|
|
9
|
-
max: "$max"
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Maps an AggregateExpr to a MongoDB $group accumulator expression.
|
|
13
|
-
*/ function toAccumulator(expr) {
|
|
14
|
-
const simple = SIMPLE_ACCUMULATORS[expr.$fn];
|
|
15
|
-
if (simple) return { [simple]: `$${expr.$field}` };
|
|
16
|
-
if (expr.$fn === "count") {
|
|
17
|
-
if (expr.$field === "*") return { $sum: 1 };
|
|
18
|
-
return { $sum: { $cond: [
|
|
19
|
-
{ $ne: [`$${expr.$field}`, null] },
|
|
20
|
-
1,
|
|
21
|
-
0
|
|
22
|
-
] } };
|
|
23
|
-
}
|
|
24
|
-
throw new Error(`Unsupported aggregate function: ${expr.$fn}`);
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Builds the common prefix stages: $match + $group._id from groupBy fields.
|
|
28
|
-
* Shared by both full aggregate and count pipelines.
|
|
29
|
-
*/ function buildPrefix(query) {
|
|
30
|
-
const controls = query.controls || {};
|
|
31
|
-
const groupBy = controls.$groupBy ?? [];
|
|
32
|
-
const pipeline = [{ $match: buildMongoFilter(query.filter) }];
|
|
33
|
-
const groupId = {};
|
|
34
|
-
for (const field of groupBy) groupId[field] = `$${field}`;
|
|
35
|
-
return {
|
|
36
|
-
pipeline,
|
|
37
|
-
groupId,
|
|
38
|
-
groupBy,
|
|
39
|
-
controls
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
function buildAggregatePipeline(query) {
|
|
43
|
-
const { pipeline, groupId, groupBy, controls } = buildPrefix(query);
|
|
44
|
-
const groupStage = { _id: groupId };
|
|
45
|
-
const project = { _id: 0 };
|
|
46
|
-
const aggregates = controls.$select?.aggregates;
|
|
47
|
-
for (const field of groupBy) project[field] = `$_id.${field}`;
|
|
48
|
-
if (aggregates) for (const expr of aggregates) {
|
|
49
|
-
const alias = resolveAlias(expr);
|
|
50
|
-
groupStage[alias] = toAccumulator(expr);
|
|
51
|
-
project[alias] = 1;
|
|
52
|
-
}
|
|
53
|
-
pipeline.push({ $group: groupStage });
|
|
54
|
-
pipeline.push({ $project: project });
|
|
55
|
-
if (controls.$having) pipeline.push({ $match: buildMongoFilter(controls.$having) });
|
|
56
|
-
if (controls.$sort) pipeline.push({ $sort: controls.$sort });
|
|
57
|
-
if (controls.$skip) pipeline.push({ $skip: controls.$skip });
|
|
58
|
-
if (controls.$limit) pipeline.push({ $limit: controls.$limit });
|
|
59
|
-
return pipeline;
|
|
60
|
-
}
|
|
61
|
-
function buildCountPipeline(query) {
|
|
62
|
-
const { pipeline, groupId, groupBy, controls } = buildPrefix(query);
|
|
63
|
-
pipeline.push({ $group: { _id: groupId } });
|
|
64
|
-
if (controls.$having) {
|
|
65
|
-
const project = { _id: 0 };
|
|
66
|
-
for (const field of groupBy) project[field] = `$_id.${field}`;
|
|
67
|
-
pipeline.push({ $project: project });
|
|
68
|
-
pipeline.push({ $match: buildMongoFilter(controls.$having) });
|
|
69
|
-
}
|
|
70
|
-
pipeline.push({ $count: "count" });
|
|
71
|
-
return pipeline;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
//#endregion
|
|
75
|
-
export { buildAggregatePipeline, buildCountPipeline };
|
package/dist/agg-FBVtOv9k.cjs
DELETED
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
const require_mongo_filter = require('./mongo-filter-C8w5by9H.cjs');
|
|
3
|
-
const __atscript_db_agg = require_mongo_filter.__toESM(require("@atscript/db/agg"));
|
|
4
|
-
|
|
5
|
-
//#region packages/db-mongo/src/agg.ts
|
|
6
|
-
/** Simple accumulators that map directly to `{ $<fn>: '$field' }`. */ const SIMPLE_ACCUMULATORS = {
|
|
7
|
-
sum: "$sum",
|
|
8
|
-
avg: "$avg",
|
|
9
|
-
min: "$min",
|
|
10
|
-
max: "$max"
|
|
11
|
-
};
|
|
12
|
-
/**
|
|
13
|
-
* Maps an AggregateExpr to a MongoDB $group accumulator expression.
|
|
14
|
-
*/ function toAccumulator(expr) {
|
|
15
|
-
const simple = SIMPLE_ACCUMULATORS[expr.$fn];
|
|
16
|
-
if (simple) return { [simple]: `$${expr.$field}` };
|
|
17
|
-
if (expr.$fn === "count") {
|
|
18
|
-
if (expr.$field === "*") return { $sum: 1 };
|
|
19
|
-
return { $sum: { $cond: [
|
|
20
|
-
{ $ne: [`$${expr.$field}`, null] },
|
|
21
|
-
1,
|
|
22
|
-
0
|
|
23
|
-
] } };
|
|
24
|
-
}
|
|
25
|
-
throw new Error(`Unsupported aggregate function: ${expr.$fn}`);
|
|
26
|
-
}
|
|
27
|
-
/**
|
|
28
|
-
* Builds the common prefix stages: $match + $group._id from groupBy fields.
|
|
29
|
-
* Shared by both full aggregate and count pipelines.
|
|
30
|
-
*/ function buildPrefix(query) {
|
|
31
|
-
const controls = query.controls || {};
|
|
32
|
-
const groupBy = controls.$groupBy ?? [];
|
|
33
|
-
const pipeline = [{ $match: require_mongo_filter.buildMongoFilter(query.filter) }];
|
|
34
|
-
const groupId = {};
|
|
35
|
-
for (const field of groupBy) groupId[field] = `$${field}`;
|
|
36
|
-
return {
|
|
37
|
-
pipeline,
|
|
38
|
-
groupId,
|
|
39
|
-
groupBy,
|
|
40
|
-
controls
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
function buildAggregatePipeline(query) {
|
|
44
|
-
const { pipeline, groupId, groupBy, controls } = buildPrefix(query);
|
|
45
|
-
const groupStage = { _id: groupId };
|
|
46
|
-
const project = { _id: 0 };
|
|
47
|
-
const aggregates = controls.$select?.aggregates;
|
|
48
|
-
for (const field of groupBy) project[field] = `$_id.${field}`;
|
|
49
|
-
if (aggregates) for (const expr of aggregates) {
|
|
50
|
-
const alias = (0, __atscript_db_agg.resolveAlias)(expr);
|
|
51
|
-
groupStage[alias] = toAccumulator(expr);
|
|
52
|
-
project[alias] = 1;
|
|
53
|
-
}
|
|
54
|
-
pipeline.push({ $group: groupStage });
|
|
55
|
-
pipeline.push({ $project: project });
|
|
56
|
-
if (controls.$having) pipeline.push({ $match: require_mongo_filter.buildMongoFilter(controls.$having) });
|
|
57
|
-
if (controls.$sort) pipeline.push({ $sort: controls.$sort });
|
|
58
|
-
if (controls.$skip) pipeline.push({ $skip: controls.$skip });
|
|
59
|
-
if (controls.$limit) pipeline.push({ $limit: controls.$limit });
|
|
60
|
-
return pipeline;
|
|
61
|
-
}
|
|
62
|
-
function buildCountPipeline(query) {
|
|
63
|
-
const { pipeline, groupId, groupBy, controls } = buildPrefix(query);
|
|
64
|
-
pipeline.push({ $group: { _id: groupId } });
|
|
65
|
-
if (controls.$having) {
|
|
66
|
-
const project = { _id: 0 };
|
|
67
|
-
for (const field of groupBy) project[field] = `$_id.${field}`;
|
|
68
|
-
pipeline.push({ $project: project });
|
|
69
|
-
pipeline.push({ $match: require_mongo_filter.buildMongoFilter(controls.$having) });
|
|
70
|
-
}
|
|
71
|
-
pipeline.push({ $count: "count" });
|
|
72
|
-
return pipeline;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
//#endregion
|
|
76
|
-
exports.buildAggregatePipeline = buildAggregatePipeline
|
|
77
|
-
exports.buildCountPipeline = buildCountPipeline
|