@atscript/db-mongo 0.1.146 → 0.1.147
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/dist/agg.cjs +20 -17
- package/dist/agg.d.cts +7 -4
- package/dist/agg.d.mts +7 -4
- package/dist/agg.mjs +20 -17
- package/dist/index.cjs +472 -177
- package/dist/index.d.cts +90 -22
- package/dist/index.d.mts +90 -22
- package/dist/index.mjs +469 -179
- package/dist/mongo-accumulator-BJcQs2OV.cjs +605 -0
- package/dist/mongo-accumulator-Y_xVYo3C.mjs +534 -0
- package/dist/mongo-filter-CGd9ryOD.d.cts +123 -0
- package/dist/mongo-filter-CGd9ryOD.d.mts +123 -0
- package/package.json +9 -9
- package/dist/mongo-accumulator-BalE9LkU.cjs +0 -265
- package/dist/mongo-accumulator-Dc0w4t0l.mjs +0 -236
package/dist/agg.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_mongo_accumulator = require("./mongo-accumulator-
|
|
2
|
+
const require_mongo_accumulator = require("./mongo-accumulator-BJcQs2OV.cjs");
|
|
3
3
|
let _atscript_db_agg = require("@atscript/db/agg");
|
|
4
4
|
let _uniqu_core = require("@uniqu/core");
|
|
5
5
|
//#region src/agg.ts
|
|
@@ -9,11 +9,9 @@ function toAccumulator(expr) {
|
|
|
9
9
|
}
|
|
10
10
|
const DAY_MS = 864e5;
|
|
11
11
|
const ISO_DATE = "%Y-%m-%d";
|
|
12
|
-
/**
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*/
|
|
16
|
-
const LOCAL_DATE_FORMATS = {
|
|
12
|
+
/** Units `$dateToString` labels straight from the instant in the zone (literal `01` for day / month). */
|
|
13
|
+
const LOCAL_LABEL_FORMATS = {
|
|
14
|
+
hour: "%Y-%m-%dT%H:00",
|
|
17
15
|
day: ISO_DATE,
|
|
18
16
|
month: "%Y-%m-01",
|
|
19
17
|
year: "%Y-01-01"
|
|
@@ -41,7 +39,8 @@ function naiveFirstDay(b) {
|
|
|
41
39
|
}
|
|
42
40
|
/**
|
|
43
41
|
* The `$group._id` expression of a calendar bucket: the ISO local date
|
|
44
|
-
* `YYYY-MM-DD` of the bucket's first day in `b.tz`,
|
|
42
|
+
* `YYYY-MM-DD` of the bucket's first day in `b.tz` (for `hour`, the local
|
|
43
|
+
* `YYYY-MM-DDTHH:00`), or `null`.
|
|
45
44
|
*
|
|
46
45
|
* The only zone-aware step is instant → local date (`timezone` on
|
|
47
46
|
* `$dateToString` / `$dateToParts`, never ambiguous). `$dateTrunc` is
|
|
@@ -58,7 +57,7 @@ function naiveFirstDay(b) {
|
|
|
58
57
|
function bucketExpression(b) {
|
|
59
58
|
const source = `$${b.field}`;
|
|
60
59
|
const date = { $toDate: { $toLong: source } };
|
|
61
|
-
const format =
|
|
60
|
+
const format = LOCAL_LABEL_FORMATS[b.unit];
|
|
62
61
|
const label = format ? { $dateToString: {
|
|
63
62
|
date,
|
|
64
63
|
format,
|
|
@@ -101,10 +100,11 @@ function bucketExpression(b) {
|
|
|
101
100
|
* position in front of the filter `$match`. Resolving it needs adapter state,
|
|
102
101
|
* so the caller passes it in and this module stays a pure translation.
|
|
103
102
|
*/
|
|
104
|
-
function buildPrefix(query, searchStage) {
|
|
103
|
+
function buildPrefix(query, searchStage, filterOptions) {
|
|
105
104
|
const controls = query.controls || {};
|
|
106
105
|
const groupBy = controls.$groupBy ?? [];
|
|
107
|
-
const
|
|
106
|
+
const filterStages = require_mongo_accumulator.mongoFilterStages(query.filter, filterOptions);
|
|
107
|
+
const pipeline = searchStage ? [searchStage, ...filterStages] : filterStages;
|
|
108
108
|
const groupId = {};
|
|
109
109
|
const groupKeys = [];
|
|
110
110
|
for (const [index, key] of groupBy.entries()) {
|
|
@@ -132,8 +132,8 @@ function buildPrefix(query, searchStage) {
|
|
|
132
132
|
* (no accumulators, no `$project`, no `$having`) — the cheapest shape for a
|
|
133
133
|
* plain group count, where no alias has to be resolvable.
|
|
134
134
|
*/
|
|
135
|
-
function buildGroupedStages(query, { accumulators, searchStage }) {
|
|
136
|
-
const { pipeline, groupId, groupKeys, controls } = buildPrefix(query, searchStage);
|
|
135
|
+
function buildGroupedStages(query, { accumulators, searchStage, filterOptions }) {
|
|
136
|
+
const { pipeline, groupId, groupKeys, controls } = buildPrefix(query, searchStage, filterOptions);
|
|
137
137
|
const groupStage = { _id: groupId };
|
|
138
138
|
if (!accumulators) {
|
|
139
139
|
pipeline.push({ $group: groupStage });
|
|
@@ -165,12 +165,14 @@ function buildGroupedStages(query, { accumulators, searchStage }) {
|
|
|
165
165
|
*
|
|
166
166
|
* `searchStage` is the resolved `$search` / `$text` stage (see
|
|
167
167
|
* `buildAggregateSearchStage`); unlike the leaf runner this path adds no
|
|
168
|
-
* relevance `$sort` and no default `$limit`.
|
|
168
|
+
* relevance `$sort` and no default `$limit`. `filterOptions` renders a
|
|
169
|
+
* filter with relational predicates (`mongoFilterStages`).
|
|
169
170
|
*/
|
|
170
|
-
function buildAggregatePipeline(query, searchStage) {
|
|
171
|
+
function buildAggregatePipeline(query, searchStage, filterOptions) {
|
|
171
172
|
const { pipeline, controls } = buildGroupedStages(query, {
|
|
172
173
|
accumulators: true,
|
|
173
|
-
searchStage
|
|
174
|
+
searchStage,
|
|
175
|
+
filterOptions
|
|
174
176
|
});
|
|
175
177
|
if (controls.$sort) pipeline.push({ $sort: controls.$sort });
|
|
176
178
|
if (controls.$skip) pipeline.push({ $skip: controls.$skip });
|
|
@@ -189,10 +191,11 @@ function buildAggregatePipeline(query, searchStage) {
|
|
|
189
191
|
* `searchStage` must be the SAME stage handed to `buildAggregatePipeline` for
|
|
190
192
|
* the same query — the counted groups are exactly the rows the search matched.
|
|
191
193
|
*/
|
|
192
|
-
function buildCountPipeline(query, searchStage) {
|
|
194
|
+
function buildCountPipeline(query, searchStage, filterOptions) {
|
|
193
195
|
const { pipeline } = buildGroupedStages(query, {
|
|
194
196
|
accumulators: Boolean(query.controls?.$having),
|
|
195
|
-
searchStage
|
|
197
|
+
searchStage,
|
|
198
|
+
filterOptions
|
|
196
199
|
});
|
|
197
200
|
pipeline.push({ $count: "count" });
|
|
198
201
|
return pipeline;
|
package/dist/agg.d.cts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { r as TMongoFilterOptions } from "./mongo-filter-CGd9ryOD.cjs";
|
|
1
2
|
import { DbQuery, TResolvedBucket } from "@atscript/db";
|
|
2
3
|
import { Document } from "mongodb";
|
|
3
4
|
|
|
4
5
|
//#region src/agg.d.ts
|
|
5
6
|
/**
|
|
6
7
|
* The `$group._id` expression of a calendar bucket: the ISO local date
|
|
7
|
-
* `YYYY-MM-DD` of the bucket's first day in `b.tz`,
|
|
8
|
+
* `YYYY-MM-DD` of the bucket's first day in `b.tz` (for `hour`, the local
|
|
9
|
+
* `YYYY-MM-DDTHH:00`), or `null`.
|
|
8
10
|
*
|
|
9
11
|
* The only zone-aware step is instant → local date (`timezone` on
|
|
10
12
|
* `$dateToString` / `$dateToParts`, never ambiguous). `$dateTrunc` is
|
|
@@ -27,9 +29,10 @@ declare function bucketExpression(b: TResolvedBucket): Document;
|
|
|
27
29
|
*
|
|
28
30
|
* `searchStage` is the resolved `$search` / `$text` stage (see
|
|
29
31
|
* `buildAggregateSearchStage`); unlike the leaf runner this path adds no
|
|
30
|
-
* relevance `$sort` and no default `$limit`.
|
|
32
|
+
* relevance `$sort` and no default `$limit`. `filterOptions` renders a
|
|
33
|
+
* filter with relational predicates (`mongoFilterStages`).
|
|
31
34
|
*/
|
|
32
|
-
declare function buildAggregatePipeline(query: DbQuery, searchStage?: Document): Document[];
|
|
35
|
+
declare function buildAggregatePipeline(query: DbQuery, searchStage?: Document, filterOptions?: TMongoFilterOptions): Document[];
|
|
33
36
|
/**
|
|
34
37
|
* Builds a count-only pipeline: the number of groups that survive `$having`
|
|
35
38
|
* (all groups when there is none). With `$having` it runs the same grouped
|
|
@@ -42,6 +45,6 @@ declare function buildAggregatePipeline(query: DbQuery, searchStage?: Document):
|
|
|
42
45
|
* `searchStage` must be the SAME stage handed to `buildAggregatePipeline` for
|
|
43
46
|
* the same query — the counted groups are exactly the rows the search matched.
|
|
44
47
|
*/
|
|
45
|
-
declare function buildCountPipeline(query: DbQuery, searchStage?: Document): Document[];
|
|
48
|
+
declare function buildCountPipeline(query: DbQuery, searchStage?: Document, filterOptions?: TMongoFilterOptions): Document[];
|
|
46
49
|
//#endregion
|
|
47
50
|
export { bucketExpression, buildAggregatePipeline, buildCountPipeline };
|
package/dist/agg.d.mts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import { r as TMongoFilterOptions } from "./mongo-filter-CGd9ryOD.mjs";
|
|
1
2
|
import { DbQuery, TResolvedBucket } from "@atscript/db";
|
|
2
3
|
import { Document } from "mongodb";
|
|
3
4
|
|
|
4
5
|
//#region src/agg.d.ts
|
|
5
6
|
/**
|
|
6
7
|
* The `$group._id` expression of a calendar bucket: the ISO local date
|
|
7
|
-
* `YYYY-MM-DD` of the bucket's first day in `b.tz`,
|
|
8
|
+
* `YYYY-MM-DD` of the bucket's first day in `b.tz` (for `hour`, the local
|
|
9
|
+
* `YYYY-MM-DDTHH:00`), or `null`.
|
|
8
10
|
*
|
|
9
11
|
* The only zone-aware step is instant → local date (`timezone` on
|
|
10
12
|
* `$dateToString` / `$dateToParts`, never ambiguous). `$dateTrunc` is
|
|
@@ -27,9 +29,10 @@ declare function bucketExpression(b: TResolvedBucket): Document;
|
|
|
27
29
|
*
|
|
28
30
|
* `searchStage` is the resolved `$search` / `$text` stage (see
|
|
29
31
|
* `buildAggregateSearchStage`); unlike the leaf runner this path adds no
|
|
30
|
-
* relevance `$sort` and no default `$limit`.
|
|
32
|
+
* relevance `$sort` and no default `$limit`. `filterOptions` renders a
|
|
33
|
+
* filter with relational predicates (`mongoFilterStages`).
|
|
31
34
|
*/
|
|
32
|
-
declare function buildAggregatePipeline(query: DbQuery, searchStage?: Document): Document[];
|
|
35
|
+
declare function buildAggregatePipeline(query: DbQuery, searchStage?: Document, filterOptions?: TMongoFilterOptions): Document[];
|
|
33
36
|
/**
|
|
34
37
|
* Builds a count-only pipeline: the number of groups that survive `$having`
|
|
35
38
|
* (all groups when there is none). With `$having` it runs the same grouped
|
|
@@ -42,6 +45,6 @@ declare function buildAggregatePipeline(query: DbQuery, searchStage?: Document):
|
|
|
42
45
|
* `searchStage` must be the SAME stage handed to `buildAggregatePipeline` for
|
|
43
46
|
* the same query — the counted groups are exactly the rows the search matched.
|
|
44
47
|
*/
|
|
45
|
-
declare function buildCountPipeline(query: DbQuery, searchStage?: Document): Document[];
|
|
48
|
+
declare function buildCountPipeline(query: DbQuery, searchStage?: Document, filterOptions?: TMongoFilterOptions): Document[];
|
|
46
49
|
//#endregion
|
|
47
50
|
export { bucketExpression, buildAggregatePipeline, buildCountPipeline };
|
package/dist/agg.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as
|
|
1
|
+
import { i as buildMongoFilter, n as distinctCountExpr, s as mongoFilterStages, t as buildAccumulator, u as orNull } from "./mongo-accumulator-Y_xVYo3C.mjs";
|
|
2
2
|
import { resolveAlias } from "@atscript/db/agg";
|
|
3
3
|
import { BUCKET_MAX_INSTANT, BUCKET_MIN_INSTANT } from "@uniqu/core";
|
|
4
4
|
//#region src/agg.ts
|
|
@@ -8,11 +8,9 @@ function toAccumulator(expr) {
|
|
|
8
8
|
}
|
|
9
9
|
const DAY_MS = 864e5;
|
|
10
10
|
const ISO_DATE = "%Y-%m-%d";
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*/
|
|
15
|
-
const LOCAL_DATE_FORMATS = {
|
|
11
|
+
/** Units `$dateToString` labels straight from the instant in the zone (literal `01` for day / month). */
|
|
12
|
+
const LOCAL_LABEL_FORMATS = {
|
|
13
|
+
hour: "%Y-%m-%dT%H:00",
|
|
16
14
|
day: ISO_DATE,
|
|
17
15
|
month: "%Y-%m-01",
|
|
18
16
|
year: "%Y-01-01"
|
|
@@ -40,7 +38,8 @@ function naiveFirstDay(b) {
|
|
|
40
38
|
}
|
|
41
39
|
/**
|
|
42
40
|
* The `$group._id` expression of a calendar bucket: the ISO local date
|
|
43
|
-
* `YYYY-MM-DD` of the bucket's first day in `b.tz`,
|
|
41
|
+
* `YYYY-MM-DD` of the bucket's first day in `b.tz` (for `hour`, the local
|
|
42
|
+
* `YYYY-MM-DDTHH:00`), or `null`.
|
|
44
43
|
*
|
|
45
44
|
* The only zone-aware step is instant → local date (`timezone` on
|
|
46
45
|
* `$dateToString` / `$dateToParts`, never ambiguous). `$dateTrunc` is
|
|
@@ -57,7 +56,7 @@ function naiveFirstDay(b) {
|
|
|
57
56
|
function bucketExpression(b) {
|
|
58
57
|
const source = `$${b.field}`;
|
|
59
58
|
const date = { $toDate: { $toLong: source } };
|
|
60
|
-
const format =
|
|
59
|
+
const format = LOCAL_LABEL_FORMATS[b.unit];
|
|
61
60
|
const label = format ? { $dateToString: {
|
|
62
61
|
date,
|
|
63
62
|
format,
|
|
@@ -100,10 +99,11 @@ function bucketExpression(b) {
|
|
|
100
99
|
* position in front of the filter `$match`. Resolving it needs adapter state,
|
|
101
100
|
* so the caller passes it in and this module stays a pure translation.
|
|
102
101
|
*/
|
|
103
|
-
function buildPrefix(query, searchStage) {
|
|
102
|
+
function buildPrefix(query, searchStage, filterOptions) {
|
|
104
103
|
const controls = query.controls || {};
|
|
105
104
|
const groupBy = controls.$groupBy ?? [];
|
|
106
|
-
const
|
|
105
|
+
const filterStages = mongoFilterStages(query.filter, filterOptions);
|
|
106
|
+
const pipeline = searchStage ? [searchStage, ...filterStages] : filterStages;
|
|
107
107
|
const groupId = {};
|
|
108
108
|
const groupKeys = [];
|
|
109
109
|
for (const [index, key] of groupBy.entries()) {
|
|
@@ -131,8 +131,8 @@ function buildPrefix(query, searchStage) {
|
|
|
131
131
|
* (no accumulators, no `$project`, no `$having`) — the cheapest shape for a
|
|
132
132
|
* plain group count, where no alias has to be resolvable.
|
|
133
133
|
*/
|
|
134
|
-
function buildGroupedStages(query, { accumulators, searchStage }) {
|
|
135
|
-
const { pipeline, groupId, groupKeys, controls } = buildPrefix(query, searchStage);
|
|
134
|
+
function buildGroupedStages(query, { accumulators, searchStage, filterOptions }) {
|
|
135
|
+
const { pipeline, groupId, groupKeys, controls } = buildPrefix(query, searchStage, filterOptions);
|
|
136
136
|
const groupStage = { _id: groupId };
|
|
137
137
|
if (!accumulators) {
|
|
138
138
|
pipeline.push({ $group: groupStage });
|
|
@@ -164,12 +164,14 @@ function buildGroupedStages(query, { accumulators, searchStage }) {
|
|
|
164
164
|
*
|
|
165
165
|
* `searchStage` is the resolved `$search` / `$text` stage (see
|
|
166
166
|
* `buildAggregateSearchStage`); unlike the leaf runner this path adds no
|
|
167
|
-
* relevance `$sort` and no default `$limit`.
|
|
167
|
+
* relevance `$sort` and no default `$limit`. `filterOptions` renders a
|
|
168
|
+
* filter with relational predicates (`mongoFilterStages`).
|
|
168
169
|
*/
|
|
169
|
-
function buildAggregatePipeline(query, searchStage) {
|
|
170
|
+
function buildAggregatePipeline(query, searchStage, filterOptions) {
|
|
170
171
|
const { pipeline, controls } = buildGroupedStages(query, {
|
|
171
172
|
accumulators: true,
|
|
172
|
-
searchStage
|
|
173
|
+
searchStage,
|
|
174
|
+
filterOptions
|
|
173
175
|
});
|
|
174
176
|
if (controls.$sort) pipeline.push({ $sort: controls.$sort });
|
|
175
177
|
if (controls.$skip) pipeline.push({ $skip: controls.$skip });
|
|
@@ -188,10 +190,11 @@ function buildAggregatePipeline(query, searchStage) {
|
|
|
188
190
|
* `searchStage` must be the SAME stage handed to `buildAggregatePipeline` for
|
|
189
191
|
* the same query — the counted groups are exactly the rows the search matched.
|
|
190
192
|
*/
|
|
191
|
-
function buildCountPipeline(query, searchStage) {
|
|
193
|
+
function buildCountPipeline(query, searchStage, filterOptions) {
|
|
192
194
|
const { pipeline } = buildGroupedStages(query, {
|
|
193
195
|
accumulators: Boolean(query.controls?.$having),
|
|
194
|
-
searchStage
|
|
196
|
+
searchStage,
|
|
197
|
+
filterOptions
|
|
195
198
|
});
|
|
196
199
|
pipeline.push({ $count: "count" });
|
|
197
200
|
return pipeline;
|