@atscript/db-mongo 0.1.101 → 0.1.103
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 +1 -1
- package/dist/agg.mjs +1 -1
- package/dist/index-Bl_O47fp.d.cts +6 -0
- package/dist/index-Bl_O47fp.d.mts +6 -0
- package/dist/index.cjs +147 -29
- package/dist/index.d.cts +49 -12
- package/dist/index.d.mts +49 -12
- package/dist/index.mjs +147 -30
- package/dist/plugin-Bq6hZMBA.cjs +242 -0
- package/dist/plugin-KVFAwoGw.mjs +237 -0
- package/dist/plugin.cjs +3 -174
- package/dist/plugin.d.cts +1 -5
- package/dist/plugin.d.mts +1 -5
- package/dist/plugin.mjs +1 -172
- package/package.json +7 -7
- /package/dist/{mongo-filter-AihWWQXp.cjs → mongo-filter-1EpqdD-T.cjs} +0 -0
- /package/dist/{mongo-filter-BsocUQG3.mjs → mongo-filter-DBYaF9aH.mjs} +0 -0
package/dist/agg.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_mongo_filter = require("./mongo-filter-
|
|
2
|
+
const require_mongo_filter = require("./mongo-filter-1EpqdD-T.cjs");
|
|
3
3
|
let _atscript_db_agg = require("@atscript/db/agg");
|
|
4
4
|
//#region src/agg.ts
|
|
5
5
|
/** Simple accumulators that map directly to `{ $<fn>: '$field' }`. */
|
package/dist/agg.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as buildMongoFilter } from "./mongo-filter-
|
|
1
|
+
import { t as buildMongoFilter } from "./mongo-filter-DBYaF9aH.mjs";
|
|
2
2
|
import { resolveAlias } from "@atscript/db/agg";
|
|
3
3
|
//#region src/agg.ts
|
|
4
4
|
/** Simple accumulators that map directly to `{ $<fn>: '$field' }`. */
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_mongo_filter = require("./mongo-filter-
|
|
2
|
+
const require_mongo_filter = require("./mongo-filter-1EpqdD-T.cjs");
|
|
3
|
+
const require_plugin = require("./plugin-Bq6hZMBA.cjs");
|
|
3
4
|
let _atscript_db = require("@atscript/db");
|
|
4
5
|
let mongodb = require("mongodb");
|
|
5
6
|
//#region src/lib/projection-dedupe.ts
|
|
@@ -72,6 +73,9 @@ function containsAggregationExpr(v) {
|
|
|
72
73
|
* `$set` map.
|
|
73
74
|
*/
|
|
74
75
|
var CollectionPatcher = class {
|
|
76
|
+
collection;
|
|
77
|
+
payload;
|
|
78
|
+
ops;
|
|
75
79
|
constructor(collection, payload, ops) {
|
|
76
80
|
this.collection = collection;
|
|
77
81
|
this.payload = payload;
|
|
@@ -630,13 +634,13 @@ function isVectorSearchableImpl(host) {
|
|
|
630
634
|
}
|
|
631
635
|
/** Text search via $search aggregation stage. */
|
|
632
636
|
async function searchImpl(host, text, query, indexName) {
|
|
633
|
-
const plan = buildSearchStage(host, text, indexName);
|
|
637
|
+
const plan = buildSearchStage(host, text, indexName, query.controls);
|
|
634
638
|
if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
|
|
635
639
|
return runSearchPipeline(host, plan.stage, query, "search", void 0, plan.classicText);
|
|
636
640
|
}
|
|
637
641
|
/** Text search with faceted count. */
|
|
638
642
|
async function searchWithCountImpl(host, text, query, indexName) {
|
|
639
|
-
const plan = buildSearchStage(host, text, indexName);
|
|
643
|
+
const plan = buildSearchStage(host, text, indexName, query.controls);
|
|
640
644
|
if (!plan) throw new Error(indexName ? `Search index "${indexName}" not found` : "No search index available");
|
|
641
645
|
return runSearchWithCountPipeline(host, plan.stage, query, "searchWithCount", void 0, plan.classicText);
|
|
642
646
|
}
|
|
@@ -672,7 +676,7 @@ function resolveThreshold(host, controls, indexName) {
|
|
|
672
676
|
* MongoDB the stage is unsupported entirely — even though the table reports
|
|
673
677
|
* `searchable: true`. The discriminator is the resolved index's `type`.
|
|
674
678
|
*/
|
|
675
|
-
function buildSearchStage(host, text, indexName) {
|
|
679
|
+
function buildSearchStage(host, text, indexName, controls) {
|
|
676
680
|
const index = host.getMongoSearchIndex(indexName);
|
|
677
681
|
if (!index) return;
|
|
678
682
|
if (index.type === "vector") throw new Error("Vector indexes cannot be used with text search. Use vectorSearch() instead.");
|
|
@@ -680,17 +684,58 @@ function buildSearchStage(host, text, indexName) {
|
|
|
680
684
|
stage: { $match: { $text: { $search: text } } },
|
|
681
685
|
classicText: true
|
|
682
686
|
};
|
|
687
|
+
const searchIndex = index;
|
|
688
|
+
const fuzzy = resolveSearchFuzzy(searchIndex, controls);
|
|
689
|
+
const strategy = searchIndex.strategy ?? "compound";
|
|
690
|
+
const autocompletePaths = autocompleteFieldPaths(searchIndex);
|
|
691
|
+
const textClause = () => ({ text: {
|
|
692
|
+
query: text,
|
|
693
|
+
path: { wildcard: "*" },
|
|
694
|
+
...fuzzy ? { fuzzy } : {}
|
|
695
|
+
} });
|
|
696
|
+
const autocompleteClause = (path) => ({ autocomplete: {
|
|
697
|
+
query: text,
|
|
698
|
+
path,
|
|
699
|
+
...fuzzy ? { fuzzy } : {}
|
|
700
|
+
} });
|
|
701
|
+
let body;
|
|
702
|
+
if (strategy === "text" || autocompletePaths.length === 0) body = textClause();
|
|
703
|
+
else if (strategy === "autocomplete") {
|
|
704
|
+
const clauses = autocompletePaths.map(autocompleteClause);
|
|
705
|
+
body = clauses.length === 1 ? clauses[0] : { compound: {
|
|
706
|
+
should: clauses,
|
|
707
|
+
minimumShouldMatch: 1
|
|
708
|
+
} };
|
|
709
|
+
} else body = { compound: {
|
|
710
|
+
should: [textClause(), ...autocompletePaths.map(autocompleteClause)],
|
|
711
|
+
minimumShouldMatch: 1
|
|
712
|
+
} };
|
|
683
713
|
return {
|
|
684
714
|
stage: { $search: {
|
|
685
715
|
index: index.key,
|
|
686
|
-
|
|
687
|
-
query: text,
|
|
688
|
-
path: { wildcard: "*" }
|
|
689
|
-
}
|
|
716
|
+
...body
|
|
690
717
|
} },
|
|
691
718
|
classicText: false
|
|
692
719
|
};
|
|
693
720
|
}
|
|
721
|
+
/**
|
|
722
|
+
* Resolves query-time fuzzy (typo tolerance): the `$fuzzy` request control
|
|
723
|
+
* overrides the schema-declared `@db.mongo.search.*` fuzzy. Only an edit distance
|
|
724
|
+
* of 1 or 2 is emitted (Atlas rejects 0) — anything else means "no fuzzy".
|
|
725
|
+
*/
|
|
726
|
+
function resolveSearchFuzzy(index, controls) {
|
|
727
|
+
const override = controls?.$fuzzy;
|
|
728
|
+
const maxEdits = override === void 0 ? index.fuzzy?.maxEdits : Number(override);
|
|
729
|
+
return maxEdits === 1 || maxEdits === 2 ? { maxEdits } : void 0;
|
|
730
|
+
}
|
|
731
|
+
/** Field paths in this index that carry an `autocomplete` mapping. */
|
|
732
|
+
function autocompleteFieldPaths(index) {
|
|
733
|
+
const fields = index.definition.mappings?.fields;
|
|
734
|
+
if (!fields) return [];
|
|
735
|
+
const paths = [];
|
|
736
|
+
for (const [path, mapping] of Object.entries(fields)) if ((Array.isArray(mapping) ? mapping : [mapping]).some((m) => m.type === "autocomplete")) paths.push(path);
|
|
737
|
+
return paths;
|
|
738
|
+
}
|
|
694
739
|
/** Builds a $vectorSearch aggregation stage from a pre-computed vector. */
|
|
695
740
|
function buildVectorSearchStage(host, vector, indexName, limit) {
|
|
696
741
|
let index;
|
|
@@ -1289,10 +1334,26 @@ function fieldsMatch(left, right) {
|
|
|
1289
1334
|
if (leftKeys.length !== rightKeys.length) return false;
|
|
1290
1335
|
for (const key of leftKeys) {
|
|
1291
1336
|
if (!(key in right)) return false;
|
|
1292
|
-
if (left[key]
|
|
1337
|
+
if (!fieldMappingEqual(left[key], right[key])) return false;
|
|
1293
1338
|
}
|
|
1294
1339
|
return true;
|
|
1295
1340
|
}
|
|
1341
|
+
/** Order-independent structural compare of a field's Atlas type mapping(s). */
|
|
1342
|
+
function fieldMappingEqual(a, b) {
|
|
1343
|
+
const am = mappingsByType(a);
|
|
1344
|
+
const bm = mappingsByType(b);
|
|
1345
|
+
if (am.size !== bm.size) return false;
|
|
1346
|
+
for (const [type, av] of am) {
|
|
1347
|
+
const bv = bm.get(type);
|
|
1348
|
+
if (!bv || av.analyzer !== bv.analyzer || av.tokenization !== bv.tokenization || av.minGrams !== bv.minGrams || av.maxGrams !== bv.maxGrams || av.foldDiacritics !== bv.foldDiacritics) return false;
|
|
1349
|
+
}
|
|
1350
|
+
return true;
|
|
1351
|
+
}
|
|
1352
|
+
function mappingsByType(m) {
|
|
1353
|
+
const map = /* @__PURE__ */ new Map();
|
|
1354
|
+
for (const x of Array.isArray(m) ? m : [m]) map.set(x.type, x);
|
|
1355
|
+
return map;
|
|
1356
|
+
}
|
|
1296
1357
|
function vectorFieldsMatch(left, right) {
|
|
1297
1358
|
if (left.length !== (right || []).length) return false;
|
|
1298
1359
|
const rightMap = /* @__PURE__ */ new Map();
|
|
@@ -1318,6 +1379,8 @@ const validateMongoIdPlugin = (ctx, def, value) => {
|
|
|
1318
1379
|
//#endregion
|
|
1319
1380
|
//#region src/lib/mongo-adapter.ts
|
|
1320
1381
|
var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
1382
|
+
db;
|
|
1383
|
+
client;
|
|
1321
1384
|
_collection;
|
|
1322
1385
|
/** MongoDB-specific indexes (search, vector) — separate from table.indexes. */
|
|
1323
1386
|
_mongoIndexes = /* @__PURE__ */ new Map();
|
|
@@ -1508,13 +1571,14 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
1508
1571
|
const dynamicText = typeMeta.get("db.mongo.search.dynamic");
|
|
1509
1572
|
if (dynamicText) this._setSearchIndex("dynamic_text", "_", {
|
|
1510
1573
|
mappings: { dynamic: true },
|
|
1511
|
-
analyzer: dynamicText.analyzer
|
|
1512
|
-
|
|
1513
|
-
});
|
|
1574
|
+
analyzer: dynamicText.analyzer
|
|
1575
|
+
}, { fuzzy: normalizeSearchFuzzy(dynamicText.fuzzy) });
|
|
1514
1576
|
for (const textSearch of typeMeta.get("db.mongo.search.static") || []) this._setSearchIndex("search_text", textSearch.indexName, {
|
|
1515
1577
|
mappings: { fields: {} },
|
|
1516
|
-
analyzer: textSearch.analyzer
|
|
1517
|
-
|
|
1578
|
+
analyzer: textSearch.analyzer
|
|
1579
|
+
}, {
|
|
1580
|
+
fuzzy: normalizeSearchFuzzy(textSearch.fuzzy),
|
|
1581
|
+
strategy: normalizeSearchStrategy(textSearch.strategy)
|
|
1518
1582
|
});
|
|
1519
1583
|
}
|
|
1520
1584
|
onFieldScanned(field, _type, metadata) {
|
|
@@ -1528,7 +1592,24 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
1528
1592
|
const startValue = metadata.get("db.default.increment");
|
|
1529
1593
|
this._incrementFields.set(physicalName, typeof startValue === "number" ? startValue : void 0);
|
|
1530
1594
|
}
|
|
1531
|
-
for (const index of metadata.get("db.mongo.search.text") || []) this._addFieldToSearchIndex("search_text", index.indexName, field, index.analyzer
|
|
1595
|
+
for (const index of metadata.get("db.mongo.search.text") || []) this._addFieldToSearchIndex("search_text", index.indexName, field, [index.analyzer ? {
|
|
1596
|
+
type: "string",
|
|
1597
|
+
analyzer: index.analyzer
|
|
1598
|
+
} : { type: "string" }]);
|
|
1599
|
+
for (const ac of metadata.get("db.mongo.search.autocomplete") || []) {
|
|
1600
|
+
const autocomplete = {
|
|
1601
|
+
type: "autocomplete",
|
|
1602
|
+
tokenization: ac.tokenization || "edgeGram",
|
|
1603
|
+
minGrams: ac.minGrams ?? 2,
|
|
1604
|
+
maxGrams: ac.maxGrams ?? 15,
|
|
1605
|
+
foldDiacritics: ac.foldDiacritics ?? true
|
|
1606
|
+
};
|
|
1607
|
+
const companion = ac.analyzer ? {
|
|
1608
|
+
type: "string",
|
|
1609
|
+
analyzer: ac.analyzer
|
|
1610
|
+
} : { type: "string" };
|
|
1611
|
+
this._addFieldToSearchIndex("search_text", ac.indexName, field, [autocomplete, companion]);
|
|
1612
|
+
}
|
|
1532
1613
|
const vectorIndex = metadata.get("db.search.vector");
|
|
1533
1614
|
if (vectorIndex) {
|
|
1534
1615
|
const indexName = vectorIndex.indexName || field;
|
|
@@ -1989,32 +2070,68 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
1989
2070
|
}
|
|
1990
2071
|
if (weight) index.weights[field] = weight;
|
|
1991
2072
|
}
|
|
1992
|
-
_setSearchIndex(type, name, definition) {
|
|
2073
|
+
_setSearchIndex(type, name, definition, meta) {
|
|
1993
2074
|
const key = mongoIndexKey(type, name || "DEFAULT");
|
|
1994
|
-
|
|
2075
|
+
const index = {
|
|
1995
2076
|
key,
|
|
1996
2077
|
name: name || "DEFAULT",
|
|
1997
2078
|
type,
|
|
1998
|
-
definition
|
|
1999
|
-
|
|
2079
|
+
definition,
|
|
2080
|
+
fuzzy: meta?.fuzzy,
|
|
2081
|
+
strategy: meta?.strategy
|
|
2082
|
+
};
|
|
2083
|
+
this._mongoIndexes.set(key, index);
|
|
2084
|
+
return index;
|
|
2000
2085
|
}
|
|
2001
|
-
|
|
2086
|
+
/**
|
|
2087
|
+
* Adds (and merges, by mapping `type`) one or more Atlas field-type mappings to
|
|
2088
|
+
* a `search_text` index. Multiple annotations on the same field — e.g.
|
|
2089
|
+
* `@db.mongo.search.text` (string) plus `@db.mongo.search.autocomplete`
|
|
2090
|
+
* (autocomplete + string) — accumulate into a single multi-type mapping
|
|
2091
|
+
* instead of overwriting one another.
|
|
2092
|
+
*/
|
|
2093
|
+
_addFieldToSearchIndex(type, _name, fieldName, mappings) {
|
|
2002
2094
|
const name = _name || "DEFAULT";
|
|
2003
2095
|
let index = this._mongoIndexes.get(mongoIndexKey(type, name));
|
|
2004
|
-
if (!index && type === "search_text") {
|
|
2005
|
-
this._setSearchIndex(type, name, {
|
|
2006
|
-
mappings: { fields: {} },
|
|
2007
|
-
text: { fuzzy: { maxEdits: 0 } }
|
|
2008
|
-
});
|
|
2009
|
-
index = this._mongoIndexes.get(mongoIndexKey(type, name));
|
|
2010
|
-
}
|
|
2096
|
+
if (!index && type === "search_text") index = this._setSearchIndex(type, name, { mappings: { fields: {} } });
|
|
2011
2097
|
if (index) {
|
|
2012
|
-
index.definition.mappings.fields
|
|
2013
|
-
|
|
2098
|
+
const fields = index.definition.mappings.fields;
|
|
2099
|
+
fields[fieldName] = mergeFieldMappings(fields[fieldName], mappings);
|
|
2014
2100
|
}
|
|
2015
2101
|
}
|
|
2016
2102
|
};
|
|
2017
2103
|
/**
|
|
2104
|
+
* Normalizes a declared `fuzzy` arg (`0-2`) into the query-time metadata Atlas
|
|
2105
|
+
* accepts. Atlas only honors an edit distance of `1` or `2`; `0`/undefined means
|
|
2106
|
+
* "no fuzzy", returned as `undefined` so no `fuzzy` clause is ever emitted.
|
|
2107
|
+
*/
|
|
2108
|
+
function normalizeSearchFuzzy(fuzzy) {
|
|
2109
|
+
return fuzzy === 1 || fuzzy === 2 ? { maxEdits: fuzzy } : void 0;
|
|
2110
|
+
}
|
|
2111
|
+
/** Narrows the declared `strategy` arg to a known value (undefined → query layer defaults to "compound"). */
|
|
2112
|
+
function normalizeSearchStrategy(strategy) {
|
|
2113
|
+
return strategy === "compound" || strategy === "autocomplete" || strategy === "text" ? strategy : void 0;
|
|
2114
|
+
}
|
|
2115
|
+
/**
|
|
2116
|
+
* Merges incoming Atlas field-type mappings into a field's existing mapping,
|
|
2117
|
+
* keyed by `type` (a later mapping of the same type replaces the earlier one).
|
|
2118
|
+
* Collapses to a single object when one type remains, else an array (Atlas's
|
|
2119
|
+
* multi-type field form).
|
|
2120
|
+
*/
|
|
2121
|
+
function mergeFieldMappings(existing, incoming) {
|
|
2122
|
+
const byType = /* @__PURE__ */ new Map();
|
|
2123
|
+
const order = [];
|
|
2124
|
+
const add = (m) => {
|
|
2125
|
+
if (!byType.has(m.type)) order.push(m.type);
|
|
2126
|
+
byType.set(m.type, m);
|
|
2127
|
+
};
|
|
2128
|
+
if (Array.isArray(existing)) existing.forEach(add);
|
|
2129
|
+
else if (existing) add(existing);
|
|
2130
|
+
incoming.forEach(add);
|
|
2131
|
+
const merged = order.map((t) => byType.get(t));
|
|
2132
|
+
return merged.length === 1 ? merged[0] : merged;
|
|
2133
|
+
}
|
|
2134
|
+
/**
|
|
2018
2135
|
* Builds a MongoDB update document from a data object that may contain
|
|
2019
2136
|
* field ops (`{ $inc: N }`, `{ $dec: N }`, `{ $mul: N }`).
|
|
2020
2137
|
* Regular fields go into `$set`, ops go into `$inc` / `$mul`. When
|
|
@@ -2047,6 +2164,7 @@ function createAdapter(connection, _options) {
|
|
|
2047
2164
|
//#endregion
|
|
2048
2165
|
exports.CollectionPatcher = CollectionPatcher;
|
|
2049
2166
|
exports.MongoAdapter = MongoAdapter;
|
|
2167
|
+
exports.MongoPlugin = require_plugin.MongoPlugin;
|
|
2050
2168
|
exports.buildMongoFilter = require_mongo_filter.buildMongoFilter;
|
|
2051
2169
|
exports.createAdapter = createAdapter;
|
|
2052
2170
|
exports.validateMongoIdPlugin = validateMongoIdPlugin;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as MongoPlugin } from "./index-Bl_O47fp.cjs";
|
|
1
2
|
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertManyResult, TDbInsertResult, TDbRelation, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
|
|
2
3
|
import { AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
|
|
3
4
|
import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
|
|
@@ -165,16 +166,45 @@ interface TSearchIndex {
|
|
|
165
166
|
name: string;
|
|
166
167
|
type: "dynamic_text" | "search_text" | "vector";
|
|
167
168
|
definition: TMongoSearchIndexDefinition;
|
|
169
|
+
/**
|
|
170
|
+
* Query-time fuzzy (typo tolerance) declared via `@db.mongo.search.dynamic` /
|
|
171
|
+
* `@db.mongo.search.static`. Carried as index metadata — NOT part of the Atlas
|
|
172
|
+
* index `definition` (Atlas applies fuzzy at query time on the `text`/
|
|
173
|
+
* `autocomplete` operator, not in the index schema). `buildSearchStage` reads
|
|
174
|
+
* this and attaches it to the emitted operator.
|
|
175
|
+
*/
|
|
176
|
+
fuzzy?: {
|
|
177
|
+
maxEdits: number;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* The match strategy declared via `@db.mongo.search.static`, locking this
|
|
181
|
+
* index's query shape. `buildSearchStage` reads it; undefined → `"compound"`.
|
|
182
|
+
* - `compound` — wildcard `text` + per-field `autocomplete` (exact ranks above prefix).
|
|
183
|
+
* - `autocomplete` — autocomplete fields only (pure typeahead, no word clause).
|
|
184
|
+
* - `text` — single `text` operator over all string-mapped fields.
|
|
185
|
+
*/
|
|
186
|
+
strategy?: "compound" | "autocomplete" | "text";
|
|
168
187
|
}
|
|
169
188
|
type TMongoIndex = TPlainIndex | TSearchIndex;
|
|
170
189
|
type TVectorSimilarity = "cosine" | "euclidean" | "dotProduct";
|
|
190
|
+
/**
|
|
191
|
+
* One Atlas Search field-type mapping. `type: "string"` is plain word matching;
|
|
192
|
+
* `type: "autocomplete"` enables prefix/typeahead (edgeGram) or substring (nGram).
|
|
193
|
+
* A field may carry several mappings at once (an array of these) — e.g. an
|
|
194
|
+
* autocomplete field double-mapped as `string` so exact-word hits still rank.
|
|
195
|
+
*/
|
|
196
|
+
interface TSearchFieldMapping {
|
|
197
|
+
type: string;
|
|
198
|
+
analyzer?: string;
|
|
199
|
+
tokenization?: "edgeGram" | "rightEdgeGram" | "nGram";
|
|
200
|
+
minGrams?: number;
|
|
201
|
+
maxGrams?: number;
|
|
202
|
+
foldDiacritics?: boolean;
|
|
203
|
+
}
|
|
171
204
|
interface TMongoSearchIndexDefinition {
|
|
172
205
|
mappings?: {
|
|
173
206
|
dynamic?: boolean;
|
|
174
|
-
fields?: Record<string,
|
|
175
|
-
type: string;
|
|
176
|
-
analyzer?: string;
|
|
177
|
-
}>;
|
|
207
|
+
fields?: Record<string, TSearchFieldMapping | TSearchFieldMapping[]>;
|
|
178
208
|
};
|
|
179
209
|
fields?: Array<{
|
|
180
210
|
path: string;
|
|
@@ -183,11 +213,6 @@ interface TMongoSearchIndexDefinition {
|
|
|
183
213
|
numDimensions?: number;
|
|
184
214
|
}>;
|
|
185
215
|
analyzer?: string;
|
|
186
|
-
text?: {
|
|
187
|
-
fuzzy?: {
|
|
188
|
-
maxEdits: number;
|
|
189
|
-
};
|
|
190
|
-
};
|
|
191
216
|
}
|
|
192
217
|
//#endregion
|
|
193
218
|
//#region src/lib/mongo-adapter.d.ts
|
|
@@ -351,8 +376,20 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
351
376
|
*/
|
|
352
377
|
private _getCollationOpts;
|
|
353
378
|
protected _addMongoIndexField(type: TPlainIndex["type"], name: string, field: string, weight?: number): void;
|
|
354
|
-
protected _setSearchIndex(type: TSearchIndex["type"], name: string | undefined, definition: TMongoSearchIndexDefinition
|
|
355
|
-
|
|
379
|
+
protected _setSearchIndex(type: TSearchIndex["type"], name: string | undefined, definition: TMongoSearchIndexDefinition, meta?: {
|
|
380
|
+
fuzzy?: {
|
|
381
|
+
maxEdits: number;
|
|
382
|
+
};
|
|
383
|
+
strategy?: TSearchIndex["strategy"];
|
|
384
|
+
}): TSearchIndex;
|
|
385
|
+
/**
|
|
386
|
+
* Adds (and merges, by mapping `type`) one or more Atlas field-type mappings to
|
|
387
|
+
* a `search_text` index. Multiple annotations on the same field — e.g.
|
|
388
|
+
* `@db.mongo.search.text` (string) plus `@db.mongo.search.autocomplete`
|
|
389
|
+
* (autocomplete + string) — accumulate into a single multi-type mapping
|
|
390
|
+
* instead of overwriting one another.
|
|
391
|
+
*/
|
|
392
|
+
protected _addFieldToSearchIndex(type: TSearchIndex["type"], _name: string | undefined, fieldName: string, mappings: TSearchFieldMapping[]): void;
|
|
356
393
|
}
|
|
357
394
|
//#endregion
|
|
358
395
|
//#region src/lib/mongo-filter.d.ts
|
|
@@ -371,4 +408,4 @@ declare const validateMongoIdPlugin: TValidatorPlugin;
|
|
|
371
408
|
//#region src/lib/index.d.ts
|
|
372
409
|
declare function createAdapter(connection: string, _options?: Record<string, unknown>): DbSpace;
|
|
373
410
|
//#endregion
|
|
374
|
-
export { CollectionPatcher, MongoAdapter, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };
|
|
411
|
+
export { CollectionPatcher, MongoAdapter, MongoPlugin, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { t as MongoPlugin } from "./index-Bl_O47fp.mjs";
|
|
1
2
|
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertManyResult, TDbInsertResult, TDbRelation, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
|
|
2
3
|
import { AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
|
|
3
4
|
import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
|
|
@@ -165,16 +166,45 @@ interface TSearchIndex {
|
|
|
165
166
|
name: string;
|
|
166
167
|
type: "dynamic_text" | "search_text" | "vector";
|
|
167
168
|
definition: TMongoSearchIndexDefinition;
|
|
169
|
+
/**
|
|
170
|
+
* Query-time fuzzy (typo tolerance) declared via `@db.mongo.search.dynamic` /
|
|
171
|
+
* `@db.mongo.search.static`. Carried as index metadata — NOT part of the Atlas
|
|
172
|
+
* index `definition` (Atlas applies fuzzy at query time on the `text`/
|
|
173
|
+
* `autocomplete` operator, not in the index schema). `buildSearchStage` reads
|
|
174
|
+
* this and attaches it to the emitted operator.
|
|
175
|
+
*/
|
|
176
|
+
fuzzy?: {
|
|
177
|
+
maxEdits: number;
|
|
178
|
+
};
|
|
179
|
+
/**
|
|
180
|
+
* The match strategy declared via `@db.mongo.search.static`, locking this
|
|
181
|
+
* index's query shape. `buildSearchStage` reads it; undefined → `"compound"`.
|
|
182
|
+
* - `compound` — wildcard `text` + per-field `autocomplete` (exact ranks above prefix).
|
|
183
|
+
* - `autocomplete` — autocomplete fields only (pure typeahead, no word clause).
|
|
184
|
+
* - `text` — single `text` operator over all string-mapped fields.
|
|
185
|
+
*/
|
|
186
|
+
strategy?: "compound" | "autocomplete" | "text";
|
|
168
187
|
}
|
|
169
188
|
type TMongoIndex = TPlainIndex | TSearchIndex;
|
|
170
189
|
type TVectorSimilarity = "cosine" | "euclidean" | "dotProduct";
|
|
190
|
+
/**
|
|
191
|
+
* One Atlas Search field-type mapping. `type: "string"` is plain word matching;
|
|
192
|
+
* `type: "autocomplete"` enables prefix/typeahead (edgeGram) or substring (nGram).
|
|
193
|
+
* A field may carry several mappings at once (an array of these) — e.g. an
|
|
194
|
+
* autocomplete field double-mapped as `string` so exact-word hits still rank.
|
|
195
|
+
*/
|
|
196
|
+
interface TSearchFieldMapping {
|
|
197
|
+
type: string;
|
|
198
|
+
analyzer?: string;
|
|
199
|
+
tokenization?: "edgeGram" | "rightEdgeGram" | "nGram";
|
|
200
|
+
minGrams?: number;
|
|
201
|
+
maxGrams?: number;
|
|
202
|
+
foldDiacritics?: boolean;
|
|
203
|
+
}
|
|
171
204
|
interface TMongoSearchIndexDefinition {
|
|
172
205
|
mappings?: {
|
|
173
206
|
dynamic?: boolean;
|
|
174
|
-
fields?: Record<string,
|
|
175
|
-
type: string;
|
|
176
|
-
analyzer?: string;
|
|
177
|
-
}>;
|
|
207
|
+
fields?: Record<string, TSearchFieldMapping | TSearchFieldMapping[]>;
|
|
178
208
|
};
|
|
179
209
|
fields?: Array<{
|
|
180
210
|
path: string;
|
|
@@ -183,11 +213,6 @@ interface TMongoSearchIndexDefinition {
|
|
|
183
213
|
numDimensions?: number;
|
|
184
214
|
}>;
|
|
185
215
|
analyzer?: string;
|
|
186
|
-
text?: {
|
|
187
|
-
fuzzy?: {
|
|
188
|
-
maxEdits: number;
|
|
189
|
-
};
|
|
190
|
-
};
|
|
191
216
|
}
|
|
192
217
|
//#endregion
|
|
193
218
|
//#region src/lib/mongo-adapter.d.ts
|
|
@@ -351,8 +376,20 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
351
376
|
*/
|
|
352
377
|
private _getCollationOpts;
|
|
353
378
|
protected _addMongoIndexField(type: TPlainIndex["type"], name: string, field: string, weight?: number): void;
|
|
354
|
-
protected _setSearchIndex(type: TSearchIndex["type"], name: string | undefined, definition: TMongoSearchIndexDefinition
|
|
355
|
-
|
|
379
|
+
protected _setSearchIndex(type: TSearchIndex["type"], name: string | undefined, definition: TMongoSearchIndexDefinition, meta?: {
|
|
380
|
+
fuzzy?: {
|
|
381
|
+
maxEdits: number;
|
|
382
|
+
};
|
|
383
|
+
strategy?: TSearchIndex["strategy"];
|
|
384
|
+
}): TSearchIndex;
|
|
385
|
+
/**
|
|
386
|
+
* Adds (and merges, by mapping `type`) one or more Atlas field-type mappings to
|
|
387
|
+
* a `search_text` index. Multiple annotations on the same field — e.g.
|
|
388
|
+
* `@db.mongo.search.text` (string) plus `@db.mongo.search.autocomplete`
|
|
389
|
+
* (autocomplete + string) — accumulate into a single multi-type mapping
|
|
390
|
+
* instead of overwriting one another.
|
|
391
|
+
*/
|
|
392
|
+
protected _addFieldToSearchIndex(type: TSearchIndex["type"], _name: string | undefined, fieldName: string, mappings: TSearchFieldMapping[]): void;
|
|
356
393
|
}
|
|
357
394
|
//#endregion
|
|
358
395
|
//#region src/lib/mongo-filter.d.ts
|
|
@@ -371,4 +408,4 @@ declare const validateMongoIdPlugin: TValidatorPlugin;
|
|
|
371
408
|
//#region src/lib/index.d.ts
|
|
372
409
|
declare function createAdapter(connection: string, _options?: Record<string, unknown>): DbSpace;
|
|
373
410
|
//#endregion
|
|
374
|
-
export { CollectionPatcher, MongoAdapter, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };
|
|
411
|
+
export { CollectionPatcher, MongoAdapter, MongoPlugin, TCollectionPatcherContext, type TMongoIndex, type TMongoSearchIndexDefinition, type TPlainIndex, type TSearchIndex, buildMongoFilter, createAdapter, validateMongoIdPlugin };
|