@atscript/db-mongo 0.1.145 → 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 CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_mongo_accumulator = require("./mongo-accumulator-BalE9LkU.cjs");
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
- * Units whose first day `$dateToString` renders straight from the instant in
14
- * the zone: the local day, then the literal `01` for day-of-month / month.
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`, or `null`.
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 = LOCAL_DATE_FORMATS[b.unit];
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 pipeline = searchStage ? [searchStage, { $match: require_mongo_accumulator.buildMongoFilter(query.filter) }] : [{ $match: require_mongo_accumulator.buildMongoFilter(query.filter) }];
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`, or `null`.
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`, or `null`.
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 orNull, n as distinctCountExpr, r as buildMongoFilter, t as buildAccumulator } from "./mongo-accumulator-Dc0w4t0l.mjs";
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
- * Units whose first day `$dateToString` renders straight from the instant in
13
- * the zone: the local day, then the literal `01` for day-of-month / month.
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`, or `null`.
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 = LOCAL_DATE_FORMATS[b.unit];
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 pipeline = searchStage ? [searchStage, { $match: buildMongoFilter(query.filter) }] : [{ $match: buildMongoFilter(query.filter) }];
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;