@atscript/db-mongo 0.1.142 → 0.1.143

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/index.cjs CHANGED
@@ -569,12 +569,14 @@ function buildLookupInnerPipeline(withRel, requiredFields) {
569
569
  if (sort) pipeline.push({ $sort: sort });
570
570
  if (skip) pipeline.push({ $skip: skip });
571
571
  if (limit !== null && limit !== void 0) pipeline.push({ $limit: limit });
572
- if (select) {
573
- const projection = {};
574
- for (const f of select) projection[f] = 1;
575
- for (const f of requiredFields) projection[f] = 1;
576
- if (!select.includes("_id") && !requiredFields.includes("_id")) projection["_id"] = 0;
577
- pipeline.push({ $project: dedupeProjection(projection) });
572
+ const projection = select ? new _atscript_db.UniquSelect(select).asProjection : void 0;
573
+ if (projection) {
574
+ const project = { ...projection };
575
+ if (Object.values(project).some((flag) => flag === 1 || flag === true)) {
576
+ for (const f of requiredFields) project[f] = 1;
577
+ if (!("_id" in projection) && !requiredFields.includes("_id")) project._id = 0;
578
+ } else for (const f of requiredFields) delete project[f];
579
+ if (Object.keys(project).length > 0) pipeline.push({ $project: dedupeProjection(project) });
578
580
  }
579
581
  return pipeline;
580
582
  }
@@ -642,16 +644,39 @@ function findRemoteFK(target, thisTableName, alias) {
642
644
  }
643
645
  //#endregion
644
646
  //#region src/lib/mongo-search.ts
645
- /** Returns available search indexes as generic metadata for UI. */
647
+ /**
648
+ * Returns available search indexes as generic metadata for UI. The text
649
+ * default is the `DEFAULT` entry (an alias of the index `search()` answers
650
+ * with when none is named); the vector default is the first vector index.
651
+ */
646
652
  function getSearchIndexesImpl(host) {
647
653
  const result = [];
648
- for (const [name, index] of host.getMongoSearchIndexes()) result.push({
649
- name,
650
- description: `${index.type} index`,
651
- type: index.type === "vector" ? "vector" : "text"
652
- });
654
+ let firstVector = true;
655
+ for (const [name, index] of host.getMongoSearchIndexes()) {
656
+ const isVector = index.type === "vector";
657
+ result.push({
658
+ name,
659
+ description: `${index.type} index`,
660
+ type: isVector ? "vector" : "text",
661
+ fields: searchIndexPaths(host, index),
662
+ isDefault: isVector ? firstVector : name === DEFAULT_INDEX_NAME
663
+ });
664
+ if (isVector) firstVector = false;
665
+ }
653
666
  return result;
654
667
  }
668
+ /**
669
+ * LOGICAL paths an index reads: a classic text index by its `@db.index.fulltext`
670
+ * definition, an Atlas index by the paths it maps; `undefined` (every field)
671
+ * for a dynamic mapping or an index whose fields cannot be told.
672
+ */
673
+ function searchIndexPaths(host, index) {
674
+ if (index.type === "text") {
675
+ const source = host._table.indexes.get(index.key);
676
+ return source ? host._indexLogicalPaths(source) : void 0;
677
+ }
678
+ return "paths" in index && index.paths ? [...index.paths] : void 0;
679
+ }
655
680
  /** Checks if any vector search index is available. */
656
681
  function isVectorSearchableImpl(host) {
657
682
  for (const index of host.getMongoSearchIndexes().values()) if (index.type === "vector") return true;
@@ -663,7 +688,7 @@ function isVectorSearchableImpl(host) {
663
688
  */
664
689
  function requireSearchStage(host, text, indexName, controls) {
665
690
  const plan = buildSearchStage(host, text, indexName, controls);
666
- if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
691
+ if (!plan) throw new Error((0, _atscript_db.searchIndexNotFoundMessage)(indexName));
667
692
  return plan;
668
693
  }
669
694
  /**
@@ -759,7 +784,7 @@ function resolveGeoKeyPath(host, indexName) {
759
784
  const field = (indexName ? geoIndexes.find((candidate) => candidate.name === indexName) : geoIndexes[0])?.fields[0]?.name;
760
785
  if (!field) throw new _atscript_db.DbError("GEO_INDEX_MISSING", [{
761
786
  path: indexName ?? "",
762
- message: `No geo index${indexName ? ` "${indexName}"` : ""} on "${host._table.tableName}"`
787
+ message: (0, _atscript_db.geoIndexNotFoundMessage)(host._table.tableName, indexName)
763
788
  }]);
764
789
  return field;
765
790
  }
@@ -935,13 +960,13 @@ function buildVectorSearchStage(host, vector, indexName, limit) {
935
960
  let index;
936
961
  if (indexName) {
937
962
  const found = host.getMongoSearchIndex(indexName);
938
- if (!found || found.type !== "vector") throw new Error(`Vector index "${indexName}" not found`);
963
+ if (!found || found.type !== "vector") throw new Error((0, _atscript_db.vectorIndexNotFoundMessage)(indexName));
939
964
  index = found;
940
965
  } else for (const idx of host.getMongoSearchIndexes().values()) if (idx.type === "vector") {
941
966
  index = idx;
942
967
  break;
943
968
  }
944
- if (!index) throw new Error("No vector index available");
969
+ if (!index) throw new Error((0, _atscript_db.vectorIndexNotFoundMessage)());
945
970
  let vectorField;
946
971
  if (index.definition.fields) {
947
972
  for (const f of index.definition.fields) if (f.type === "vector") {
@@ -1975,7 +2000,7 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
1975
2000
  path: field,
1976
2001
  similarity: vectorIndex.similarity || "cosine",
1977
2002
  numDimensions: vectorIndex.dimensions
1978
- }] });
2003
+ }] }, { paths: [field] });
1979
2004
  const threshold = metadata.get("db.search.vector.threshold");
1980
2005
  if (threshold !== void 0) this._vectorThresholds.set(mongoIndexKey("vector", indexName), threshold);
1981
2006
  }
@@ -2461,7 +2486,8 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
2461
2486
  type,
2462
2487
  definition,
2463
2488
  fuzzy: meta?.fuzzy,
2464
- strategy: meta?.strategy
2489
+ strategy: meta?.strategy,
2490
+ paths: meta?.paths ?? (type === "search_text" ? [] : void 0)
2465
2491
  };
2466
2492
  this._mongoIndexes.set(key, index);
2467
2493
  return index;
@@ -2478,6 +2504,7 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
2478
2504
  let index = this._mongoIndexes.get(mongoIndexKey(type, name));
2479
2505
  if (!index && type === "search_text") index = this._setSearchIndex(type, name, { mappings: { fields: {} } });
2480
2506
  if (!index) return;
2507
+ if (index.paths && !index.paths.includes(fieldName)) index.paths.push(fieldName);
2481
2508
  const rootFields = index.definition.mappings.fields;
2482
2509
  const segments = fieldName.split(".");
2483
2510
  const topKey = this._table.columnMap.get(segments[0]) ?? segments[0];
package/dist/index.d.cts CHANGED
@@ -185,6 +185,12 @@ interface TSearchIndex {
185
185
  * - `text` — single `text` operator over all string-mapped fields.
186
186
  */
187
187
  strategy?: "compound" | "autocomplete" | "text";
188
+ /**
189
+ * LOGICAL paths of the fields the index maps (search_text) or embeds
190
+ * (vector); absent for a dynamic mapping (every field). Reported as
191
+ * `TSearchIndexInfo.fields` (since 0.1.143).
192
+ */
193
+ paths?: string[];
188
194
  }
189
195
  type TMongoIndex = TPlainIndex | TSearchIndex;
190
196
  declare function mongoIndexKey(type: TMongoIndex["type"], name: string): string;
@@ -459,6 +465,7 @@ declare class MongoAdapter extends BaseDbAdapter {
459
465
  maxEdits: number;
460
466
  };
461
467
  strategy?: TSearchIndex["strategy"];
468
+ paths?: string[];
462
469
  }): TSearchIndex;
463
470
  /**
464
471
  * Adds (and merges, by mapping `type`) one or more Atlas field-type mappings to
package/dist/index.d.mts CHANGED
@@ -185,6 +185,12 @@ interface TSearchIndex {
185
185
  * - `text` — single `text` operator over all string-mapped fields.
186
186
  */
187
187
  strategy?: "compound" | "autocomplete" | "text";
188
+ /**
189
+ * LOGICAL paths of the fields the index maps (search_text) or embeds
190
+ * (vector); absent for a dynamic mapping (every field). Reported as
191
+ * `TSearchIndexInfo.fields` (since 0.1.143).
192
+ */
193
+ paths?: string[];
188
194
  }
189
195
  type TMongoIndex = TPlainIndex | TSearchIndex;
190
196
  declare function mongoIndexKey(type: TMongoIndex["type"], name: string): string;
@@ -459,6 +465,7 @@ declare class MongoAdapter extends BaseDbAdapter {
459
465
  maxEdits: number;
460
466
  };
461
467
  strategy?: TSearchIndex["strategy"];
468
+ paths?: string[];
462
469
  }): TSearchIndex;
463
470
  /**
464
471
  * Adds (and merges, by mapping `type`) one or more Atlas field-type mappings to
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as queryNodeToExpr, i as orNull, n as distinctCountExpr, r as buildMongoFilter, t as buildAccumulator } from "./mongo-accumulator-Dc0w4t0l.mjs";
2
- import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, BaseDbAdapter, DbError, DbSpace, bucketTimeZoneUnavailable, computeInsights, createFailureCollector, getDbFieldOp, getKeyProps, isAtscriptDbView, isFieldRef, resolveDesignType, tableNameOf, translateQueryTree } from "@atscript/db";
2
+ import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, BaseDbAdapter, DbError, DbSpace, UniquSelect, bucketTimeZoneUnavailable, computeInsights, createFailureCollector, geoIndexNotFoundMessage, getDbFieldOp, getKeyProps, isAtscriptDbView, isFieldRef, resolveDesignType, searchIndexNotFoundMessage, tableNameOf, translateQueryTree, vectorIndexNotFoundMessage } from "@atscript/db";
3
3
  import { MongoClient, MongoServerError, ObjectId } from "mongodb";
4
4
  import { resolveAggregateSearch } from "@atscript/db/agg";
5
5
  //#region src/lib/path-utils.ts
@@ -568,12 +568,14 @@ function buildLookupInnerPipeline(withRel, requiredFields) {
568
568
  if (sort) pipeline.push({ $sort: sort });
569
569
  if (skip) pipeline.push({ $skip: skip });
570
570
  if (limit !== null && limit !== void 0) pipeline.push({ $limit: limit });
571
- if (select) {
572
- const projection = {};
573
- for (const f of select) projection[f] = 1;
574
- for (const f of requiredFields) projection[f] = 1;
575
- if (!select.includes("_id") && !requiredFields.includes("_id")) projection["_id"] = 0;
576
- pipeline.push({ $project: dedupeProjection(projection) });
571
+ const projection = select ? new UniquSelect(select).asProjection : void 0;
572
+ if (projection) {
573
+ const project = { ...projection };
574
+ if (Object.values(project).some((flag) => flag === 1 || flag === true)) {
575
+ for (const f of requiredFields) project[f] = 1;
576
+ if (!("_id" in projection) && !requiredFields.includes("_id")) project._id = 0;
577
+ } else for (const f of requiredFields) delete project[f];
578
+ if (Object.keys(project).length > 0) pipeline.push({ $project: dedupeProjection(project) });
577
579
  }
578
580
  return pipeline;
579
581
  }
@@ -641,16 +643,39 @@ function findRemoteFK(target, thisTableName, alias) {
641
643
  }
642
644
  //#endregion
643
645
  //#region src/lib/mongo-search.ts
644
- /** Returns available search indexes as generic metadata for UI. */
646
+ /**
647
+ * Returns available search indexes as generic metadata for UI. The text
648
+ * default is the `DEFAULT` entry (an alias of the index `search()` answers
649
+ * with when none is named); the vector default is the first vector index.
650
+ */
645
651
  function getSearchIndexesImpl(host) {
646
652
  const result = [];
647
- for (const [name, index] of host.getMongoSearchIndexes()) result.push({
648
- name,
649
- description: `${index.type} index`,
650
- type: index.type === "vector" ? "vector" : "text"
651
- });
653
+ let firstVector = true;
654
+ for (const [name, index] of host.getMongoSearchIndexes()) {
655
+ const isVector = index.type === "vector";
656
+ result.push({
657
+ name,
658
+ description: `${index.type} index`,
659
+ type: isVector ? "vector" : "text",
660
+ fields: searchIndexPaths(host, index),
661
+ isDefault: isVector ? firstVector : name === DEFAULT_INDEX_NAME
662
+ });
663
+ if (isVector) firstVector = false;
664
+ }
652
665
  return result;
653
666
  }
667
+ /**
668
+ * LOGICAL paths an index reads: a classic text index by its `@db.index.fulltext`
669
+ * definition, an Atlas index by the paths it maps; `undefined` (every field)
670
+ * for a dynamic mapping or an index whose fields cannot be told.
671
+ */
672
+ function searchIndexPaths(host, index) {
673
+ if (index.type === "text") {
674
+ const source = host._table.indexes.get(index.key);
675
+ return source ? host._indexLogicalPaths(source) : void 0;
676
+ }
677
+ return "paths" in index && index.paths ? [...index.paths] : void 0;
678
+ }
654
679
  /** Checks if any vector search index is available. */
655
680
  function isVectorSearchableImpl(host) {
656
681
  for (const index of host.getMongoSearchIndexes().values()) if (index.type === "vector") return true;
@@ -662,7 +687,7 @@ function isVectorSearchableImpl(host) {
662
687
  */
663
688
  function requireSearchStage(host, text, indexName, controls) {
664
689
  const plan = buildSearchStage(host, text, indexName, controls);
665
- if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
690
+ if (!plan) throw new Error(searchIndexNotFoundMessage(indexName));
666
691
  return plan;
667
692
  }
668
693
  /**
@@ -758,7 +783,7 @@ function resolveGeoKeyPath(host, indexName) {
758
783
  const field = (indexName ? geoIndexes.find((candidate) => candidate.name === indexName) : geoIndexes[0])?.fields[0]?.name;
759
784
  if (!field) throw new DbError("GEO_INDEX_MISSING", [{
760
785
  path: indexName ?? "",
761
- message: `No geo index${indexName ? ` "${indexName}"` : ""} on "${host._table.tableName}"`
786
+ message: geoIndexNotFoundMessage(host._table.tableName, indexName)
762
787
  }]);
763
788
  return field;
764
789
  }
@@ -934,13 +959,13 @@ function buildVectorSearchStage(host, vector, indexName, limit) {
934
959
  let index;
935
960
  if (indexName) {
936
961
  const found = host.getMongoSearchIndex(indexName);
937
- if (!found || found.type !== "vector") throw new Error(`Vector index "${indexName}" not found`);
962
+ if (!found || found.type !== "vector") throw new Error(vectorIndexNotFoundMessage(indexName));
938
963
  index = found;
939
964
  } else for (const idx of host.getMongoSearchIndexes().values()) if (idx.type === "vector") {
940
965
  index = idx;
941
966
  break;
942
967
  }
943
- if (!index) throw new Error("No vector index available");
968
+ if (!index) throw new Error(vectorIndexNotFoundMessage());
944
969
  let vectorField;
945
970
  if (index.definition.fields) {
946
971
  for (const f of index.definition.fields) if (f.type === "vector") {
@@ -1974,7 +1999,7 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
1974
1999
  path: field,
1975
2000
  similarity: vectorIndex.similarity || "cosine",
1976
2001
  numDimensions: vectorIndex.dimensions
1977
- }] });
2002
+ }] }, { paths: [field] });
1978
2003
  const threshold = metadata.get("db.search.vector.threshold");
1979
2004
  if (threshold !== void 0) this._vectorThresholds.set(mongoIndexKey("vector", indexName), threshold);
1980
2005
  }
@@ -2460,7 +2485,8 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
2460
2485
  type,
2461
2486
  definition,
2462
2487
  fuzzy: meta?.fuzzy,
2463
- strategy: meta?.strategy
2488
+ strategy: meta?.strategy,
2489
+ paths: meta?.paths ?? (type === "search_text" ? [] : void 0)
2464
2490
  };
2465
2491
  this._mongoIndexes.set(key, index);
2466
2492
  return index;
@@ -2477,6 +2503,7 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
2477
2503
  let index = this._mongoIndexes.get(mongoIndexKey(type, name));
2478
2504
  if (!index && type === "search_text") index = this._setSearchIndex(type, name, { mappings: { fields: {} } });
2479
2505
  if (!index) return;
2506
+ if (index.paths && !index.paths.includes(fieldName)) index.paths.push(fieldName);
2480
2507
  const rootFields = index.definition.mappings.fields;
2481
2508
  const segments = fieldName.split(".");
2482
2509
  const topKey = this._table.columnMap.get(segments[0]) ?? segments[0];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db-mongo",
3
- "version": "0.1.142",
3
+ "version": "0.1.143",
4
4
  "description": "Mongodb plugin for atscript.",
5
5
  "keywords": [
6
6
  "atscript",
@@ -58,7 +58,7 @@
58
58
  "@atscript/typescript": "^0.1.95",
59
59
  "@uniqu/core": "^0.1.11",
60
60
  "mongodb": "^6.17.0",
61
- "@atscript/db": "^0.1.142"
61
+ "@atscript/db": "^0.1.143"
62
62
  },
63
63
  "scripts": {
64
64
  "postinstall": "asc -f dts",