@atscript/db-mongo 0.1.127 → 0.1.128
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 +23 -6
- package/dist/agg.mjs +23 -6
- package/dist/index.cjs +35 -6
- package/dist/index.d.cts +12 -2
- package/dist/index.d.mts +12 -2
- package/dist/index.mjs +36 -7
- package/package.json +7 -7
package/dist/agg.cjs
CHANGED
|
@@ -26,19 +26,36 @@ function toAccumulator(expr) {
|
|
|
26
26
|
throw new Error(`Unsupported aggregate function: ${expr.$fn}`);
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
+
* `$group` output field names (including `_id` sub-keys) may not contain `.`,
|
|
30
|
+
* so a dotted `$groupBy` path (a JSON/nested descendant such as
|
|
31
|
+
* `metadata.clicks`) is keyed positionally inside `_id` (`k0`, `k1`, …) and
|
|
32
|
+
* projected back under its dotted path — `$project` accepts dotted output
|
|
33
|
+
* keys and nests them, which is the row shape a dotted `$select` yields on
|
|
34
|
+
* find (`{ metadata: { clicks } }`). Plain paths keep their own name.
|
|
35
|
+
*/
|
|
36
|
+
function groupIdKey(field, index) {
|
|
37
|
+
return field.includes(".") ? `k${index}` : field;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
29
40
|
* Builds the common prefix stages: $match + $group._id from groupBy fields.
|
|
30
41
|
* Shared by both full aggregate and count pipelines.
|
|
42
|
+
* `groupKeys` maps each `$groupBy` path to its `_id` sub-key.
|
|
31
43
|
*/
|
|
32
44
|
function buildPrefix(query) {
|
|
33
45
|
const controls = query.controls || {};
|
|
34
46
|
const groupBy = controls.$groupBy ?? [];
|
|
35
47
|
const pipeline = [{ $match: require_mongo_filter.buildMongoFilter(query.filter) }];
|
|
36
48
|
const groupId = {};
|
|
37
|
-
|
|
49
|
+
const groupKeys = [];
|
|
50
|
+
for (const [index, field] of groupBy.entries()) {
|
|
51
|
+
const idKey = groupIdKey(field, index);
|
|
52
|
+
groupId[idKey] = `$${field}`;
|
|
53
|
+
groupKeys.push([field, idKey]);
|
|
54
|
+
}
|
|
38
55
|
return {
|
|
39
56
|
pipeline,
|
|
40
57
|
groupId,
|
|
41
|
-
|
|
58
|
+
groupKeys,
|
|
42
59
|
controls
|
|
43
60
|
};
|
|
44
61
|
}
|
|
@@ -48,11 +65,11 @@ function buildPrefix(query) {
|
|
|
48
65
|
* Pipeline: $match → $group → $project → $match(having) → $sort → $skip → $limit
|
|
49
66
|
*/
|
|
50
67
|
function buildAggregatePipeline(query) {
|
|
51
|
-
const { pipeline, groupId,
|
|
68
|
+
const { pipeline, groupId, groupKeys, controls } = buildPrefix(query);
|
|
52
69
|
const groupStage = { _id: groupId };
|
|
53
70
|
const project = { _id: 0 };
|
|
54
71
|
const aggregates = controls.$select?.aggregates;
|
|
55
|
-
for (const field of
|
|
72
|
+
for (const [field, idKey] of groupKeys) project[field] = `$_id.${idKey}`;
|
|
56
73
|
if (aggregates) for (const expr of aggregates) {
|
|
57
74
|
const alias = (0, _atscript_db_agg.resolveAlias)(expr);
|
|
58
75
|
groupStage[alias] = toAccumulator(expr);
|
|
@@ -72,11 +89,11 @@ function buildAggregatePipeline(query) {
|
|
|
72
89
|
* Pipeline: $match → $group (just _id) → $project → $match(having) → $count
|
|
73
90
|
*/
|
|
74
91
|
function buildCountPipeline(query) {
|
|
75
|
-
const { pipeline, groupId,
|
|
92
|
+
const { pipeline, groupId, groupKeys, controls } = buildPrefix(query);
|
|
76
93
|
pipeline.push({ $group: { _id: groupId } });
|
|
77
94
|
if (controls.$having) {
|
|
78
95
|
const project = { _id: 0 };
|
|
79
|
-
for (const field of
|
|
96
|
+
for (const [field, idKey] of groupKeys) project[field] = `$_id.${idKey}`;
|
|
80
97
|
pipeline.push({ $project: project });
|
|
81
98
|
pipeline.push({ $match: require_mongo_filter.buildMongoFilter(controls.$having) });
|
|
82
99
|
}
|
package/dist/agg.mjs
CHANGED
|
@@ -25,19 +25,36 @@ function toAccumulator(expr) {
|
|
|
25
25
|
throw new Error(`Unsupported aggregate function: ${expr.$fn}`);
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
+
* `$group` output field names (including `_id` sub-keys) may not contain `.`,
|
|
29
|
+
* so a dotted `$groupBy` path (a JSON/nested descendant such as
|
|
30
|
+
* `metadata.clicks`) is keyed positionally inside `_id` (`k0`, `k1`, …) and
|
|
31
|
+
* projected back under its dotted path — `$project` accepts dotted output
|
|
32
|
+
* keys and nests them, which is the row shape a dotted `$select` yields on
|
|
33
|
+
* find (`{ metadata: { clicks } }`). Plain paths keep their own name.
|
|
34
|
+
*/
|
|
35
|
+
function groupIdKey(field, index) {
|
|
36
|
+
return field.includes(".") ? `k${index}` : field;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
28
39
|
* Builds the common prefix stages: $match + $group._id from groupBy fields.
|
|
29
40
|
* Shared by both full aggregate and count pipelines.
|
|
41
|
+
* `groupKeys` maps each `$groupBy` path to its `_id` sub-key.
|
|
30
42
|
*/
|
|
31
43
|
function buildPrefix(query) {
|
|
32
44
|
const controls = query.controls || {};
|
|
33
45
|
const groupBy = controls.$groupBy ?? [];
|
|
34
46
|
const pipeline = [{ $match: buildMongoFilter(query.filter) }];
|
|
35
47
|
const groupId = {};
|
|
36
|
-
|
|
48
|
+
const groupKeys = [];
|
|
49
|
+
for (const [index, field] of groupBy.entries()) {
|
|
50
|
+
const idKey = groupIdKey(field, index);
|
|
51
|
+
groupId[idKey] = `$${field}`;
|
|
52
|
+
groupKeys.push([field, idKey]);
|
|
53
|
+
}
|
|
37
54
|
return {
|
|
38
55
|
pipeline,
|
|
39
56
|
groupId,
|
|
40
|
-
|
|
57
|
+
groupKeys,
|
|
41
58
|
controls
|
|
42
59
|
};
|
|
43
60
|
}
|
|
@@ -47,11 +64,11 @@ function buildPrefix(query) {
|
|
|
47
64
|
* Pipeline: $match → $group → $project → $match(having) → $sort → $skip → $limit
|
|
48
65
|
*/
|
|
49
66
|
function buildAggregatePipeline(query) {
|
|
50
|
-
const { pipeline, groupId,
|
|
67
|
+
const { pipeline, groupId, groupKeys, controls } = buildPrefix(query);
|
|
51
68
|
const groupStage = { _id: groupId };
|
|
52
69
|
const project = { _id: 0 };
|
|
53
70
|
const aggregates = controls.$select?.aggregates;
|
|
54
|
-
for (const field of
|
|
71
|
+
for (const [field, idKey] of groupKeys) project[field] = `$_id.${idKey}`;
|
|
55
72
|
if (aggregates) for (const expr of aggregates) {
|
|
56
73
|
const alias = resolveAlias(expr);
|
|
57
74
|
groupStage[alias] = toAccumulator(expr);
|
|
@@ -71,11 +88,11 @@ function buildAggregatePipeline(query) {
|
|
|
71
88
|
* Pipeline: $match → $group (just _id) → $project → $match(having) → $count
|
|
72
89
|
*/
|
|
73
90
|
function buildCountPipeline(query) {
|
|
74
|
-
const { pipeline, groupId,
|
|
91
|
+
const { pipeline, groupId, groupKeys, controls } = buildPrefix(query);
|
|
75
92
|
pipeline.push({ $group: { _id: groupId } });
|
|
76
93
|
if (controls.$having) {
|
|
77
94
|
const project = { _id: 0 };
|
|
78
|
-
for (const field of
|
|
95
|
+
for (const [field, idKey] of groupKeys) project[field] = `$_id.${idKey}`;
|
|
79
96
|
pipeline.push({ $project: project });
|
|
80
97
|
pipeline.push({ $match: buildMongoFilter(controls.$having) });
|
|
81
98
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -1007,8 +1007,8 @@ function getDesiredTableOptionsImpl(cappedOptions) {
|
|
|
1007
1007
|
});
|
|
1008
1008
|
return opts;
|
|
1009
1009
|
}
|
|
1010
|
-
async function getExistingTableOptionsImpl(host) {
|
|
1011
|
-
const cols = await host.db.listCollections({ name: host._table.tableName }, { nameOnly: false }).toArray();
|
|
1010
|
+
async function getExistingTableOptionsImpl(host, tableName) {
|
|
1011
|
+
const cols = await host.db.listCollections({ name: tableName ?? host._table.tableName }, { nameOnly: false }).toArray();
|
|
1012
1012
|
if (cols.length === 0) return [];
|
|
1013
1013
|
const collOpts = cols[0].options;
|
|
1014
1014
|
if (!collOpts?.capped) return [];
|
|
@@ -1027,9 +1027,22 @@ async function getExistingTableOptionsImpl(host) {
|
|
|
1027
1027
|
return opts;
|
|
1028
1028
|
}
|
|
1029
1029
|
async function ensureTableImpl(host, table) {
|
|
1030
|
-
if (
|
|
1030
|
+
if ((0, _atscript_db.isAtscriptDbView)(table) && !table.isExternal) return ensureView(host, table);
|
|
1031
1031
|
return host.ensureCollectionExists();
|
|
1032
1032
|
}
|
|
1033
|
+
/** Whether the collection has at least one document (exact — `findOne`, not the estimated count). */
|
|
1034
|
+
async function hasRowsImpl(host, tableName) {
|
|
1035
|
+
return await host.db.collection(tableName ?? host.resolveTableName(false)).findOne({}, {
|
|
1036
|
+
projection: { _id: 1 },
|
|
1037
|
+
...host._getSessionOpts()
|
|
1038
|
+
}) !== null;
|
|
1039
|
+
}
|
|
1040
|
+
/** Kind of the object stored under `name` (`listCollections` reports views as `"view"`). */
|
|
1041
|
+
async function getObjectKindImpl(host, name) {
|
|
1042
|
+
const type = (await host.db.listCollections({ name }, { nameOnly: true }).toArray())[0]?.type;
|
|
1043
|
+
if (type === void 0) return;
|
|
1044
|
+
return type === "view" ? "view" : "table";
|
|
1045
|
+
}
|
|
1033
1046
|
/** Creates a MongoDB view from the AtscriptDbView's view plan. */
|
|
1034
1047
|
async function ensureView(host, view) {
|
|
1035
1048
|
if (await host.collectionExists()) return;
|
|
@@ -1554,7 +1567,7 @@ const OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;
|
|
|
1554
1567
|
function isObjectIdColumn(fieldType) {
|
|
1555
1568
|
const t = fieldType.type;
|
|
1556
1569
|
if (t.kind === "array") return !!t.of && isObjectIdColumn(t.of);
|
|
1557
|
-
return t.tags?.has("objectId") === true && t.tags?.has("mongo")
|
|
1570
|
+
return t.tags?.has("objectId") === true && t.tags?.has("mongo");
|
|
1558
1571
|
}
|
|
1559
1572
|
function objectIdToStorage(value) {
|
|
1560
1573
|
if (typeof value === "string" && OBJECT_ID_HEX.test(value)) return new mongodb.ObjectId(value);
|
|
@@ -1604,6 +1617,10 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
1604
1617
|
get _client() {
|
|
1605
1618
|
return this.client;
|
|
1606
1619
|
}
|
|
1620
|
+
/** Every adapter over this client (or database handle) shares one session (since 0.1.128). */
|
|
1621
|
+
_transactionOwner() {
|
|
1622
|
+
return this.client ?? this.db;
|
|
1623
|
+
}
|
|
1607
1624
|
/**
|
|
1608
1625
|
* Per-client cache: whether transactions are unavailable (standalone MongoDB).
|
|
1609
1626
|
* Shared across all adapter instances for the same client so topology is probed once.
|
|
@@ -2163,6 +2180,18 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
2163
2180
|
async ensureTable() {
|
|
2164
2181
|
return ensureTableImpl(this, this._table);
|
|
2165
2182
|
}
|
|
2183
|
+
async hasRows(tableName) {
|
|
2184
|
+
return hasRowsImpl(this, tableName);
|
|
2185
|
+
}
|
|
2186
|
+
async getObjectKind(name) {
|
|
2187
|
+
return getObjectKindImpl(this, name);
|
|
2188
|
+
}
|
|
2189
|
+
/**
|
|
2190
|
+
* No physical primary key on MongoDB (`_id` is fixed): a `@meta.id` move is
|
|
2191
|
+
* an index change that `syncIndexes` reconciles. Schema sync refuses the
|
|
2192
|
+
* change on a populated collection for cross-adapter consistency.
|
|
2193
|
+
*/
|
|
2194
|
+
async rebuildPrimaryKey(_change) {}
|
|
2166
2195
|
async syncIndexes() {
|
|
2167
2196
|
return syncIndexesImpl(this);
|
|
2168
2197
|
}
|
|
@@ -2190,8 +2219,8 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
|
|
|
2190
2219
|
getDesiredTableOptions() {
|
|
2191
2220
|
return getDesiredTableOptionsImpl(this._cappedOptions);
|
|
2192
2221
|
}
|
|
2193
|
-
async getExistingTableOptions() {
|
|
2194
|
-
return getExistingTableOptionsImpl(this);
|
|
2222
|
+
async getExistingTableOptions(tableName) {
|
|
2223
|
+
return getExistingTableOptionsImpl(this, tableName);
|
|
2195
2224
|
}
|
|
2196
2225
|
destructiveOptionKeys() {
|
|
2197
2226
|
return DESTRUCTIVE_OPTION_KEYS;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
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";
|
|
1
|
+
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbRelation, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TPrimaryKeyChange, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
|
|
2
2
|
import { AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
|
|
3
3
|
import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
@@ -268,6 +268,8 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
268
268
|
private _pendingUniqueFields;
|
|
269
269
|
constructor(db: Db, client?: MongoClient | undefined);
|
|
270
270
|
private get _client();
|
|
271
|
+
/** Every adapter over this client (or database handle) shares one session (since 0.1.128). */
|
|
272
|
+
protected _transactionOwner(): unknown;
|
|
271
273
|
/**
|
|
272
274
|
* Per-client cache: whether transactions are unavailable (standalone MongoDB).
|
|
273
275
|
* Shared across all adapter instances for the same client so topology is probed once.
|
|
@@ -393,6 +395,14 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
393
395
|
clearCollectionCache(): void;
|
|
394
396
|
tableExists(): Promise<boolean>;
|
|
395
397
|
ensureTable(): Promise<void>;
|
|
398
|
+
hasRows(tableName?: string): Promise<boolean>;
|
|
399
|
+
getObjectKind(name: string): Promise<TDbObjectKind | undefined>;
|
|
400
|
+
/**
|
|
401
|
+
* No physical primary key on MongoDB (`_id` is fixed): a `@meta.id` move is
|
|
402
|
+
* an index change that `syncIndexes` reconciles. Schema sync refuses the
|
|
403
|
+
* change on a populated collection for cross-adapter consistency.
|
|
404
|
+
*/
|
|
405
|
+
rebuildPrimaryKey(_change: TPrimaryKeyChange): Promise<void>;
|
|
396
406
|
syncIndexes(): Promise<void>;
|
|
397
407
|
syncColumns(diff: TColumnDiff): Promise<TSyncColumnResult>;
|
|
398
408
|
dropColumns(columns: string[]): Promise<void>;
|
|
@@ -402,7 +412,7 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
402
412
|
dropViewByName(viewName: string): Promise<void>;
|
|
403
413
|
dropTableByName(tableName: string): Promise<void>;
|
|
404
414
|
getDesiredTableOptions(): TExistingTableOption[];
|
|
405
|
-
getExistingTableOptions(): Promise<TExistingTableOption[]>;
|
|
415
|
+
getExistingTableOptions(tableName?: string): Promise<TExistingTableOption[]>;
|
|
406
416
|
destructiveOptionKeys(): ReadonlySet<string>;
|
|
407
417
|
/** Returns the counters collection used for atomic auto-increment. */
|
|
408
418
|
protected get _countersCollection(): Collection<{
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
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";
|
|
1
|
+
import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbRelation, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TPrimaryKeyChange, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
|
|
2
2
|
import { AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
|
|
3
3
|
import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
|
|
4
4
|
|
|
@@ -268,6 +268,8 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
268
268
|
private _pendingUniqueFields;
|
|
269
269
|
constructor(db: Db, client?: MongoClient | undefined);
|
|
270
270
|
private get _client();
|
|
271
|
+
/** Every adapter over this client (or database handle) shares one session (since 0.1.128). */
|
|
272
|
+
protected _transactionOwner(): unknown;
|
|
271
273
|
/**
|
|
272
274
|
* Per-client cache: whether transactions are unavailable (standalone MongoDB).
|
|
273
275
|
* Shared across all adapter instances for the same client so topology is probed once.
|
|
@@ -393,6 +395,14 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
393
395
|
clearCollectionCache(): void;
|
|
394
396
|
tableExists(): Promise<boolean>;
|
|
395
397
|
ensureTable(): Promise<void>;
|
|
398
|
+
hasRows(tableName?: string): Promise<boolean>;
|
|
399
|
+
getObjectKind(name: string): Promise<TDbObjectKind | undefined>;
|
|
400
|
+
/**
|
|
401
|
+
* No physical primary key on MongoDB (`_id` is fixed): a `@meta.id` move is
|
|
402
|
+
* an index change that `syncIndexes` reconciles. Schema sync refuses the
|
|
403
|
+
* change on a populated collection for cross-adapter consistency.
|
|
404
|
+
*/
|
|
405
|
+
rebuildPrimaryKey(_change: TPrimaryKeyChange): Promise<void>;
|
|
396
406
|
syncIndexes(): Promise<void>;
|
|
397
407
|
syncColumns(diff: TColumnDiff): Promise<TSyncColumnResult>;
|
|
398
408
|
dropColumns(columns: string[]): Promise<void>;
|
|
@@ -402,7 +412,7 @@ declare class MongoAdapter extends BaseDbAdapter {
|
|
|
402
412
|
dropViewByName(viewName: string): Promise<void>;
|
|
403
413
|
dropTableByName(tableName: string): Promise<void>;
|
|
404
414
|
getDesiredTableOptions(): TExistingTableOption[];
|
|
405
|
-
getExistingTableOptions(): Promise<TExistingTableOption[]>;
|
|
415
|
+
getExistingTableOptions(tableName?: string): Promise<TExistingTableOption[]>;
|
|
406
416
|
destructiveOptionKeys(): ReadonlySet<string>;
|
|
407
417
|
/** Returns the counters collection used for atomic auto-increment. */
|
|
408
418
|
protected get _countersCollection(): Collection<{
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as buildMongoFilter } from "./mongo-filter-DceAGI-S.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { BaseDbAdapter, DbError, DbSpace, computeInsights, createFailureCollector, getDbFieldOp, getKeyProps, isAtscriptDbView, resolveDesignType } from "@atscript/db";
|
|
3
3
|
import { MongoClient, MongoServerError, ObjectId } from "mongodb";
|
|
4
4
|
//#region src/lib/path-utils.ts
|
|
5
5
|
function joinPath(prefix, segment) {
|
|
@@ -1006,8 +1006,8 @@ function getDesiredTableOptionsImpl(cappedOptions) {
|
|
|
1006
1006
|
});
|
|
1007
1007
|
return opts;
|
|
1008
1008
|
}
|
|
1009
|
-
async function getExistingTableOptionsImpl(host) {
|
|
1010
|
-
const cols = await host.db.listCollections({ name: host._table.tableName }, { nameOnly: false }).toArray();
|
|
1009
|
+
async function getExistingTableOptionsImpl(host, tableName) {
|
|
1010
|
+
const cols = await host.db.listCollections({ name: tableName ?? host._table.tableName }, { nameOnly: false }).toArray();
|
|
1011
1011
|
if (cols.length === 0) return [];
|
|
1012
1012
|
const collOpts = cols[0].options;
|
|
1013
1013
|
if (!collOpts?.capped) return [];
|
|
@@ -1026,9 +1026,22 @@ async function getExistingTableOptionsImpl(host) {
|
|
|
1026
1026
|
return opts;
|
|
1027
1027
|
}
|
|
1028
1028
|
async function ensureTableImpl(host, table) {
|
|
1029
|
-
if (table
|
|
1029
|
+
if (isAtscriptDbView(table) && !table.isExternal) return ensureView(host, table);
|
|
1030
1030
|
return host.ensureCollectionExists();
|
|
1031
1031
|
}
|
|
1032
|
+
/** Whether the collection has at least one document (exact — `findOne`, not the estimated count). */
|
|
1033
|
+
async function hasRowsImpl(host, tableName) {
|
|
1034
|
+
return await host.db.collection(tableName ?? host.resolveTableName(false)).findOne({}, {
|
|
1035
|
+
projection: { _id: 1 },
|
|
1036
|
+
...host._getSessionOpts()
|
|
1037
|
+
}) !== null;
|
|
1038
|
+
}
|
|
1039
|
+
/** Kind of the object stored under `name` (`listCollections` reports views as `"view"`). */
|
|
1040
|
+
async function getObjectKindImpl(host, name) {
|
|
1041
|
+
const type = (await host.db.listCollections({ name }, { nameOnly: true }).toArray())[0]?.type;
|
|
1042
|
+
if (type === void 0) return;
|
|
1043
|
+
return type === "view" ? "view" : "table";
|
|
1044
|
+
}
|
|
1032
1045
|
/** Creates a MongoDB view from the AtscriptDbView's view plan. */
|
|
1033
1046
|
async function ensureView(host, view) {
|
|
1034
1047
|
if (await host.collectionExists()) return;
|
|
@@ -1553,7 +1566,7 @@ const OBJECT_ID_HEX = /^[a-fA-F0-9]{24}$/;
|
|
|
1553
1566
|
function isObjectIdColumn(fieldType) {
|
|
1554
1567
|
const t = fieldType.type;
|
|
1555
1568
|
if (t.kind === "array") return !!t.of && isObjectIdColumn(t.of);
|
|
1556
|
-
return t.tags?.has("objectId") === true && t.tags?.has("mongo")
|
|
1569
|
+
return t.tags?.has("objectId") === true && t.tags?.has("mongo");
|
|
1557
1570
|
}
|
|
1558
1571
|
function objectIdToStorage(value) {
|
|
1559
1572
|
if (typeof value === "string" && OBJECT_ID_HEX.test(value)) return new ObjectId(value);
|
|
@@ -1603,6 +1616,10 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
|
|
|
1603
1616
|
get _client() {
|
|
1604
1617
|
return this.client;
|
|
1605
1618
|
}
|
|
1619
|
+
/** Every adapter over this client (or database handle) shares one session (since 0.1.128). */
|
|
1620
|
+
_transactionOwner() {
|
|
1621
|
+
return this.client ?? this.db;
|
|
1622
|
+
}
|
|
1606
1623
|
/**
|
|
1607
1624
|
* Per-client cache: whether transactions are unavailable (standalone MongoDB).
|
|
1608
1625
|
* Shared across all adapter instances for the same client so topology is probed once.
|
|
@@ -2162,6 +2179,18 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
|
|
|
2162
2179
|
async ensureTable() {
|
|
2163
2180
|
return ensureTableImpl(this, this._table);
|
|
2164
2181
|
}
|
|
2182
|
+
async hasRows(tableName) {
|
|
2183
|
+
return hasRowsImpl(this, tableName);
|
|
2184
|
+
}
|
|
2185
|
+
async getObjectKind(name) {
|
|
2186
|
+
return getObjectKindImpl(this, name);
|
|
2187
|
+
}
|
|
2188
|
+
/**
|
|
2189
|
+
* No physical primary key on MongoDB (`_id` is fixed): a `@meta.id` move is
|
|
2190
|
+
* an index change that `syncIndexes` reconciles. Schema sync refuses the
|
|
2191
|
+
* change on a populated collection for cross-adapter consistency.
|
|
2192
|
+
*/
|
|
2193
|
+
async rebuildPrimaryKey(_change) {}
|
|
2165
2194
|
async syncIndexes() {
|
|
2166
2195
|
return syncIndexesImpl(this);
|
|
2167
2196
|
}
|
|
@@ -2189,8 +2218,8 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
|
|
|
2189
2218
|
getDesiredTableOptions() {
|
|
2190
2219
|
return getDesiredTableOptionsImpl(this._cappedOptions);
|
|
2191
2220
|
}
|
|
2192
|
-
async getExistingTableOptions() {
|
|
2193
|
-
return getExistingTableOptionsImpl(this);
|
|
2221
|
+
async getExistingTableOptions(tableName) {
|
|
2222
|
+
return getExistingTableOptionsImpl(this, tableName);
|
|
2194
2223
|
}
|
|
2195
2224
|
destructiveOptionKeys() {
|
|
2196
2225
|
return DESTRUCTIVE_OPTION_KEYS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atscript/db-mongo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.128",
|
|
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.92",
|
|
50
|
+
"@atscript/typescript": "^0.1.92",
|
|
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.92"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
|
-
"@atscript/core": "^0.1.
|
|
57
|
-
"@atscript/typescript": "^0.1.
|
|
56
|
+
"@atscript/core": "^0.1.92",
|
|
57
|
+
"@atscript/typescript": "^0.1.92",
|
|
58
58
|
"mongodb": "^6.17.0",
|
|
59
|
-
"@atscript/db": "^0.1.
|
|
59
|
+
"@atscript/db": "^0.1.128"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"postinstall": "asc -f dts",
|