@atscript/db-mongo 0.1.97 → 0.1.99
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 +43 -24
- package/dist/index.mjs +43 -24
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -630,15 +630,15 @@ function isVectorSearchableImpl(host) {
|
|
|
630
630
|
}
|
|
631
631
|
/** Text search via $search aggregation stage. */
|
|
632
632
|
async function searchImpl(host, text, query, indexName) {
|
|
633
|
-
const
|
|
634
|
-
if (!
|
|
635
|
-
return runSearchPipeline(host, stage, query, "search");
|
|
633
|
+
const plan = buildSearchStage(host, text, indexName);
|
|
634
|
+
if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
|
|
635
|
+
return runSearchPipeline(host, plan.stage, query, "search", void 0, plan.classicText);
|
|
636
636
|
}
|
|
637
637
|
/** Text search with faceted count. */
|
|
638
638
|
async function searchWithCountImpl(host, text, query, indexName) {
|
|
639
|
-
const
|
|
640
|
-
if (!
|
|
641
|
-
return runSearchWithCountPipeline(host, stage, query, "searchWithCount");
|
|
639
|
+
const plan = buildSearchStage(host, text, indexName);
|
|
640
|
+
if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
|
|
641
|
+
return runSearchWithCountPipeline(host, plan.stage, query, "searchWithCount", void 0, plan.classicText);
|
|
642
642
|
}
|
|
643
643
|
/** Vector search via $vectorSearch aggregation stage. */
|
|
644
644
|
async function vectorSearchImpl(host, vector, query, indexName) {
|
|
@@ -656,18 +656,40 @@ function resolveThreshold(host, controls, indexName) {
|
|
|
656
656
|
if (queryThreshold !== void 0) return queryThreshold;
|
|
657
657
|
return host.getVectorThreshold(indexName);
|
|
658
658
|
}
|
|
659
|
-
/**
|
|
659
|
+
/**
|
|
660
|
+
* Builds the first pipeline stage for a text search and reports whether it is a
|
|
661
|
+
* classic `$text` query (vs. an Atlas `$search`).
|
|
662
|
+
*
|
|
663
|
+
* Two distinct execution paths share the `search()` API:
|
|
664
|
+
* - **Classic text** (`@db.index.fulltext`, or an adapter-scanned `text`
|
|
665
|
+
* index): served by the collection's MongoDB text index via `$match $text`.
|
|
666
|
+
* Works on community MongoDB and Atlas alike.
|
|
667
|
+
* - **Atlas Search** (`search_text` / `dynamic_text`): served by `mongot` via
|
|
668
|
+
* `$search`, which can ONLY resolve Atlas Search indexes.
|
|
669
|
+
*
|
|
670
|
+
* Routing a classic index through `$search` (as this used to) is guaranteed to
|
|
671
|
+
* fail at runtime — `$search` can't see a classic text index, and on community
|
|
672
|
+
* MongoDB the stage is unsupported entirely — even though the table reports
|
|
673
|
+
* `searchable: true`. The discriminator is the resolved index's `type`.
|
|
674
|
+
*/
|
|
660
675
|
function buildSearchStage(host, text, indexName) {
|
|
661
676
|
const index = host.getMongoSearchIndex(indexName);
|
|
662
677
|
if (!index) return;
|
|
663
678
|
if (index.type === "vector") throw new Error("Vector indexes cannot be used with text search. Use vectorSearch() instead.");
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
679
|
+
if (index.type === "text") return {
|
|
680
|
+
stage: { $match: { $text: { $search: text } } },
|
|
681
|
+
classicText: true
|
|
682
|
+
};
|
|
683
|
+
return {
|
|
684
|
+
stage: { $search: {
|
|
685
|
+
index: index.key,
|
|
686
|
+
text: {
|
|
687
|
+
query: text,
|
|
688
|
+
path: { wildcard: "*" }
|
|
689
|
+
}
|
|
690
|
+
} },
|
|
691
|
+
classicText: false
|
|
692
|
+
};
|
|
671
693
|
}
|
|
672
694
|
/** Builds a $vectorSearch aggregation stage from a pre-computed vector. */
|
|
673
695
|
function buildVectorSearchStage(host, vector, indexName, limit) {
|
|
@@ -698,16 +720,17 @@ function buildVectorSearchStage(host, vector, indexName, limit) {
|
|
|
698
720
|
} };
|
|
699
721
|
}
|
|
700
722
|
/** Runs a search/vector pipeline and returns results. Shared by search + vectorSearch. */
|
|
701
|
-
async function runSearchPipeline(host, stage, query, label, threshold) {
|
|
723
|
+
async function runSearchPipeline(host, stage, query, label, threshold, classicText = false) {
|
|
702
724
|
const filter = require_mongo_filter.buildMongoFilter(query.filter);
|
|
703
725
|
const controls = query.controls || {};
|
|
704
726
|
const pipeline = [stage];
|
|
705
727
|
if (threshold !== void 0) {
|
|
706
728
|
pipeline.push({ $addFields: { _score: { $meta: "vectorSearchScore" } } });
|
|
707
729
|
pipeline.push({ $match: { _score: { $gte: threshold } } });
|
|
708
|
-
}
|
|
730
|
+
} else if (classicText) pipeline.push({ $addFields: { _score: { $meta: "textScore" } } });
|
|
709
731
|
pipeline.push({ $match: filter });
|
|
710
732
|
if (controls.$sort) pipeline.push({ $sort: controls.$sort });
|
|
733
|
+
else if (classicText) pipeline.push({ $sort: { _score: -1 } });
|
|
711
734
|
if (controls.$skip) pipeline.push({ $skip: controls.$skip });
|
|
712
735
|
if (controls.$limit) pipeline.push({ $limit: controls.$limit });
|
|
713
736
|
else pipeline.push({ $limit: 1e3 });
|
|
@@ -719,16 +742,17 @@ async function runSearchPipeline(host, stage, query, label, threshold) {
|
|
|
719
742
|
return wrapInvalidQuery(() => host.collection.aggregate(pipeline, host._getSessionOpts()).toArray());
|
|
720
743
|
}
|
|
721
744
|
/** Runs a search/vector pipeline with $facet for count. Shared by searchWithCount + vectorSearchWithCount. */
|
|
722
|
-
async function runSearchWithCountPipeline(host, stage, query, label, threshold) {
|
|
745
|
+
async function runSearchWithCountPipeline(host, stage, query, label, threshold, classicText = false) {
|
|
723
746
|
const filter = require_mongo_filter.buildMongoFilter(query.filter);
|
|
724
747
|
const controls = query.controls || {};
|
|
725
748
|
const preStages = [];
|
|
726
749
|
if (threshold !== void 0) {
|
|
727
750
|
preStages.push({ $addFields: { _score: { $meta: "vectorSearchScore" } } });
|
|
728
751
|
preStages.push({ $match: { _score: { $gte: threshold } } });
|
|
729
|
-
}
|
|
752
|
+
} else if (classicText) preStages.push({ $addFields: { _score: { $meta: "textScore" } } });
|
|
730
753
|
const dataStages = [];
|
|
731
754
|
if (controls.$sort) dataStages.push({ $sort: controls.$sort });
|
|
755
|
+
else if (classicText) dataStages.push({ $sort: { _score: -1 } });
|
|
732
756
|
if (controls.$skip) dataStages.push({ $skip: controls.$skip });
|
|
733
757
|
if (controls.$limit) dataStages.push({ $limit: controls.$limit });
|
|
734
758
|
if (controls.$select) {
|
|
@@ -1058,7 +1082,7 @@ async function syncIndexesImpl(host) {
|
|
|
1058
1082
|
mongoType = "text";
|
|
1059
1083
|
for (const f of index.fields) {
|
|
1060
1084
|
fields[f.name] = "text";
|
|
1061
|
-
|
|
1085
|
+
weights[f.name] = f.weight ?? 1;
|
|
1062
1086
|
}
|
|
1063
1087
|
} else {
|
|
1064
1088
|
mongoType = index.type;
|
|
@@ -1504,11 +1528,6 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
1504
1528
|
const startValue = metadata.get("db.default.increment");
|
|
1505
1529
|
this._incrementFields.set(physicalName, typeof startValue === "number" ? startValue : void 0);
|
|
1506
1530
|
}
|
|
1507
|
-
for (const index of metadata.get("db.index.fulltext") || []) {
|
|
1508
|
-
const name = typeof index === "object" ? index.name || "" : "";
|
|
1509
|
-
const weight = typeof index === "object" ? index.weight || 1 : 1;
|
|
1510
|
-
this._addMongoIndexField("text", name, field, weight);
|
|
1511
|
-
}
|
|
1512
1531
|
for (const index of metadata.get("db.mongo.search.text") || []) this._addFieldToSearchIndex("search_text", index.indexName, field, index.analyzer);
|
|
1513
1532
|
const vectorIndex = metadata.get("db.search.vector");
|
|
1514
1533
|
if (vectorIndex) {
|
package/dist/index.mjs
CHANGED
|
@@ -629,15 +629,15 @@ function isVectorSearchableImpl(host) {
|
|
|
629
629
|
}
|
|
630
630
|
/** Text search via $search aggregation stage. */
|
|
631
631
|
async function searchImpl(host, text, query, indexName) {
|
|
632
|
-
const
|
|
633
|
-
if (!
|
|
634
|
-
return runSearchPipeline(host, stage, query, "search");
|
|
632
|
+
const plan = buildSearchStage(host, text, indexName);
|
|
633
|
+
if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
|
|
634
|
+
return runSearchPipeline(host, plan.stage, query, "search", void 0, plan.classicText);
|
|
635
635
|
}
|
|
636
636
|
/** Text search with faceted count. */
|
|
637
637
|
async function searchWithCountImpl(host, text, query, indexName) {
|
|
638
|
-
const
|
|
639
|
-
if (!
|
|
640
|
-
return runSearchWithCountPipeline(host, stage, query, "searchWithCount");
|
|
638
|
+
const plan = buildSearchStage(host, text, indexName);
|
|
639
|
+
if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
|
|
640
|
+
return runSearchWithCountPipeline(host, plan.stage, query, "searchWithCount", void 0, plan.classicText);
|
|
641
641
|
}
|
|
642
642
|
/** Vector search via $vectorSearch aggregation stage. */
|
|
643
643
|
async function vectorSearchImpl(host, vector, query, indexName) {
|
|
@@ -655,18 +655,40 @@ function resolveThreshold(host, controls, indexName) {
|
|
|
655
655
|
if (queryThreshold !== void 0) return queryThreshold;
|
|
656
656
|
return host.getVectorThreshold(indexName);
|
|
657
657
|
}
|
|
658
|
-
/**
|
|
658
|
+
/**
|
|
659
|
+
* Builds the first pipeline stage for a text search and reports whether it is a
|
|
660
|
+
* classic `$text` query (vs. an Atlas `$search`).
|
|
661
|
+
*
|
|
662
|
+
* Two distinct execution paths share the `search()` API:
|
|
663
|
+
* - **Classic text** (`@db.index.fulltext`, or an adapter-scanned `text`
|
|
664
|
+
* index): served by the collection's MongoDB text index via `$match $text`.
|
|
665
|
+
* Works on community MongoDB and Atlas alike.
|
|
666
|
+
* - **Atlas Search** (`search_text` / `dynamic_text`): served by `mongot` via
|
|
667
|
+
* `$search`, which can ONLY resolve Atlas Search indexes.
|
|
668
|
+
*
|
|
669
|
+
* Routing a classic index through `$search` (as this used to) is guaranteed to
|
|
670
|
+
* fail at runtime — `$search` can't see a classic text index, and on community
|
|
671
|
+
* MongoDB the stage is unsupported entirely — even though the table reports
|
|
672
|
+
* `searchable: true`. The discriminator is the resolved index's `type`.
|
|
673
|
+
*/
|
|
659
674
|
function buildSearchStage(host, text, indexName) {
|
|
660
675
|
const index = host.getMongoSearchIndex(indexName);
|
|
661
676
|
if (!index) return;
|
|
662
677
|
if (index.type === "vector") throw new Error("Vector indexes cannot be used with text search. Use vectorSearch() instead.");
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
678
|
+
if (index.type === "text") return {
|
|
679
|
+
stage: { $match: { $text: { $search: text } } },
|
|
680
|
+
classicText: true
|
|
681
|
+
};
|
|
682
|
+
return {
|
|
683
|
+
stage: { $search: {
|
|
684
|
+
index: index.key,
|
|
685
|
+
text: {
|
|
686
|
+
query: text,
|
|
687
|
+
path: { wildcard: "*" }
|
|
688
|
+
}
|
|
689
|
+
} },
|
|
690
|
+
classicText: false
|
|
691
|
+
};
|
|
670
692
|
}
|
|
671
693
|
/** Builds a $vectorSearch aggregation stage from a pre-computed vector. */
|
|
672
694
|
function buildVectorSearchStage(host, vector, indexName, limit) {
|
|
@@ -697,16 +719,17 @@ function buildVectorSearchStage(host, vector, indexName, limit) {
|
|
|
697
719
|
} };
|
|
698
720
|
}
|
|
699
721
|
/** Runs a search/vector pipeline and returns results. Shared by search + vectorSearch. */
|
|
700
|
-
async function runSearchPipeline(host, stage, query, label, threshold) {
|
|
722
|
+
async function runSearchPipeline(host, stage, query, label, threshold, classicText = false) {
|
|
701
723
|
const filter = buildMongoFilter(query.filter);
|
|
702
724
|
const controls = query.controls || {};
|
|
703
725
|
const pipeline = [stage];
|
|
704
726
|
if (threshold !== void 0) {
|
|
705
727
|
pipeline.push({ $addFields: { _score: { $meta: "vectorSearchScore" } } });
|
|
706
728
|
pipeline.push({ $match: { _score: { $gte: threshold } } });
|
|
707
|
-
}
|
|
729
|
+
} else if (classicText) pipeline.push({ $addFields: { _score: { $meta: "textScore" } } });
|
|
708
730
|
pipeline.push({ $match: filter });
|
|
709
731
|
if (controls.$sort) pipeline.push({ $sort: controls.$sort });
|
|
732
|
+
else if (classicText) pipeline.push({ $sort: { _score: -1 } });
|
|
710
733
|
if (controls.$skip) pipeline.push({ $skip: controls.$skip });
|
|
711
734
|
if (controls.$limit) pipeline.push({ $limit: controls.$limit });
|
|
712
735
|
else pipeline.push({ $limit: 1e3 });
|
|
@@ -718,16 +741,17 @@ async function runSearchPipeline(host, stage, query, label, threshold) {
|
|
|
718
741
|
return wrapInvalidQuery(() => host.collection.aggregate(pipeline, host._getSessionOpts()).toArray());
|
|
719
742
|
}
|
|
720
743
|
/** Runs a search/vector pipeline with $facet for count. Shared by searchWithCount + vectorSearchWithCount. */
|
|
721
|
-
async function runSearchWithCountPipeline(host, stage, query, label, threshold) {
|
|
744
|
+
async function runSearchWithCountPipeline(host, stage, query, label, threshold, classicText = false) {
|
|
722
745
|
const filter = buildMongoFilter(query.filter);
|
|
723
746
|
const controls = query.controls || {};
|
|
724
747
|
const preStages = [];
|
|
725
748
|
if (threshold !== void 0) {
|
|
726
749
|
preStages.push({ $addFields: { _score: { $meta: "vectorSearchScore" } } });
|
|
727
750
|
preStages.push({ $match: { _score: { $gte: threshold } } });
|
|
728
|
-
}
|
|
751
|
+
} else if (classicText) preStages.push({ $addFields: { _score: { $meta: "textScore" } } });
|
|
729
752
|
const dataStages = [];
|
|
730
753
|
if (controls.$sort) dataStages.push({ $sort: controls.$sort });
|
|
754
|
+
else if (classicText) dataStages.push({ $sort: { _score: -1 } });
|
|
731
755
|
if (controls.$skip) dataStages.push({ $skip: controls.$skip });
|
|
732
756
|
if (controls.$limit) dataStages.push({ $limit: controls.$limit });
|
|
733
757
|
if (controls.$select) {
|
|
@@ -1057,7 +1081,7 @@ async function syncIndexesImpl(host) {
|
|
|
1057
1081
|
mongoType = "text";
|
|
1058
1082
|
for (const f of index.fields) {
|
|
1059
1083
|
fields[f.name] = "text";
|
|
1060
|
-
|
|
1084
|
+
weights[f.name] = f.weight ?? 1;
|
|
1061
1085
|
}
|
|
1062
1086
|
} else {
|
|
1063
1087
|
mongoType = index.type;
|
|
@@ -1503,11 +1527,6 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
|
|
|
1503
1527
|
const startValue = metadata.get("db.default.increment");
|
|
1504
1528
|
this._incrementFields.set(physicalName, typeof startValue === "number" ? startValue : void 0);
|
|
1505
1529
|
}
|
|
1506
|
-
for (const index of metadata.get("db.index.fulltext") || []) {
|
|
1507
|
-
const name = typeof index === "object" ? index.name || "" : "";
|
|
1508
|
-
const weight = typeof index === "object" ? index.weight || 1 : 1;
|
|
1509
|
-
this._addMongoIndexField("text", name, field, weight);
|
|
1510
|
-
}
|
|
1511
1530
|
for (const index of metadata.get("db.mongo.search.text") || []) this._addFieldToSearchIndex("search_text", index.indexName, field, index.analyzer);
|
|
1512
1531
|
const vectorIndex = metadata.get("db.search.vector");
|
|
1513
1532
|
if (vectorIndex) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db-mongo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.99",
|
|
4
4
|
"description": "Mongodb plugin for atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
@@ -46,17 +46,17 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@atscript/core": "^0.1.
|
|
50
|
-
"@atscript/typescript": "^0.1.
|
|
49
|
+
"@atscript/core": "^0.1.72",
|
|
50
|
+
"@atscript/typescript": "^0.1.72",
|
|
51
51
|
"mongodb": "^6.17.0",
|
|
52
52
|
"mongodb-memory-server-core": "^10.0.0",
|
|
53
|
-
"unplugin-atscript": "^0.1.
|
|
53
|
+
"unplugin-atscript": "^0.1.72"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@atscript/core": "^0.1.
|
|
57
|
-
"@atscript/typescript": "^0.1.
|
|
56
|
+
"@atscript/core": "^0.1.72",
|
|
57
|
+
"@atscript/typescript": "^0.1.72",
|
|
58
58
|
"mongodb": "^6.17.0",
|
|
59
|
-
"@atscript/db": "^0.1.
|
|
59
|
+
"@atscript/db": "^0.1.99"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"postinstall": "asc -f dts",
|