@atscript/db-mongo 0.1.164 → 0.1.165
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 +30 -13
- package/dist/index.mjs +31 -14
- package/package.json +7 -7
package/dist/index.cjs
CHANGED
|
@@ -1025,21 +1025,36 @@ function filterOptionsOf(host) {
|
|
|
1025
1025
|
return { collation: require_mongo_accumulator.collationOfAdapter(host) };
|
|
1026
1026
|
}
|
|
1027
1027
|
/**
|
|
1028
|
-
* Returns available search indexes as generic metadata for UI.
|
|
1029
|
-
*
|
|
1030
|
-
* with when none is named)
|
|
1028
|
+
* Returns available search indexes as generic metadata for UI. Each index is
|
|
1029
|
+
* listed once, by its declared name; the text default (the index `search()`
|
|
1030
|
+
* answers with when none is named) carries `isDefault` and lists `DEFAULT` —
|
|
1031
|
+
* which also resolves — as an alias (since 0.1.165). Only an index without a
|
|
1032
|
+
* name of its own (a dynamic Atlas mapping) is listed as `DEFAULT`. The vector
|
|
1033
|
+
* default is the first vector index.
|
|
1031
1034
|
*/
|
|
1035
|
+
/** Classic `$text` relevance order: `textScore` descending, `_id` breaking ties (since 0.1.165). */
|
|
1036
|
+
const RELEVANCE_SORT = { $sort: {
|
|
1037
|
+
_score: -1,
|
|
1038
|
+
_id: 1
|
|
1039
|
+
} };
|
|
1032
1040
|
function getSearchIndexesImpl(host) {
|
|
1033
1041
|
const result = [];
|
|
1042
|
+
const indexes = host.getMongoSearchIndexes();
|
|
1043
|
+
const textDefault = indexes.get(DEFAULT_INDEX_NAME);
|
|
1044
|
+
let defaultListedByName = false;
|
|
1045
|
+
for (const [name, index] of indexes) if (index === textDefault && name !== "DEFAULT") defaultListedByName = true;
|
|
1034
1046
|
let firstVector = true;
|
|
1035
|
-
for (const [name, index] of
|
|
1047
|
+
for (const [name, index] of indexes) {
|
|
1036
1048
|
const isVector = index.type === "vector";
|
|
1049
|
+
const aliased = !isVector && index === textDefault && name !== "DEFAULT";
|
|
1050
|
+
if (name === "DEFAULT" && defaultListedByName) continue;
|
|
1037
1051
|
result.push({
|
|
1038
1052
|
name,
|
|
1053
|
+
...aliased ? { aliases: [DEFAULT_INDEX_NAME] } : {},
|
|
1039
1054
|
description: `${index.type} index`,
|
|
1040
1055
|
type: isVector ? "vector" : "text",
|
|
1041
1056
|
fields: searchIndexPaths(host, index),
|
|
1042
|
-
isDefault: isVector ? firstVector :
|
|
1057
|
+
isDefault: isVector ? firstVector : index === textDefault
|
|
1043
1058
|
});
|
|
1044
1059
|
if (isVector) firstVector = false;
|
|
1045
1060
|
}
|
|
@@ -1514,7 +1529,7 @@ async function runSearchPipeline(host, stage, query, label, { threshold, classic
|
|
|
1514
1529
|
pipeline.push(...filterStages ?? require_mongo_accumulator.mongoFilterStages(query.filter, filterOptionsOf(host)));
|
|
1515
1530
|
const { stages: sorted, cleanup } = require_mongo_accumulator.sortStages(controls);
|
|
1516
1531
|
if (sorted.length > 0) pipeline.push(...sorted);
|
|
1517
|
-
else if (classicText) pipeline.push(
|
|
1532
|
+
else if (classicText) pipeline.push(RELEVANCE_SORT);
|
|
1518
1533
|
if (controls.$skip) pipeline.push({ $skip: controls.$skip });
|
|
1519
1534
|
if (controls.$limit) pipeline.push({ $limit: controls.$limit });
|
|
1520
1535
|
else pipeline.push({ $limit: 1e3 });
|
|
@@ -1544,7 +1559,7 @@ async function runSearchWithCountPipeline(host, stage, query, label, { threshold
|
|
|
1544
1559
|
preStages.push({ $match: { _score: { $gte: threshold } } });
|
|
1545
1560
|
} else if (classicText) preStages.push({ $addFields: { _score: { $meta: "textScore" } } });
|
|
1546
1561
|
const { stages: dataStages, cleanup } = require_mongo_accumulator.sortStages(controls);
|
|
1547
|
-
if (dataStages.length === 0 && classicText) dataStages.push(
|
|
1562
|
+
if (dataStages.length === 0 && classicText) dataStages.push(RELEVANCE_SORT);
|
|
1548
1563
|
if (controls.$skip) dataStages.push({ $skip: controls.$skip });
|
|
1549
1564
|
if (controls.$limit) dataStages.push({ $limit: controls.$limit });
|
|
1550
1565
|
if (cleanup) dataStages.push(cleanup);
|
|
@@ -2791,7 +2806,7 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
2791
2806
|
weights: Object.fromEntries(index.fields.filter((f) => f.weight && !f.integer).map((f) => [f.name, f.weight]))
|
|
2792
2807
|
};
|
|
2793
2808
|
if (index === defaultFulltext) defaultIndex = converted;
|
|
2794
|
-
|
|
2809
|
+
if (!this._searchIndexesMap.has(index.name)) this._searchIndexesMap.set(index.name, converted);
|
|
2795
2810
|
}
|
|
2796
2811
|
for (const index of this._mongoIndexes.values()) switch (index.type) {
|
|
2797
2812
|
case "text":
|
|
@@ -3027,10 +3042,10 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
3027
3042
|
}
|
|
3028
3043
|
/** Rethrows a duplicate-key (11000) server error as `CONFLICT`, anything else as is. */
|
|
3029
3044
|
_mapConstraintError(error) {
|
|
3030
|
-
if (error instanceof mongodb.MongoServerError && error.code === 11e3) throw
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
}
|
|
3045
|
+
if (error instanceof mongodb.MongoServerError && error.code === 11e3) throw (0, _atscript_db.constraintViolation)("CONFLICT", {
|
|
3046
|
+
columns: error.keyPattern ? Object.keys(error.keyPattern) : void 0,
|
|
3047
|
+
name: error.message.match(/index: (\S+) dup key/)?.[1]
|
|
3048
|
+
}, error.message);
|
|
3034
3049
|
throw error;
|
|
3035
3050
|
}
|
|
3036
3051
|
/** An update-shaped write under {@link _wrapDuplicateKeyError}, reduced to `TDbUpdateResult`. */
|
|
@@ -3226,11 +3241,13 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
3226
3241
|
};
|
|
3227
3242
|
const versionColumn = this._table.versionColumnPhysical;
|
|
3228
3243
|
const counters = this._table.onUpdateIncPhysical;
|
|
3244
|
+
const keep = this._table.insertOnlyPhysical;
|
|
3229
3245
|
this._log("replaceOne", mongoFilter, data);
|
|
3230
|
-
if (versionColumn !== void 0 || counters.length > 0) {
|
|
3246
|
+
if (versionColumn !== void 0 || counters.length > 0 || keep.length > 0) {
|
|
3231
3247
|
const replacement = {};
|
|
3232
3248
|
for (const [key, value] of Object.entries(data)) replacement[key] = { $literal: value };
|
|
3233
3249
|
for (const col of counters) replacement[col] = incExpr(col, 1);
|
|
3250
|
+
for (const col of keep) replacement[col] = `$${col}`;
|
|
3234
3251
|
if (versionColumn !== void 0) replacement[versionColumn] = { $add: [`$${versionColumn}`, 1] };
|
|
3235
3252
|
const pipeline = [{ $replaceWith: replacement }];
|
|
3236
3253
|
return this._wrapUpdate(() => this.collection.updateOne(mongoFilter, pipeline, this._getSessionOpts()));
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { a as buildMongoQuery, c as planStages, f as sortStages, g as queryNodeToExpr, h as orNull, i as buildMongoFilter, l as correlate, n as distinctCountExpr, o as collationOfAdapter, p as exprToMongo, r as REL_FILTER_TEMP_PREFIX, s as mongoFilterStages, t as buildAccumulator, u as hasNullsPlacement } from "./mongo-accumulator-DMPFOscc.mjs";
|
|
2
2
|
import { t as dynamicSearchEncryptedMessage } from "./search-messages-mQSpJ4GD.mjs";
|
|
3
|
-
import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, ALL_VIEW_CAPABILITIES, BaseDbAdapter, DbError, DbSpace, andFilters, bucketTimeZoneUnavailable, computeInsights, containsRelationFilter, createChangeRecorder, createFailureCollector, defaultFulltextIndex, geoIndexNotFoundMessage, getDbFieldOp, getKeyProps, getPath, isAtscriptDbView, isFieldRef, isPlainObject, isViewType, queryReadColumns, relationStaticFilter, resolveDesignType, searchIndexNotFoundMessage, searchTermInteger, tableNameOf, translateQueryTree, uniqueKeyTuple, vectorIndexNotFoundMessage } from "@atscript/db";
|
|
3
|
+
import { ALL_AGGREGATE_FNS, ALL_BUCKET_UNITS, ALL_VIEW_CAPABILITIES, BaseDbAdapter, DbError, DbSpace, andFilters, bucketTimeZoneUnavailable, computeInsights, constraintViolation, containsRelationFilter, createChangeRecorder, createFailureCollector, defaultFulltextIndex, geoIndexNotFoundMessage, getDbFieldOp, getKeyProps, getPath, isAtscriptDbView, isFieldRef, isPlainObject, isViewType, queryReadColumns, relationStaticFilter, resolveDesignType, searchIndexNotFoundMessage, searchTermInteger, tableNameOf, translateQueryTree, uniqueKeyTuple, vectorIndexNotFoundMessage } from "@atscript/db";
|
|
4
4
|
import { MongoBulkWriteError, MongoClient, MongoServerError, ObjectId } from "mongodb";
|
|
5
5
|
import { resolveAggregateSearch } from "@atscript/db/agg";
|
|
6
6
|
//#region src/lib/path-utils.ts
|
|
@@ -1024,21 +1024,36 @@ function filterOptionsOf(host) {
|
|
|
1024
1024
|
return { collation: collationOfAdapter(host) };
|
|
1025
1025
|
}
|
|
1026
1026
|
/**
|
|
1027
|
-
* Returns available search indexes as generic metadata for UI.
|
|
1028
|
-
*
|
|
1029
|
-
* with when none is named)
|
|
1027
|
+
* Returns available search indexes as generic metadata for UI. Each index is
|
|
1028
|
+
* listed once, by its declared name; the text default (the index `search()`
|
|
1029
|
+
* answers with when none is named) carries `isDefault` and lists `DEFAULT` —
|
|
1030
|
+
* which also resolves — as an alias (since 0.1.165). Only an index without a
|
|
1031
|
+
* name of its own (a dynamic Atlas mapping) is listed as `DEFAULT`. The vector
|
|
1032
|
+
* default is the first vector index.
|
|
1030
1033
|
*/
|
|
1034
|
+
/** Classic `$text` relevance order: `textScore` descending, `_id` breaking ties (since 0.1.165). */
|
|
1035
|
+
const RELEVANCE_SORT = { $sort: {
|
|
1036
|
+
_score: -1,
|
|
1037
|
+
_id: 1
|
|
1038
|
+
} };
|
|
1031
1039
|
function getSearchIndexesImpl(host) {
|
|
1032
1040
|
const result = [];
|
|
1041
|
+
const indexes = host.getMongoSearchIndexes();
|
|
1042
|
+
const textDefault = indexes.get(DEFAULT_INDEX_NAME);
|
|
1043
|
+
let defaultListedByName = false;
|
|
1044
|
+
for (const [name, index] of indexes) if (index === textDefault && name !== "DEFAULT") defaultListedByName = true;
|
|
1033
1045
|
let firstVector = true;
|
|
1034
|
-
for (const [name, index] of
|
|
1046
|
+
for (const [name, index] of indexes) {
|
|
1035
1047
|
const isVector = index.type === "vector";
|
|
1048
|
+
const aliased = !isVector && index === textDefault && name !== "DEFAULT";
|
|
1049
|
+
if (name === "DEFAULT" && defaultListedByName) continue;
|
|
1036
1050
|
result.push({
|
|
1037
1051
|
name,
|
|
1052
|
+
...aliased ? { aliases: [DEFAULT_INDEX_NAME] } : {},
|
|
1038
1053
|
description: `${index.type} index`,
|
|
1039
1054
|
type: isVector ? "vector" : "text",
|
|
1040
1055
|
fields: searchIndexPaths(host, index),
|
|
1041
|
-
isDefault: isVector ? firstVector :
|
|
1056
|
+
isDefault: isVector ? firstVector : index === textDefault
|
|
1042
1057
|
});
|
|
1043
1058
|
if (isVector) firstVector = false;
|
|
1044
1059
|
}
|
|
@@ -1513,7 +1528,7 @@ async function runSearchPipeline(host, stage, query, label, { threshold, classic
|
|
|
1513
1528
|
pipeline.push(...filterStages ?? mongoFilterStages(query.filter, filterOptionsOf(host)));
|
|
1514
1529
|
const { stages: sorted, cleanup } = sortStages(controls);
|
|
1515
1530
|
if (sorted.length > 0) pipeline.push(...sorted);
|
|
1516
|
-
else if (classicText) pipeline.push(
|
|
1531
|
+
else if (classicText) pipeline.push(RELEVANCE_SORT);
|
|
1517
1532
|
if (controls.$skip) pipeline.push({ $skip: controls.$skip });
|
|
1518
1533
|
if (controls.$limit) pipeline.push({ $limit: controls.$limit });
|
|
1519
1534
|
else pipeline.push({ $limit: 1e3 });
|
|
@@ -1543,7 +1558,7 @@ async function runSearchWithCountPipeline(host, stage, query, label, { threshold
|
|
|
1543
1558
|
preStages.push({ $match: { _score: { $gte: threshold } } });
|
|
1544
1559
|
} else if (classicText) preStages.push({ $addFields: { _score: { $meta: "textScore" } } });
|
|
1545
1560
|
const { stages: dataStages, cleanup } = sortStages(controls);
|
|
1546
|
-
if (dataStages.length === 0 && classicText) dataStages.push(
|
|
1561
|
+
if (dataStages.length === 0 && classicText) dataStages.push(RELEVANCE_SORT);
|
|
1547
1562
|
if (controls.$skip) dataStages.push({ $skip: controls.$skip });
|
|
1548
1563
|
if (controls.$limit) dataStages.push({ $limit: controls.$limit });
|
|
1549
1564
|
if (cleanup) dataStages.push(cleanup);
|
|
@@ -2790,7 +2805,7 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
|
|
|
2790
2805
|
weights: Object.fromEntries(index.fields.filter((f) => f.weight && !f.integer).map((f) => [f.name, f.weight]))
|
|
2791
2806
|
};
|
|
2792
2807
|
if (index === defaultFulltext) defaultIndex = converted;
|
|
2793
|
-
|
|
2808
|
+
if (!this._searchIndexesMap.has(index.name)) this._searchIndexesMap.set(index.name, converted);
|
|
2794
2809
|
}
|
|
2795
2810
|
for (const index of this._mongoIndexes.values()) switch (index.type) {
|
|
2796
2811
|
case "text":
|
|
@@ -3026,10 +3041,10 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
|
|
|
3026
3041
|
}
|
|
3027
3042
|
/** Rethrows a duplicate-key (11000) server error as `CONFLICT`, anything else as is. */
|
|
3028
3043
|
_mapConstraintError(error) {
|
|
3029
|
-
if (error instanceof MongoServerError && error.code === 11e3) throw
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
}
|
|
3044
|
+
if (error instanceof MongoServerError && error.code === 11e3) throw constraintViolation("CONFLICT", {
|
|
3045
|
+
columns: error.keyPattern ? Object.keys(error.keyPattern) : void 0,
|
|
3046
|
+
name: error.message.match(/index: (\S+) dup key/)?.[1]
|
|
3047
|
+
}, error.message);
|
|
3033
3048
|
throw error;
|
|
3034
3049
|
}
|
|
3035
3050
|
/** An update-shaped write under {@link _wrapDuplicateKeyError}, reduced to `TDbUpdateResult`. */
|
|
@@ -3225,11 +3240,13 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
|
|
|
3225
3240
|
};
|
|
3226
3241
|
const versionColumn = this._table.versionColumnPhysical;
|
|
3227
3242
|
const counters = this._table.onUpdateIncPhysical;
|
|
3243
|
+
const keep = this._table.insertOnlyPhysical;
|
|
3228
3244
|
this._log("replaceOne", mongoFilter, data);
|
|
3229
|
-
if (versionColumn !== void 0 || counters.length > 0) {
|
|
3245
|
+
if (versionColumn !== void 0 || counters.length > 0 || keep.length > 0) {
|
|
3230
3246
|
const replacement = {};
|
|
3231
3247
|
for (const [key, value] of Object.entries(data)) replacement[key] = { $literal: value };
|
|
3232
3248
|
for (const col of counters) replacement[col] = incExpr(col, 1);
|
|
3249
|
+
for (const col of keep) replacement[col] = `$${col}`;
|
|
3233
3250
|
if (versionColumn !== void 0) replacement[versionColumn] = { $add: [`$${versionColumn}`, 1] };
|
|
3234
3251
|
const pipeline = [{ $replaceWith: replacement }];
|
|
3235
3252
|
return this._wrapUpdate(() => this.collection.updateOne(mongoFilter, pipeline, this._getSessionOpts()));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db-mongo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.165",
|
|
4
4
|
"description": "Mongodb plugin for atscript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"atscript",
|
|
@@ -46,19 +46,19 @@
|
|
|
46
46
|
"access": "public"
|
|
47
47
|
},
|
|
48
48
|
"devDependencies": {
|
|
49
|
-
"@atscript/core": "^0.1
|
|
50
|
-
"@atscript/typescript": "^0.1
|
|
49
|
+
"@atscript/core": "^0.2.1",
|
|
50
|
+
"@atscript/typescript": "^0.2.1",
|
|
51
51
|
"@uniqu/core": "^0.1.15",
|
|
52
52
|
"mongodb": "^6.17.0",
|
|
53
53
|
"mongodb-memory-server-core": "^10.0.0",
|
|
54
|
-
"unplugin-atscript": "^0.1
|
|
54
|
+
"unplugin-atscript": "^0.2.1"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@atscript/core": "^0.1
|
|
58
|
-
"@atscript/typescript": "^0.1
|
|
57
|
+
"@atscript/core": "^0.2.1",
|
|
58
|
+
"@atscript/typescript": "^0.2.1",
|
|
59
59
|
"@uniqu/core": "^0.1.15",
|
|
60
60
|
"mongodb": "^6.17.0",
|
|
61
|
-
"@atscript/db": "^0.1.
|
|
61
|
+
"@atscript/db": "^0.1.165"
|
|
62
62
|
},
|
|
63
63
|
"scripts": {
|
|
64
64
|
"build": "vp pack",
|