@atscript/db-mongo 0.1.128 → 0.1.130

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
@@ -37,14 +37,19 @@ function groupIdKey(field, index) {
37
37
  return field.includes(".") ? `k${index}` : field;
38
38
  }
39
39
  /**
40
- * Builds the common prefix stages: $match + $group._id from groupBy fields.
41
- * Shared by both full aggregate and count pipelines.
40
+ * Builds the common prefix stages: [search] + $match + $group._id from groupBy
41
+ * fields. Shared by both full aggregate and count pipelines.
42
42
  * `groupKeys` maps each `$groupBy` path to its `_id` sub-key.
43
+ *
44
+ * `searchStage` is the resolved text-search stage (classic `$text` `$match`, or
45
+ * an Atlas `$search`). Both MUST be the pipeline's FIRST stage, hence its
46
+ * position in front of the filter `$match`. Resolving it needs adapter state,
47
+ * so the caller passes it in and this module stays a pure translation.
43
48
  */
44
- function buildPrefix(query) {
49
+ function buildPrefix(query, searchStage) {
45
50
  const controls = query.controls || {};
46
51
  const groupBy = controls.$groupBy ?? [];
47
- const pipeline = [{ $match: require_mongo_filter.buildMongoFilter(query.filter) }];
52
+ const pipeline = searchStage ? [searchStage, { $match: require_mongo_filter.buildMongoFilter(query.filter) }] : [{ $match: require_mongo_filter.buildMongoFilter(query.filter) }];
48
53
  const groupId = {};
49
54
  const groupKeys = [];
50
55
  for (const [index, field] of groupBy.entries()) {
@@ -60,17 +65,30 @@ function buildPrefix(query) {
60
65
  };
61
66
  }
62
67
  /**
63
- * Builds a full MongoDB aggregation pipeline for GROUP BY queries.
68
+ * The stages every grouped query shares: `[search →] $match(filter)` →
69
+ * `$group` (dimensions + accumulators) → `$project` (flatten `_id`, keep
70
+ * aliases) → `$match($having)`. The row pipeline appends sort/skip/limit, the
71
+ * count pipeline appends `$count`, so both see exactly the same group set —
72
+ * including the same `$search` narrowing, which is why the stage is threaded
73
+ * through this single seam instead of being appended by each caller.
64
74
  *
65
- * Pipeline: $match → $group → $project → $match(having) → $sort → $skip → $limit
75
+ * With `accumulators: false` only the `$group._id` dimensions are emitted
76
+ * (no accumulators, no `$project`, no `$having`) — the cheapest shape for a
77
+ * plain group count, where no alias has to be resolvable.
66
78
  */
67
- function buildAggregatePipeline(query) {
68
- const { pipeline, groupId, groupKeys, controls } = buildPrefix(query);
79
+ function buildGroupedStages(query, { accumulators, searchStage }) {
80
+ const { pipeline, groupId, groupKeys, controls } = buildPrefix(query, searchStage);
69
81
  const groupStage = { _id: groupId };
82
+ if (!accumulators) {
83
+ pipeline.push({ $group: groupStage });
84
+ return {
85
+ pipeline,
86
+ controls
87
+ };
88
+ }
70
89
  const project = { _id: 0 };
71
- const aggregates = controls.$select?.aggregates;
72
90
  for (const [field, idKey] of groupKeys) project[field] = `$_id.${idKey}`;
73
- if (aggregates) for (const expr of aggregates) {
91
+ for (const expr of controls.$select?.aggregates ?? []) {
74
92
  const alias = (0, _atscript_db_agg.resolveAlias)(expr);
75
93
  groupStage[alias] = toAccumulator(expr);
76
94
  project[alias] = 1;
@@ -78,25 +96,48 @@ function buildAggregatePipeline(query) {
78
96
  pipeline.push({ $group: groupStage });
79
97
  pipeline.push({ $project: project });
80
98
  if (controls.$having) pipeline.push({ $match: require_mongo_filter.buildMongoFilter(controls.$having) });
99
+ return {
100
+ pipeline,
101
+ controls
102
+ };
103
+ }
104
+ /**
105
+ * Builds a full MongoDB aggregation pipeline for GROUP BY queries.
106
+ *
107
+ * Pipeline: [search →] $match → $group → $project → $match(having) → $sort →
108
+ * $skip → $limit
109
+ *
110
+ * `searchStage` is the resolved `$search` / `$text` stage (see
111
+ * `buildAggregateSearchStage`); unlike the leaf runner this path adds no
112
+ * relevance `$sort` and no default `$limit`.
113
+ */
114
+ function buildAggregatePipeline(query, searchStage) {
115
+ const { pipeline, controls } = buildGroupedStages(query, {
116
+ accumulators: true,
117
+ searchStage
118
+ });
81
119
  if (controls.$sort) pipeline.push({ $sort: controls.$sort });
82
120
  if (controls.$skip) pipeline.push({ $skip: controls.$skip });
83
121
  if (controls.$limit) pipeline.push({ $limit: controls.$limit });
84
122
  return pipeline;
85
123
  }
86
124
  /**
87
- * Builds a count-only pipeline: returns the number of distinct groups.
125
+ * Builds a count-only pipeline: the number of groups that survive `$having`
126
+ * (all groups when there is none). With `$having` it runs the same grouped
127
+ * stages as the row pipeline — the accumulators must run so an alias
128
+ * `$having` has a value to match; without it only the `$group._id`
129
+ * dimensions are needed.
130
+ *
131
+ * Pipeline: [search →] $match → $group → [$project → $match(having)] → $count
88
132
  *
89
- * Pipeline: $match → $group (just _id) → $project → $match(having) → $count
133
+ * `searchStage` must be the SAME stage handed to `buildAggregatePipeline` for
134
+ * the same query — the counted groups are exactly the rows the search matched.
90
135
  */
91
- function buildCountPipeline(query) {
92
- const { pipeline, groupId, groupKeys, controls } = buildPrefix(query);
93
- pipeline.push({ $group: { _id: groupId } });
94
- if (controls.$having) {
95
- const project = { _id: 0 };
96
- for (const [field, idKey] of groupKeys) project[field] = `$_id.${idKey}`;
97
- pipeline.push({ $project: project });
98
- pipeline.push({ $match: require_mongo_filter.buildMongoFilter(controls.$having) });
99
- }
136
+ function buildCountPipeline(query, searchStage) {
137
+ const { pipeline } = buildGroupedStages(query, {
138
+ accumulators: Boolean(query.controls?.$having),
139
+ searchStage
140
+ });
100
141
  pipeline.push({ $count: "count" });
101
142
  return pipeline;
102
143
  }
package/dist/agg.d.cts CHANGED
@@ -5,14 +5,26 @@ import { Document } from "mongodb";
5
5
  /**
6
6
  * Builds a full MongoDB aggregation pipeline for GROUP BY queries.
7
7
  *
8
- * Pipeline: $match → $group → $project → $match(having) → $sort → $skip → $limit
8
+ * Pipeline: [search →] $match → $group → $project → $match(having) → $sort →
9
+ * $skip → $limit
10
+ *
11
+ * `searchStage` is the resolved `$search` / `$text` stage (see
12
+ * `buildAggregateSearchStage`); unlike the leaf runner this path adds no
13
+ * relevance `$sort` and no default `$limit`.
9
14
  */
10
- declare function buildAggregatePipeline(query: DbQuery): Document[];
15
+ declare function buildAggregatePipeline(query: DbQuery, searchStage?: Document): Document[];
11
16
  /**
12
- * Builds a count-only pipeline: returns the number of distinct groups.
17
+ * Builds a count-only pipeline: the number of groups that survive `$having`
18
+ * (all groups when there is none). With `$having` it runs the same grouped
19
+ * stages as the row pipeline — the accumulators must run so an alias
20
+ * `$having` has a value to match; without it only the `$group._id`
21
+ * dimensions are needed.
22
+ *
23
+ * Pipeline: [search →] $match → $group → [$project → $match(having)] → $count
13
24
  *
14
- * Pipeline: $match → $group (just _id) → $project → $match(having) → $count
25
+ * `searchStage` must be the SAME stage handed to `buildAggregatePipeline` for
26
+ * the same query — the counted groups are exactly the rows the search matched.
15
27
  */
16
- declare function buildCountPipeline(query: DbQuery): Document[];
28
+ declare function buildCountPipeline(query: DbQuery, searchStage?: Document): Document[];
17
29
  //#endregion
18
30
  export { buildAggregatePipeline, buildCountPipeline };
package/dist/agg.d.mts CHANGED
@@ -5,14 +5,26 @@ import { Document } from "mongodb";
5
5
  /**
6
6
  * Builds a full MongoDB aggregation pipeline for GROUP BY queries.
7
7
  *
8
- * Pipeline: $match → $group → $project → $match(having) → $sort → $skip → $limit
8
+ * Pipeline: [search →] $match → $group → $project → $match(having) → $sort →
9
+ * $skip → $limit
10
+ *
11
+ * `searchStage` is the resolved `$search` / `$text` stage (see
12
+ * `buildAggregateSearchStage`); unlike the leaf runner this path adds no
13
+ * relevance `$sort` and no default `$limit`.
9
14
  */
10
- declare function buildAggregatePipeline(query: DbQuery): Document[];
15
+ declare function buildAggregatePipeline(query: DbQuery, searchStage?: Document): Document[];
11
16
  /**
12
- * Builds a count-only pipeline: returns the number of distinct groups.
17
+ * Builds a count-only pipeline: the number of groups that survive `$having`
18
+ * (all groups when there is none). With `$having` it runs the same grouped
19
+ * stages as the row pipeline — the accumulators must run so an alias
20
+ * `$having` has a value to match; without it only the `$group._id`
21
+ * dimensions are needed.
22
+ *
23
+ * Pipeline: [search →] $match → $group → [$project → $match(having)] → $count
13
24
  *
14
- * Pipeline: $match → $group (just _id) → $project → $match(having) → $count
25
+ * `searchStage` must be the SAME stage handed to `buildAggregatePipeline` for
26
+ * the same query — the counted groups are exactly the rows the search matched.
15
27
  */
16
- declare function buildCountPipeline(query: DbQuery): Document[];
28
+ declare function buildCountPipeline(query: DbQuery, searchStage?: Document): Document[];
17
29
  //#endregion
18
30
  export { buildAggregatePipeline, buildCountPipeline };
package/dist/agg.mjs CHANGED
@@ -36,14 +36,19 @@ function groupIdKey(field, index) {
36
36
  return field.includes(".") ? `k${index}` : field;
37
37
  }
38
38
  /**
39
- * Builds the common prefix stages: $match + $group._id from groupBy fields.
40
- * Shared by both full aggregate and count pipelines.
39
+ * Builds the common prefix stages: [search] + $match + $group._id from groupBy
40
+ * fields. Shared by both full aggregate and count pipelines.
41
41
  * `groupKeys` maps each `$groupBy` path to its `_id` sub-key.
42
+ *
43
+ * `searchStage` is the resolved text-search stage (classic `$text` `$match`, or
44
+ * an Atlas `$search`). Both MUST be the pipeline's FIRST stage, hence its
45
+ * position in front of the filter `$match`. Resolving it needs adapter state,
46
+ * so the caller passes it in and this module stays a pure translation.
42
47
  */
43
- function buildPrefix(query) {
48
+ function buildPrefix(query, searchStage) {
44
49
  const controls = query.controls || {};
45
50
  const groupBy = controls.$groupBy ?? [];
46
- const pipeline = [{ $match: buildMongoFilter(query.filter) }];
51
+ const pipeline = searchStage ? [searchStage, { $match: buildMongoFilter(query.filter) }] : [{ $match: buildMongoFilter(query.filter) }];
47
52
  const groupId = {};
48
53
  const groupKeys = [];
49
54
  for (const [index, field] of groupBy.entries()) {
@@ -59,17 +64,30 @@ function buildPrefix(query) {
59
64
  };
60
65
  }
61
66
  /**
62
- * Builds a full MongoDB aggregation pipeline for GROUP BY queries.
67
+ * The stages every grouped query shares: `[search →] $match(filter)` →
68
+ * `$group` (dimensions + accumulators) → `$project` (flatten `_id`, keep
69
+ * aliases) → `$match($having)`. The row pipeline appends sort/skip/limit, the
70
+ * count pipeline appends `$count`, so both see exactly the same group set —
71
+ * including the same `$search` narrowing, which is why the stage is threaded
72
+ * through this single seam instead of being appended by each caller.
63
73
  *
64
- * Pipeline: $match → $group → $project → $match(having) → $sort → $skip → $limit
74
+ * With `accumulators: false` only the `$group._id` dimensions are emitted
75
+ * (no accumulators, no `$project`, no `$having`) — the cheapest shape for a
76
+ * plain group count, where no alias has to be resolvable.
65
77
  */
66
- function buildAggregatePipeline(query) {
67
- const { pipeline, groupId, groupKeys, controls } = buildPrefix(query);
78
+ function buildGroupedStages(query, { accumulators, searchStage }) {
79
+ const { pipeline, groupId, groupKeys, controls } = buildPrefix(query, searchStage);
68
80
  const groupStage = { _id: groupId };
81
+ if (!accumulators) {
82
+ pipeline.push({ $group: groupStage });
83
+ return {
84
+ pipeline,
85
+ controls
86
+ };
87
+ }
69
88
  const project = { _id: 0 };
70
- const aggregates = controls.$select?.aggregates;
71
89
  for (const [field, idKey] of groupKeys) project[field] = `$_id.${idKey}`;
72
- if (aggregates) for (const expr of aggregates) {
90
+ for (const expr of controls.$select?.aggregates ?? []) {
73
91
  const alias = resolveAlias(expr);
74
92
  groupStage[alias] = toAccumulator(expr);
75
93
  project[alias] = 1;
@@ -77,25 +95,48 @@ function buildAggregatePipeline(query) {
77
95
  pipeline.push({ $group: groupStage });
78
96
  pipeline.push({ $project: project });
79
97
  if (controls.$having) pipeline.push({ $match: buildMongoFilter(controls.$having) });
98
+ return {
99
+ pipeline,
100
+ controls
101
+ };
102
+ }
103
+ /**
104
+ * Builds a full MongoDB aggregation pipeline for GROUP BY queries.
105
+ *
106
+ * Pipeline: [search →] $match → $group → $project → $match(having) → $sort →
107
+ * $skip → $limit
108
+ *
109
+ * `searchStage` is the resolved `$search` / `$text` stage (see
110
+ * `buildAggregateSearchStage`); unlike the leaf runner this path adds no
111
+ * relevance `$sort` and no default `$limit`.
112
+ */
113
+ function buildAggregatePipeline(query, searchStage) {
114
+ const { pipeline, controls } = buildGroupedStages(query, {
115
+ accumulators: true,
116
+ searchStage
117
+ });
80
118
  if (controls.$sort) pipeline.push({ $sort: controls.$sort });
81
119
  if (controls.$skip) pipeline.push({ $skip: controls.$skip });
82
120
  if (controls.$limit) pipeline.push({ $limit: controls.$limit });
83
121
  return pipeline;
84
122
  }
85
123
  /**
86
- * Builds a count-only pipeline: returns the number of distinct groups.
124
+ * Builds a count-only pipeline: the number of groups that survive `$having`
125
+ * (all groups when there is none). With `$having` it runs the same grouped
126
+ * stages as the row pipeline — the accumulators must run so an alias
127
+ * `$having` has a value to match; without it only the `$group._id`
128
+ * dimensions are needed.
129
+ *
130
+ * Pipeline: [search →] $match → $group → [$project → $match(having)] → $count
87
131
  *
88
- * Pipeline: $match → $group (just _id) → $project → $match(having) → $count
132
+ * `searchStage` must be the SAME stage handed to `buildAggregatePipeline` for
133
+ * the same query — the counted groups are exactly the rows the search matched.
89
134
  */
90
- function buildCountPipeline(query) {
91
- const { pipeline, groupId, groupKeys, controls } = buildPrefix(query);
92
- pipeline.push({ $group: { _id: groupId } });
93
- if (controls.$having) {
94
- const project = { _id: 0 };
95
- for (const [field, idKey] of groupKeys) project[field] = `$_id.${idKey}`;
96
- pipeline.push({ $project: project });
97
- pipeline.push({ $match: buildMongoFilter(controls.$having) });
98
- }
135
+ function buildCountPipeline(query, searchStage) {
136
+ const { pipeline } = buildGroupedStages(query, {
137
+ accumulators: Boolean(query.controls?.$having),
138
+ searchStage
139
+ });
99
140
  pipeline.push({ $count: "count" });
100
141
  return pipeline;
101
142
  }
package/dist/index.cjs CHANGED
@@ -2,6 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_mongo_filter = require("./mongo-filter-z_hLPMyv.cjs");
3
3
  let _atscript_db = require("@atscript/db");
4
4
  let mongodb = require("mongodb");
5
+ let _atscript_db_agg = require("@atscript/db/agg");
5
6
  //#region src/lib/path-utils.ts
6
7
  function joinPath(prefix, segment) {
7
8
  return prefix ? `${prefix}.${segment}` : segment;
@@ -648,16 +649,39 @@ function isVectorSearchableImpl(host) {
648
649
  for (const index of host.getMongoSearchIndexes().values()) if (index.type === "vector") return true;
649
650
  return false;
650
651
  }
652
+ /**
653
+ * Resolves the leading stage for a text search, or throws when no index
654
+ * answers to `indexName` (the default index when it is omitted).
655
+ */
656
+ function requireSearchStage(host, text, indexName, controls) {
657
+ const plan = buildSearchStage(host, text, indexName, controls);
658
+ if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
659
+ return plan;
660
+ }
661
+ /**
662
+ * Resolves the leading search stage for a GROUPED aggregate query, or
663
+ * `undefined` when the query carries no `$search` term (the plain grouped
664
+ * path). `$index` picks the search index by the same rules as `search()`, and
665
+ * an unresolvable one throws exactly as the leaf path does.
666
+ *
667
+ * `classicText` is deliberately dropped: it only drives the leaf runner's
668
+ * `_score` projection and relevance `$sort`, and no per-document score
669
+ * survives `$group`. See `resolveAggregateSearch` in `@atscript/db/agg` for
670
+ * the normative cross-adapter contract.
671
+ */
672
+ function buildAggregateSearchStage(host, controls) {
673
+ const search = (0, _atscript_db_agg.resolveAggregateSearch)(controls);
674
+ if (!search) return;
675
+ return requireSearchStage(host, search.text, search.indexName, controls).stage;
676
+ }
651
677
  /** Text search via $search aggregation stage. */
652
678
  async function searchImpl(host, text, query, indexName) {
653
- const plan = buildSearchStage(host, text, indexName, query.controls);
654
- if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
679
+ const plan = requireSearchStage(host, text, indexName, query.controls);
655
680
  return runSearchPipeline(host, plan.stage, query, "search", void 0, plan.classicText);
656
681
  }
657
682
  /** Text search with faceted count. */
658
683
  async function searchWithCountImpl(host, text, query, indexName) {
659
- const plan = buildSearchStage(host, text, indexName, query.controls);
660
- if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
684
+ const plan = requireSearchStage(host, text, indexName, query.controls);
661
685
  return runSearchWithCountPipeline(host, plan.stage, query, "searchWithCount", void 0, plan.classicText);
662
686
  }
663
687
  /** Vector search via $vectorSearch aggregation stage. */
@@ -1681,13 +1705,14 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
1681
1705
  }
1682
1706
  async aggregate(query) {
1683
1707
  const { buildAggregatePipeline, buildCountPipeline } = await Promise.resolve().then(() => require("./agg.cjs"));
1708
+ const searchStage = buildAggregateSearchStage(this, query.controls);
1684
1709
  if (query.controls?.$count) {
1685
- const pipeline = buildCountPipeline(query);
1710
+ const pipeline = buildCountPipeline(query, searchStage);
1686
1711
  this._log("aggregate (count)", pipeline);
1687
1712
  const result = await wrapInvalidQuery(() => this.aggregatePipeline(pipeline).toArray());
1688
1713
  return result.length > 0 ? result : [{ count: 0 }];
1689
1714
  }
1690
- const pipeline = buildAggregatePipeline(query);
1715
+ const pipeline = buildAggregatePipeline(query, searchStage);
1691
1716
  this._log("aggregate", pipeline);
1692
1717
  return wrapInvalidQuery(() => this.aggregatePipeline(pipeline).toArray());
1693
1718
  }
package/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { t as buildMongoFilter } from "./mongo-filter-DceAGI-S.mjs";
2
2
  import { BaseDbAdapter, DbError, DbSpace, computeInsights, createFailureCollector, getDbFieldOp, getKeyProps, isAtscriptDbView, resolveDesignType } from "@atscript/db";
3
3
  import { MongoClient, MongoServerError, ObjectId } from "mongodb";
4
+ import { resolveAggregateSearch } from "@atscript/db/agg";
4
5
  //#region src/lib/path-utils.ts
5
6
  function joinPath(prefix, segment) {
6
7
  return prefix ? `${prefix}.${segment}` : segment;
@@ -647,16 +648,39 @@ function isVectorSearchableImpl(host) {
647
648
  for (const index of host.getMongoSearchIndexes().values()) if (index.type === "vector") return true;
648
649
  return false;
649
650
  }
651
+ /**
652
+ * Resolves the leading stage for a text search, or throws when no index
653
+ * answers to `indexName` (the default index when it is omitted).
654
+ */
655
+ function requireSearchStage(host, text, indexName, controls) {
656
+ const plan = buildSearchStage(host, text, indexName, controls);
657
+ if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
658
+ return plan;
659
+ }
660
+ /**
661
+ * Resolves the leading search stage for a GROUPED aggregate query, or
662
+ * `undefined` when the query carries no `$search` term (the plain grouped
663
+ * path). `$index` picks the search index by the same rules as `search()`, and
664
+ * an unresolvable one throws exactly as the leaf path does.
665
+ *
666
+ * `classicText` is deliberately dropped: it only drives the leaf runner's
667
+ * `_score` projection and relevance `$sort`, and no per-document score
668
+ * survives `$group`. See `resolveAggregateSearch` in `@atscript/db/agg` for
669
+ * the normative cross-adapter contract.
670
+ */
671
+ function buildAggregateSearchStage(host, controls) {
672
+ const search = resolveAggregateSearch(controls);
673
+ if (!search) return;
674
+ return requireSearchStage(host, search.text, search.indexName, controls).stage;
675
+ }
650
676
  /** Text search via $search aggregation stage. */
651
677
  async function searchImpl(host, text, query, indexName) {
652
- const plan = buildSearchStage(host, text, indexName, query.controls);
653
- if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
678
+ const plan = requireSearchStage(host, text, indexName, query.controls);
654
679
  return runSearchPipeline(host, plan.stage, query, "search", void 0, plan.classicText);
655
680
  }
656
681
  /** Text search with faceted count. */
657
682
  async function searchWithCountImpl(host, text, query, indexName) {
658
- const plan = buildSearchStage(host, text, indexName, query.controls);
659
- if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
683
+ const plan = requireSearchStage(host, text, indexName, query.controls);
660
684
  return runSearchWithCountPipeline(host, plan.stage, query, "searchWithCount", void 0, plan.classicText);
661
685
  }
662
686
  /** Vector search via $vectorSearch aggregation stage. */
@@ -1680,13 +1704,14 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
1680
1704
  }
1681
1705
  async aggregate(query) {
1682
1706
  const { buildAggregatePipeline, buildCountPipeline } = await import("./agg.mjs");
1707
+ const searchStage = buildAggregateSearchStage(this, query.controls);
1683
1708
  if (query.controls?.$count) {
1684
- const pipeline = buildCountPipeline(query);
1709
+ const pipeline = buildCountPipeline(query, searchStage);
1685
1710
  this._log("aggregate (count)", pipeline);
1686
1711
  const result = await wrapInvalidQuery(() => this.aggregatePipeline(pipeline).toArray());
1687
1712
  return result.length > 0 ? result : [{ count: 0 }];
1688
1713
  }
1689
- const pipeline = buildAggregatePipeline(query);
1714
+ const pipeline = buildAggregatePipeline(query, searchStage);
1690
1715
  this._log("aggregate", pipeline);
1691
1716
  return wrapInvalidQuery(() => this.aggregatePipeline(pipeline).toArray());
1692
1717
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db-mongo",
3
- "version": "0.1.128",
3
+ "version": "0.1.130",
4
4
  "description": "Mongodb plugin for atscript.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -56,7 +56,7 @@
56
56
  "@atscript/core": "^0.1.92",
57
57
  "@atscript/typescript": "^0.1.92",
58
58
  "mongodb": "^6.17.0",
59
- "@atscript/db": "^0.1.128"
59
+ "@atscript/db": "^0.1.130"
60
60
  },
61
61
  "scripts": {
62
62
  "postinstall": "asc -f dts",