@atscript/db-mongo 0.1.155 → 0.1.156

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 CHANGED
@@ -1770,6 +1770,27 @@ async function dropColumnsImpl(host, columns) {
1770
1770
  await host.collection.updateMany({}, { $unset: unsetSpec }, host._getSessionOpts());
1771
1771
  }
1772
1772
  /**
1773
+ * Drops the managed indexes over any of `columns` (or a path under one).
1774
+ * Schema sync calls it before {@link dropColumnsImpl}: a unique index on a
1775
+ * required field indexes a `$unset` document as `null`, so the second one
1776
+ * collides (E11000) — e.g. the old key field's `__pk` index when a `@meta.id`
1777
+ * move removes that field. `syncIndexesImpl` then builds what the model wants.
1778
+ */
1779
+ async function dropIndexesForColumnsImpl(host, columns) {
1780
+ const dropped = new Set(columns);
1781
+ let existing;
1782
+ try {
1783
+ existing = await host.collection.listIndexes().toArray();
1784
+ } catch (error) {
1785
+ if (error.code === 26) return;
1786
+ throw error;
1787
+ }
1788
+ for (const remote of existing) if (remote.name.startsWith("atscript__") && coversAny(remote.key, remote.weights, dropped)) {
1789
+ host._log("dropIndex", remote.name);
1790
+ await host.collection.dropIndex(remote.name);
1791
+ }
1792
+ }
1793
+ /**
1773
1794
  * The segments of a field's physical document path with `$[]` after every
1774
1795
  * segment whose logical prefix is an array in the flatMap — Mongo's
1775
1796
  * all-positional operator walks every element. `physicalPath` defaults to
@@ -1803,7 +1824,23 @@ function resolveSyncDefault(field) {
1803
1824
  if (!field.defaultValue) return;
1804
1825
  if (field.defaultValue.kind === "value") return field.designType === "string" ? field.defaultValue.value : JSON.parse(field.defaultValue.value);
1805
1826
  }
1806
- async function syncIndexesImpl(host) {
1827
+ /**
1828
+ * Whether an index's fields include one of `paths` or a path under one —
1829
+ * a text index lists its fields in `weights` (its key is `_fts` / `_ftsx`).
1830
+ */
1831
+ function coversAny(key, weights, paths) {
1832
+ return [...Object.keys(key), ...Object.keys(weights ?? {})].some((f) => paths.has(f) || hasAncestorIn(f, paths));
1833
+ }
1834
+ /** The `__pk` unique index of non-`_id` `@meta.id` fields. */
1835
+ const PK_INDEX = mongoIndexKey("unique", "__pk");
1836
+ /**
1837
+ * Reconciles the managed indexes with the model. `opts` (schema sync, safe
1838
+ * mode) names the indexes that stay as they are — neither dropped, replaced
1839
+ * nor created — because the change they depend on was skipped: the `__pk`
1840
+ * index of a skipped key rebuild, the indexes over a column whose type change
1841
+ * is pending (its present-only filter is typed).
1842
+ */
1843
+ async function syncIndexesImpl(host, opts) {
1807
1844
  await host.ensureCollectionExists();
1808
1845
  const allIndexes = /* @__PURE__ */ new Map();
1809
1846
  for (const [key, index] of host._table.indexes.entries()) {
@@ -1844,6 +1881,12 @@ async function syncIndexesImpl(host) {
1844
1881
  } else allIndexes.set(key, index);
1845
1882
  const existingIndexes = await host.collection.listIndexes().toArray();
1846
1883
  const indexesToCreate = new Map(allIndexes);
1884
+ const keepColumns = new Set(opts?.keepColumns ?? []);
1885
+ const isKept = (name, key, weights) => opts?.keepPrimaryKey === true && name === PK_INDEX || coversAny(key, weights, keepColumns);
1886
+ const kept = /* @__PURE__ */ new Set();
1887
+ for (const index of allIndexes.values()) if (isPlainIndex(index) && isKept(index.key, index.fields, index.weights)) kept.add(index.key);
1888
+ for (const remote of existingIndexes) if (isKept(remote.name, remote.key, remote.weights)) kept.add(remote.name);
1889
+ for (const name of kept) indexesToCreate.delete(name);
1847
1890
  const { attempt, throwIfAny } = (0, _atscript_db.createFailureCollector)("index sync");
1848
1891
  let defaultCollation;
1849
1892
  const collectionDefaultCollation = async () => {
@@ -1851,7 +1894,7 @@ async function syncIndexesImpl(host) {
1851
1894
  return defaultCollation ?? void 0;
1852
1895
  };
1853
1896
  for (const remote of existingIndexes) {
1854
- if (!remote.name.startsWith("atscript__")) continue;
1897
+ if (!remote.name.startsWith("atscript__") || kept.has(remote.name)) continue;
1855
1898
  if (indexesToCreate.has(remote.name)) {
1856
1899
  const local = indexesToCreate.get(remote.name);
1857
1900
  if (isPlainIndex(local)) {
@@ -3168,11 +3211,12 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
3168
3211
  * No physical primary key on MongoDB (`_id` is fixed): a `@meta.id` move is
3169
3212
  * an index change that `syncIndexes` reconciles. Like on the other adapters,
3170
3213
  * schema sync refuses it on a populated collection whose documents do not
3171
- * satisfy the new key.
3214
+ * satisfy the new key. An old key field removed in the same sync loses its
3215
+ * `__pk` index first (`dropIndexesForColumns`).
3172
3216
  */
3173
3217
  async rebuildPrimaryKey(_change) {}
3174
- async syncIndexes() {
3175
- return syncIndexesImpl(this);
3218
+ async syncIndexes(opts) {
3219
+ return syncIndexesImpl(this, opts);
3176
3220
  }
3177
3221
  async syncColumns(diff) {
3178
3222
  return syncColumnsImpl(this, diff);
@@ -3180,6 +3224,9 @@ var MongoAdapter = class MongoAdapter extends _atscript_db.BaseDbAdapter {
3180
3224
  async dropColumns(columns) {
3181
3225
  return dropColumnsImpl(this, columns);
3182
3226
  }
3227
+ async dropIndexesForColumns(columns) {
3228
+ return dropIndexesForColumnsImpl(this, columns);
3229
+ }
3183
3230
  async renameTable(oldName) {
3184
3231
  return renameTableImpl(this, oldName);
3185
3232
  }
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as buildMongoFilter, c as mongoFilterStages, i as TMongoFilterPlan, l as planStages, n as TMongoFieldCollation, o as buildMongoQuery, r as TMongoFilterOptions, s as collationOfAdapter, t as REL_FILTER_TEMP_PREFIX } from "./mongo-filter-CGd9ryOD.cjs";
2
- import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbCollation, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbRelation, TDbUpdateOptions, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TPrimaryKeyChange, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TViewCapability, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
2
+ import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbCollation, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbRelation, TDbUpdateOptions, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TPrimaryKeyChange, TSearchIndexInfo, TSyncColumnResult, TSyncIndexesOptions, TTableResolver, TViewCapability, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
3
3
  import { AggregateOptions, AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
4
4
  import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
5
5
  import { AggregateFn, BucketUnit } from "@uniqu/core";
@@ -592,12 +592,14 @@ declare class MongoAdapter extends BaseDbAdapter {
592
592
  * No physical primary key on MongoDB (`_id` is fixed): a `@meta.id` move is
593
593
  * an index change that `syncIndexes` reconciles. Like on the other adapters,
594
594
  * schema sync refuses it on a populated collection whose documents do not
595
- * satisfy the new key.
595
+ * satisfy the new key. An old key field removed in the same sync loses its
596
+ * `__pk` index first (`dropIndexesForColumns`).
596
597
  */
597
598
  rebuildPrimaryKey(_change: TPrimaryKeyChange): Promise<void>;
598
- syncIndexes(): Promise<void>;
599
+ syncIndexes(opts?: TSyncIndexesOptions): Promise<void>;
599
600
  syncColumns(diff: TColumnDiff): Promise<TSyncColumnResult>;
600
601
  dropColumns(columns: string[]): Promise<void>;
602
+ dropIndexesForColumns(columns: string[]): Promise<void>;
601
603
  renameTable(oldName: string): Promise<void>;
602
604
  recreateTable(): Promise<void>;
603
605
  dropTable(): Promise<void>;
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as buildMongoFilter, c as mongoFilterStages, i as TMongoFilterPlan, l as planStages, n as TMongoFieldCollation, o as buildMongoQuery, r as TMongoFilterOptions, s as collationOfAdapter, t as REL_FILTER_TEMP_PREFIX } from "./mongo-filter-CGd9ryOD.mjs";
2
- import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbCollation, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbRelation, TDbUpdateOptions, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TPrimaryKeyChange, TSearchIndexInfo, TSyncColumnResult, TTableResolver, TViewCapability, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
2
+ import { BaseDbAdapter, DbQuery, DbSpace, FilterExpr, TColumnDiff, TDbCollation, TDbDeleteResult, TDbFieldMeta, TDbForeignKey, TDbInsertIgnoreSlot, TDbInsertManyResult, TDbInsertResult, TDbObjectKind, TDbRelation, TDbUpdateOptions, TDbUpdateResult, TExistingTableOption, TFieldOps, TMetadataOverrides, TPrimaryKeyChange, TSearchIndexInfo, TSyncColumnResult, TSyncIndexesOptions, TTableResolver, TViewCapability, TableMetadata, WithRelation, getKeyProps } from "@atscript/db";
3
3
  import { AggregateOptions, AggregationCursor, ClientSession, Collection, Db, Document, Filter, MongoClient, ObjectId, UpdateFilter, UpdateOptions } from "mongodb";
4
4
  import { AggregateFn, BucketUnit } from "@uniqu/core";
5
5
  import { TAtscriptAnnotatedType, TMetadataMap, TValidatorOptions, TValidatorPlugin, Validator } from "@atscript/typescript/utils";
@@ -592,12 +592,14 @@ declare class MongoAdapter extends BaseDbAdapter {
592
592
  * No physical primary key on MongoDB (`_id` is fixed): a `@meta.id` move is
593
593
  * an index change that `syncIndexes` reconciles. Like on the other adapters,
594
594
  * schema sync refuses it on a populated collection whose documents do not
595
- * satisfy the new key.
595
+ * satisfy the new key. An old key field removed in the same sync loses its
596
+ * `__pk` index first (`dropIndexesForColumns`).
596
597
  */
597
598
  rebuildPrimaryKey(_change: TPrimaryKeyChange): Promise<void>;
598
- syncIndexes(): Promise<void>;
599
+ syncIndexes(opts?: TSyncIndexesOptions): Promise<void>;
599
600
  syncColumns(diff: TColumnDiff): Promise<TSyncColumnResult>;
600
601
  dropColumns(columns: string[]): Promise<void>;
602
+ dropIndexesForColumns(columns: string[]): Promise<void>;
601
603
  renameTable(oldName: string): Promise<void>;
602
604
  recreateTable(): Promise<void>;
603
605
  dropTable(): Promise<void>;
package/dist/index.mjs CHANGED
@@ -1769,6 +1769,27 @@ async function dropColumnsImpl(host, columns) {
1769
1769
  await host.collection.updateMany({}, { $unset: unsetSpec }, host._getSessionOpts());
1770
1770
  }
1771
1771
  /**
1772
+ * Drops the managed indexes over any of `columns` (or a path under one).
1773
+ * Schema sync calls it before {@link dropColumnsImpl}: a unique index on a
1774
+ * required field indexes a `$unset` document as `null`, so the second one
1775
+ * collides (E11000) — e.g. the old key field's `__pk` index when a `@meta.id`
1776
+ * move removes that field. `syncIndexesImpl` then builds what the model wants.
1777
+ */
1778
+ async function dropIndexesForColumnsImpl(host, columns) {
1779
+ const dropped = new Set(columns);
1780
+ let existing;
1781
+ try {
1782
+ existing = await host.collection.listIndexes().toArray();
1783
+ } catch (error) {
1784
+ if (error.code === 26) return;
1785
+ throw error;
1786
+ }
1787
+ for (const remote of existing) if (remote.name.startsWith("atscript__") && coversAny(remote.key, remote.weights, dropped)) {
1788
+ host._log("dropIndex", remote.name);
1789
+ await host.collection.dropIndex(remote.name);
1790
+ }
1791
+ }
1792
+ /**
1772
1793
  * The segments of a field's physical document path with `$[]` after every
1773
1794
  * segment whose logical prefix is an array in the flatMap — Mongo's
1774
1795
  * all-positional operator walks every element. `physicalPath` defaults to
@@ -1802,7 +1823,23 @@ function resolveSyncDefault(field) {
1802
1823
  if (!field.defaultValue) return;
1803
1824
  if (field.defaultValue.kind === "value") return field.designType === "string" ? field.defaultValue.value : JSON.parse(field.defaultValue.value);
1804
1825
  }
1805
- async function syncIndexesImpl(host) {
1826
+ /**
1827
+ * Whether an index's fields include one of `paths` or a path under one —
1828
+ * a text index lists its fields in `weights` (its key is `_fts` / `_ftsx`).
1829
+ */
1830
+ function coversAny(key, weights, paths) {
1831
+ return [...Object.keys(key), ...Object.keys(weights ?? {})].some((f) => paths.has(f) || hasAncestorIn(f, paths));
1832
+ }
1833
+ /** The `__pk` unique index of non-`_id` `@meta.id` fields. */
1834
+ const PK_INDEX = mongoIndexKey("unique", "__pk");
1835
+ /**
1836
+ * Reconciles the managed indexes with the model. `opts` (schema sync, safe
1837
+ * mode) names the indexes that stay as they are — neither dropped, replaced
1838
+ * nor created — because the change they depend on was skipped: the `__pk`
1839
+ * index of a skipped key rebuild, the indexes over a column whose type change
1840
+ * is pending (its present-only filter is typed).
1841
+ */
1842
+ async function syncIndexesImpl(host, opts) {
1806
1843
  await host.ensureCollectionExists();
1807
1844
  const allIndexes = /* @__PURE__ */ new Map();
1808
1845
  for (const [key, index] of host._table.indexes.entries()) {
@@ -1843,6 +1880,12 @@ async function syncIndexesImpl(host) {
1843
1880
  } else allIndexes.set(key, index);
1844
1881
  const existingIndexes = await host.collection.listIndexes().toArray();
1845
1882
  const indexesToCreate = new Map(allIndexes);
1883
+ const keepColumns = new Set(opts?.keepColumns ?? []);
1884
+ const isKept = (name, key, weights) => opts?.keepPrimaryKey === true && name === PK_INDEX || coversAny(key, weights, keepColumns);
1885
+ const kept = /* @__PURE__ */ new Set();
1886
+ for (const index of allIndexes.values()) if (isPlainIndex(index) && isKept(index.key, index.fields, index.weights)) kept.add(index.key);
1887
+ for (const remote of existingIndexes) if (isKept(remote.name, remote.key, remote.weights)) kept.add(remote.name);
1888
+ for (const name of kept) indexesToCreate.delete(name);
1846
1889
  const { attempt, throwIfAny } = createFailureCollector("index sync");
1847
1890
  let defaultCollation;
1848
1891
  const collectionDefaultCollation = async () => {
@@ -1850,7 +1893,7 @@ async function syncIndexesImpl(host) {
1850
1893
  return defaultCollation ?? void 0;
1851
1894
  };
1852
1895
  for (const remote of existingIndexes) {
1853
- if (!remote.name.startsWith("atscript__")) continue;
1896
+ if (!remote.name.startsWith("atscript__") || kept.has(remote.name)) continue;
1854
1897
  if (indexesToCreate.has(remote.name)) {
1855
1898
  const local = indexesToCreate.get(remote.name);
1856
1899
  if (isPlainIndex(local)) {
@@ -3167,11 +3210,12 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
3167
3210
  * No physical primary key on MongoDB (`_id` is fixed): a `@meta.id` move is
3168
3211
  * an index change that `syncIndexes` reconciles. Like on the other adapters,
3169
3212
  * schema sync refuses it on a populated collection whose documents do not
3170
- * satisfy the new key.
3213
+ * satisfy the new key. An old key field removed in the same sync loses its
3214
+ * `__pk` index first (`dropIndexesForColumns`).
3171
3215
  */
3172
3216
  async rebuildPrimaryKey(_change) {}
3173
- async syncIndexes() {
3174
- return syncIndexesImpl(this);
3217
+ async syncIndexes(opts) {
3218
+ return syncIndexesImpl(this, opts);
3175
3219
  }
3176
3220
  async syncColumns(diff) {
3177
3221
  return syncColumnsImpl(this, diff);
@@ -3179,6 +3223,9 @@ var MongoAdapter = class MongoAdapter extends BaseDbAdapter {
3179
3223
  async dropColumns(columns) {
3180
3224
  return dropColumnsImpl(this, columns);
3181
3225
  }
3226
+ async dropIndexesForColumns(columns) {
3227
+ return dropIndexesForColumnsImpl(this, columns);
3228
+ }
3182
3229
  async renameTable(oldName) {
3183
3230
  return renameTableImpl(this, oldName);
3184
3231
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atscript/db-mongo",
3
- "version": "0.1.155",
3
+ "version": "0.1.156",
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.105",
50
- "@atscript/typescript": "^0.1.105",
49
+ "@atscript/core": "^0.1.106",
50
+ "@atscript/typescript": "^0.1.106",
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.105"
54
+ "unplugin-atscript": "^0.1.106"
55
55
  },
56
56
  "peerDependencies": {
57
- "@atscript/core": "^0.1.105",
58
- "@atscript/typescript": "^0.1.105",
57
+ "@atscript/core": "^0.1.106",
58
+ "@atscript/typescript": "^0.1.106",
59
59
  "@uniqu/core": "^0.1.15",
60
60
  "mongodb": "^6.17.0",
61
- "@atscript/db": "^0.1.155"
61
+ "@atscript/db": "^0.1.156"
62
62
  },
63
63
  "scripts": {
64
64
  "postinstall": "asc -f dts",