@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 +45 -18
- package/dist/index.d.cts +7 -0
- package/dist/index.d.mts +7 -0
- package/dist/index.mjs +46 -19
- package/package.json +2 -2
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
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(
|
|
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:
|
|
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(
|
|
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(
|
|
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
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
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
|
-
/**
|
|
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
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
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
|
|
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:
|
|
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(
|
|
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(
|
|
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.
|
|
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.
|
|
61
|
+
"@atscript/db": "^0.1.143"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"postinstall": "asc -f dts",
|