@orion-js/mongodb 4.1.10 → 4.2.1

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
@@ -36,13 +36,17 @@ __export(index_exports, {
36
36
  MongoDB: () => MongoDB,
37
37
  Repository: () => Repository,
38
38
  allConnectionPromises: () => allConnectionPromises,
39
+ collectionsRegistry: () => collectionsRegistry,
39
40
  configureConnection: () => configureConnection,
40
41
  connections: () => connections,
41
42
  createCollection: () => createCollection,
42
43
  createIndexesPromises: () => createIndexesPromises,
44
+ deleteAllUnusedIndexes: () => deleteAllUnusedIndexes,
43
45
  getDBName: () => getDBName,
44
46
  getMongoConnection: () => getMongoConnection,
45
47
  getOrCreateEncryptionKey: () => getOrCreateEncryptionKey,
48
+ getRegisteredCollections: () => getRegisteredCollections,
49
+ registerCollection: () => registerCollection,
46
50
  typedId: () => typedId
47
51
  });
48
52
  module.exports = __toCommonJS(index_exports);
@@ -295,6 +299,12 @@ var BaseCollection = class {
295
299
  */
296
300
  createIndexes;
297
301
  createIndexesPromise;
302
+ /**
303
+ * Deletes indexes that exist in MongoDB but are not defined in the collection configuration.
304
+ * This helps clean up stale indexes that are no longer needed.
305
+ * Always preserves the _id_ index.
306
+ */
307
+ deleteUnusedIndexes;
298
308
  /**
299
309
  * @deprecated Use startConnection() instead. This property is not guaranteed to be resolved if the connection is not started.
300
310
  * When using async calls startConnection or connectionPromise is no longer needed. Orion will automatically start the connection if it is not already started.
@@ -1187,12 +1197,82 @@ var generateId_default = getIdGenerator;
1187
1197
 
1188
1198
  // src/createCollection/createIndexes.ts
1189
1199
  var import_mongodb2 = require("mongodb");
1200
+ var import_logger3 = require("@orion-js/logger");
1201
+
1202
+ // src/createCollection/deleteUnusedIndexes.ts
1190
1203
  var import_logger2 = require("@orion-js/logger");
1191
- function matchingDefinition(defIndex, curIndex) {
1192
- if (defIndex.options && defIndex.options.name === curIndex.name) return true;
1193
- const defIndexName = Object.keys(defIndex.keys).map((key) => `${key}_${defIndex.keys[key]}`).join("_");
1194
- return defIndexName === curIndex.name;
1204
+
1205
+ // src/createCollection/getIndexOptions.ts
1206
+ function getIndexOptions(indexDef) {
1207
+ const { keys: _keys, options: deprecatedOptions, ...flatOptions } = indexDef;
1208
+ if (deprecatedOptions) {
1209
+ return { ...deprecatedOptions, ...flatOptions };
1210
+ }
1211
+ return Object.keys(flatOptions).length > 0 ? flatOptions : void 0;
1212
+ }
1213
+ function getIndexName(indexDef) {
1214
+ var _a;
1215
+ if (indexDef.name) {
1216
+ return indexDef.name;
1217
+ }
1218
+ return (_a = indexDef.options) == null ? void 0 : _a.name;
1219
+ }
1220
+
1221
+ // src/createCollection/deleteUnusedIndexes.ts
1222
+ function keysMatch(definitionKeys, currentIndexKey) {
1223
+ const defEntries = Object.entries(definitionKeys);
1224
+ const curEntries = Object.entries(currentIndexKey);
1225
+ if (defEntries.length !== curEntries.length) return false;
1226
+ for (let i = 0; i < defEntries.length; i++) {
1227
+ const [defKey, defValue] = defEntries[i];
1228
+ const [curKey, curValue] = curEntries[i];
1229
+ if (defKey !== curKey || defValue !== curValue) return false;
1230
+ }
1231
+ return true;
1232
+ }
1233
+ function isIndexDefined(definedIndexes, currentIndex) {
1234
+ return definedIndexes.some((defIndex) => {
1235
+ const customName = getIndexName(defIndex);
1236
+ if (customName && customName === currentIndex.name) return true;
1237
+ return keysMatch(defIndex.keys, currentIndex.key);
1238
+ });
1195
1239
  }
1240
+ async function deleteUnusedIndexes(collection) {
1241
+ await collection.connectionPromise;
1242
+ const result = {
1243
+ deletedIndexes: [],
1244
+ collectionName: collection.name
1245
+ };
1246
+ let currentIndexes = [];
1247
+ try {
1248
+ currentIndexes = await collection.rawCollection.indexes();
1249
+ } catch (error) {
1250
+ if (error.codeName === "NamespaceNotFound") {
1251
+ return result;
1252
+ }
1253
+ throw error;
1254
+ }
1255
+ if (!collection.indexes || collection.indexes.length === 0) {
1256
+ return result;
1257
+ }
1258
+ const unusedIndexes = currentIndexes.filter(
1259
+ (index) => index.name !== "_id_" && !isIndexDefined(collection.indexes, index)
1260
+ );
1261
+ for (const index of unusedIndexes) {
1262
+ try {
1263
+ await collection.rawCollection.dropIndex(index.name);
1264
+ result.deletedIndexes.push(index.name);
1265
+ import_logger2.logger.info(`Deleted unused index "${index.name}" from collection "${collection.name}"`);
1266
+ } catch (error) {
1267
+ import_logger2.logger.error(`Failed to delete index "${index.name}" from collection "${collection.name}"`, {
1268
+ error
1269
+ });
1270
+ }
1271
+ }
1272
+ return result;
1273
+ }
1274
+
1275
+ // src/createCollection/createIndexes.ts
1196
1276
  async function checkIndexes(collection) {
1197
1277
  await collection.connectionPromise;
1198
1278
  let currentIndexes = [];
@@ -1200,13 +1280,17 @@ async function checkIndexes(collection) {
1200
1280
  currentIndexes = await collection.rawCollection.indexes();
1201
1281
  } catch (error) {
1202
1282
  if (error.codeName !== "NamespaceNotFound") throw error;
1283
+ return;
1203
1284
  }
1204
- const indexesToDelete = collection.indexes ? currentIndexes.filter(
1205
- (index) => index.name !== "_id_" && !collection.indexes.find((definitionIndex) => matchingDefinition(definitionIndex, index))
1206
- ) : currentIndexes;
1207
- if (indexesToDelete.length > 0) {
1208
- import_logger2.logger.warn(
1209
- `${indexesToDelete.length} unexpected indexes found in collection "${collection.name}": ${indexesToDelete.map((i) => i.name).join(", ")} | Delete the index or fix the collection definition`
1285
+ if (!collection.indexes || collection.indexes.length === 0) {
1286
+ return;
1287
+ }
1288
+ const unexpectedIndexes = currentIndexes.filter(
1289
+ (index) => index.name !== "_id_" && !isIndexDefined(collection.indexes, index)
1290
+ );
1291
+ if (unexpectedIndexes.length > 0) {
1292
+ import_logger3.logger.warn(
1293
+ `${unexpectedIndexes.length} unexpected indexes found in collection "${collection.name}": ${unexpectedIndexes.map((i) => i.name).join(", ")} | Delete the index or fix the collection definition`
1210
1294
  );
1211
1295
  }
1212
1296
  }
@@ -1215,7 +1299,9 @@ async function loadIndexes(collection) {
1215
1299
  if (!collection.indexes.length) return;
1216
1300
  await collection.connectionPromise;
1217
1301
  const results = Promise.all(
1218
- collection.indexes.map(async ({ keys, options }) => {
1302
+ collection.indexes.map(async (indexDef) => {
1303
+ const { keys } = indexDef;
1304
+ const options = getIndexOptions(indexDef);
1219
1305
  try {
1220
1306
  return await collection.rawCollection.createIndex(keys, options);
1221
1307
  } catch (error) {
@@ -1245,6 +1331,43 @@ async function loadIndexes(collection) {
1245
1331
  return results;
1246
1332
  }
1247
1333
 
1334
+ // src/createCollection/collectionsRegistry.ts
1335
+ var import_logger4 = require("@orion-js/logger");
1336
+ var collectionsRegistry = /* @__PURE__ */ new Map();
1337
+ function registerCollection(connectionName, collection) {
1338
+ if (!collectionsRegistry.has(connectionName)) {
1339
+ collectionsRegistry.set(connectionName, /* @__PURE__ */ new Map());
1340
+ }
1341
+ collectionsRegistry.get(connectionName).set(collection.name, collection);
1342
+ }
1343
+ function getRegisteredCollections(connectionName = "main") {
1344
+ const connectionCollections = collectionsRegistry.get(connectionName);
1345
+ if (!connectionCollections) {
1346
+ return [];
1347
+ }
1348
+ return Array.from(connectionCollections.values());
1349
+ }
1350
+ async function deleteAllUnusedIndexes(connectionName = "main") {
1351
+ const collections = getRegisteredCollections(connectionName);
1352
+ if (collections.length === 0) {
1353
+ import_logger4.logger.warn(`No collections registered for connection "${connectionName}"`);
1354
+ return [];
1355
+ }
1356
+ import_logger4.logger.info(
1357
+ `Deleting unused indexes from ${collections.length} collections on connection "${connectionName}"`
1358
+ );
1359
+ const results = [];
1360
+ for (const collection of collections) {
1361
+ const result = await collection.deleteUnusedIndexes();
1362
+ if (result.deletedIndexes.length > 0) {
1363
+ results.push(result);
1364
+ }
1365
+ }
1366
+ const totalDeleted = results.reduce((sum, r) => sum + r.deletedIndexes.length, 0);
1367
+ import_logger4.logger.info(`Deleted ${totalDeleted} unused indexes from ${results.length} collections`);
1368
+ return results;
1369
+ }
1370
+
1248
1371
  // src/createCollection/getSchemaAndModel.ts
1249
1372
  var import_helpers7 = require("@orion-js/helpers");
1250
1373
  Symbol.metadata ?? (Symbol.metadata = Symbol("Symbol.metadata"));
@@ -1277,7 +1400,7 @@ function getSchema(options) {
1277
1400
  }
1278
1401
 
1279
1402
  // src/createCollection/wrapMethods.ts
1280
- var import_logger3 = require("@orion-js/logger");
1403
+ var import_logger5 = require("@orion-js/logger");
1281
1404
  function wrapMethods(collection) {
1282
1405
  const methodsWithPromises = [
1283
1406
  "findOne",
@@ -1313,7 +1436,7 @@ function wrapMethods(collection) {
1313
1436
  collection[methodName2] = (...args) => {
1314
1437
  collection.startConnection();
1315
1438
  if (!collection.rawCollection) {
1316
- import_logger3.logger.error("Method called before connection was initialized", {
1439
+ import_logger5.logger.error("Method called before connection was initialized", {
1317
1440
  collectionName: collection.name,
1318
1441
  connectionName: collection.connectionName,
1319
1442
  methodName: methodName2
@@ -1414,11 +1537,16 @@ function createCollection(options) {
1414
1537
  return createIndexPromise;
1415
1538
  };
1416
1539
  mainCollection.createIndexes = createIndexes;
1540
+ mainCollection.deleteUnusedIndexes = async () => {
1541
+ await orionConnection.startConnection();
1542
+ return deleteUnusedIndexes(mainCollection);
1543
+ };
1417
1544
  if (!process.env.DONT_CREATE_INDEXES_AUTOMATICALLY) {
1418
1545
  createIndexes();
1419
1546
  }
1420
1547
  wrapMethods(mainCollection);
1421
1548
  wrapMethods(encryptedCollection);
1549
+ registerCollection(connectionName, mainCollection);
1422
1550
  return mainCollection;
1423
1551
  }
1424
1552
 
@@ -1449,7 +1577,7 @@ function MongoCollection(options) {
1449
1577
  }
1450
1578
 
1451
1579
  // src/encrypted/getOrCreateEncryptionKey.ts
1452
- var import_logger4 = require("@orion-js/logger");
1580
+ var import_logger6 = require("@orion-js/logger");
1453
1581
  var import_mongodb3 = require("mongodb");
1454
1582
  async function getOrCreateEncryptionKey({
1455
1583
  keyAltName,
@@ -1461,7 +1589,7 @@ async function getOrCreateEncryptionKey({
1461
1589
  kmsProviders
1462
1590
  }) {
1463
1591
  const keyVaultNamespace = `${keyVaultDatabase}.${keyVaultCollection}`;
1464
- import_logger4.logger.info("Connecting to database to get or create the encryption key", {
1592
+ import_logger6.logger.info("Connecting to database to get or create the encryption key", {
1465
1593
  keyVaultNamespace,
1466
1594
  keyAltName
1467
1595
  });
@@ -1479,14 +1607,14 @@ async function getOrCreateEncryptionKey({
1479
1607
  });
1480
1608
  const key = await clientEncryption.getKeyByAltName(keyAltName);
1481
1609
  if (key) {
1482
- import_logger4.logger.info("Key found on the key vault", {
1610
+ import_logger6.logger.info("Key found on the key vault", {
1483
1611
  keyVaultNamespace,
1484
1612
  keyAltName,
1485
1613
  UUID: key._id
1486
1614
  });
1487
1615
  return { key: key._id, keyVaultNamespace };
1488
1616
  }
1489
- import_logger4.logger.info("Key not found on the key vault, creating a new one", {
1617
+ import_logger6.logger.info("Key not found on the key vault, creating a new one", {
1490
1618
  keyVaultNamespace,
1491
1619
  keyAltName
1492
1620
  });
@@ -1494,7 +1622,7 @@ async function getOrCreateEncryptionKey({
1494
1622
  keyAltNames: [keyAltName],
1495
1623
  ...masterKey ? { masterKey } : {}
1496
1624
  });
1497
- import_logger4.logger.info("New encryption key created", {
1625
+ import_logger6.logger.info("New encryption key created", {
1498
1626
  keyVaultNamespace,
1499
1627
  keyAltName,
1500
1628
  UUID: newKey
@@ -1514,13 +1642,17 @@ var ENCRYPTION_ALGORITHMS = {
1514
1642
  MongoDB,
1515
1643
  Repository,
1516
1644
  allConnectionPromises,
1645
+ collectionsRegistry,
1517
1646
  configureConnection,
1518
1647
  connections,
1519
1648
  createCollection,
1520
1649
  createIndexesPromises,
1650
+ deleteAllUnusedIndexes,
1521
1651
  getDBName,
1522
1652
  getMongoConnection,
1523
1653
  getOrCreateEncryptionKey,
1654
+ getRegisteredCollections,
1655
+ registerCollection,
1524
1656
  typedId
1525
1657
  });
1526
1658
  //# sourceMappingURL=index.cjs.map