@orion-js/mongodb 4.1.10 → 4.2.0

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
@@ -295,6 +295,12 @@ var BaseCollection = class {
295
295
  */
296
296
  createIndexes;
297
297
  createIndexesPromise;
298
+ /**
299
+ * Deletes indexes that exist in MongoDB but are not defined in the collection configuration.
300
+ * This helps clean up stale indexes that are no longer needed.
301
+ * Always preserves the _id_ index.
302
+ */
303
+ deleteUnusedIndexes;
298
304
  /**
299
305
  * @deprecated Use startConnection() instead. This property is not guaranteed to be resolved if the connection is not started.
300
306
  * 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 +1193,82 @@ var generateId_default = getIdGenerator;
1187
1193
 
1188
1194
  // src/createCollection/createIndexes.ts
1189
1195
  var import_mongodb2 = require("mongodb");
1196
+ var import_logger3 = require("@orion-js/logger");
1197
+
1198
+ // src/createCollection/deleteUnusedIndexes.ts
1190
1199
  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;
1200
+
1201
+ // src/createCollection/getIndexOptions.ts
1202
+ function getIndexOptions(indexDef) {
1203
+ const { keys: _keys, options: deprecatedOptions, ...flatOptions } = indexDef;
1204
+ if (deprecatedOptions) {
1205
+ return { ...deprecatedOptions, ...flatOptions };
1206
+ }
1207
+ return Object.keys(flatOptions).length > 0 ? flatOptions : void 0;
1195
1208
  }
1209
+ function getIndexName(indexDef) {
1210
+ var _a;
1211
+ if (indexDef.name) {
1212
+ return indexDef.name;
1213
+ }
1214
+ return (_a = indexDef.options) == null ? void 0 : _a.name;
1215
+ }
1216
+
1217
+ // src/createCollection/deleteUnusedIndexes.ts
1218
+ function keysMatch(definitionKeys, currentIndexKey) {
1219
+ const defEntries = Object.entries(definitionKeys);
1220
+ const curEntries = Object.entries(currentIndexKey);
1221
+ if (defEntries.length !== curEntries.length) return false;
1222
+ for (let i = 0; i < defEntries.length; i++) {
1223
+ const [defKey, defValue] = defEntries[i];
1224
+ const [curKey, curValue] = curEntries[i];
1225
+ if (defKey !== curKey || defValue !== curValue) return false;
1226
+ }
1227
+ return true;
1228
+ }
1229
+ function isIndexDefined(definedIndexes, currentIndex) {
1230
+ return definedIndexes.some((defIndex) => {
1231
+ const customName = getIndexName(defIndex);
1232
+ if (customName && customName === currentIndex.name) return true;
1233
+ return keysMatch(defIndex.keys, currentIndex.key);
1234
+ });
1235
+ }
1236
+ async function deleteUnusedIndexes(collection) {
1237
+ await collection.connectionPromise;
1238
+ const result = {
1239
+ deletedIndexes: [],
1240
+ collectionName: collection.name
1241
+ };
1242
+ let currentIndexes = [];
1243
+ try {
1244
+ currentIndexes = await collection.rawCollection.indexes();
1245
+ } catch (error) {
1246
+ if (error.codeName === "NamespaceNotFound") {
1247
+ return result;
1248
+ }
1249
+ throw error;
1250
+ }
1251
+ if (!collection.indexes || collection.indexes.length === 0) {
1252
+ return result;
1253
+ }
1254
+ const unusedIndexes = currentIndexes.filter(
1255
+ (index) => index.name !== "_id_" && !isIndexDefined(collection.indexes, index)
1256
+ );
1257
+ for (const index of unusedIndexes) {
1258
+ try {
1259
+ await collection.rawCollection.dropIndex(index.name);
1260
+ result.deletedIndexes.push(index.name);
1261
+ import_logger2.logger.info(`Deleted unused index "${index.name}" from collection "${collection.name}"`);
1262
+ } catch (error) {
1263
+ import_logger2.logger.error(`Failed to delete index "${index.name}" from collection "${collection.name}"`, {
1264
+ error
1265
+ });
1266
+ }
1267
+ }
1268
+ return result;
1269
+ }
1270
+
1271
+ // src/createCollection/createIndexes.ts
1196
1272
  async function checkIndexes(collection) {
1197
1273
  await collection.connectionPromise;
1198
1274
  let currentIndexes = [];
@@ -1200,13 +1276,17 @@ async function checkIndexes(collection) {
1200
1276
  currentIndexes = await collection.rawCollection.indexes();
1201
1277
  } catch (error) {
1202
1278
  if (error.codeName !== "NamespaceNotFound") throw error;
1279
+ return;
1203
1280
  }
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`
1281
+ if (!collection.indexes || collection.indexes.length === 0) {
1282
+ return;
1283
+ }
1284
+ const unexpectedIndexes = currentIndexes.filter(
1285
+ (index) => index.name !== "_id_" && !isIndexDefined(collection.indexes, index)
1286
+ );
1287
+ if (unexpectedIndexes.length > 0) {
1288
+ import_logger3.logger.warn(
1289
+ `${unexpectedIndexes.length} unexpected indexes found in collection "${collection.name}": ${unexpectedIndexes.map((i) => i.name).join(", ")} | Delete the index or fix the collection definition`
1210
1290
  );
1211
1291
  }
1212
1292
  }
@@ -1215,7 +1295,9 @@ async function loadIndexes(collection) {
1215
1295
  if (!collection.indexes.length) return;
1216
1296
  await collection.connectionPromise;
1217
1297
  const results = Promise.all(
1218
- collection.indexes.map(async ({ keys, options }) => {
1298
+ collection.indexes.map(async (indexDef) => {
1299
+ const { keys } = indexDef;
1300
+ const options = getIndexOptions(indexDef);
1219
1301
  try {
1220
1302
  return await collection.rawCollection.createIndex(keys, options);
1221
1303
  } catch (error) {
@@ -1277,7 +1359,7 @@ function getSchema(options) {
1277
1359
  }
1278
1360
 
1279
1361
  // src/createCollection/wrapMethods.ts
1280
- var import_logger3 = require("@orion-js/logger");
1362
+ var import_logger4 = require("@orion-js/logger");
1281
1363
  function wrapMethods(collection) {
1282
1364
  const methodsWithPromises = [
1283
1365
  "findOne",
@@ -1313,7 +1395,7 @@ function wrapMethods(collection) {
1313
1395
  collection[methodName2] = (...args) => {
1314
1396
  collection.startConnection();
1315
1397
  if (!collection.rawCollection) {
1316
- import_logger3.logger.error("Method called before connection was initialized", {
1398
+ import_logger4.logger.error("Method called before connection was initialized", {
1317
1399
  collectionName: collection.name,
1318
1400
  connectionName: collection.connectionName,
1319
1401
  methodName: methodName2
@@ -1414,6 +1496,10 @@ function createCollection(options) {
1414
1496
  return createIndexPromise;
1415
1497
  };
1416
1498
  mainCollection.createIndexes = createIndexes;
1499
+ mainCollection.deleteUnusedIndexes = async () => {
1500
+ await orionConnection.startConnection();
1501
+ return deleteUnusedIndexes(mainCollection);
1502
+ };
1417
1503
  if (!process.env.DONT_CREATE_INDEXES_AUTOMATICALLY) {
1418
1504
  createIndexes();
1419
1505
  }
@@ -1449,7 +1535,7 @@ function MongoCollection(options) {
1449
1535
  }
1450
1536
 
1451
1537
  // src/encrypted/getOrCreateEncryptionKey.ts
1452
- var import_logger4 = require("@orion-js/logger");
1538
+ var import_logger5 = require("@orion-js/logger");
1453
1539
  var import_mongodb3 = require("mongodb");
1454
1540
  async function getOrCreateEncryptionKey({
1455
1541
  keyAltName,
@@ -1461,7 +1547,7 @@ async function getOrCreateEncryptionKey({
1461
1547
  kmsProviders
1462
1548
  }) {
1463
1549
  const keyVaultNamespace = `${keyVaultDatabase}.${keyVaultCollection}`;
1464
- import_logger4.logger.info("Connecting to database to get or create the encryption key", {
1550
+ import_logger5.logger.info("Connecting to database to get or create the encryption key", {
1465
1551
  keyVaultNamespace,
1466
1552
  keyAltName
1467
1553
  });
@@ -1479,14 +1565,14 @@ async function getOrCreateEncryptionKey({
1479
1565
  });
1480
1566
  const key = await clientEncryption.getKeyByAltName(keyAltName);
1481
1567
  if (key) {
1482
- import_logger4.logger.info("Key found on the key vault", {
1568
+ import_logger5.logger.info("Key found on the key vault", {
1483
1569
  keyVaultNamespace,
1484
1570
  keyAltName,
1485
1571
  UUID: key._id
1486
1572
  });
1487
1573
  return { key: key._id, keyVaultNamespace };
1488
1574
  }
1489
- import_logger4.logger.info("Key not found on the key vault, creating a new one", {
1575
+ import_logger5.logger.info("Key not found on the key vault, creating a new one", {
1490
1576
  keyVaultNamespace,
1491
1577
  keyAltName
1492
1578
  });
@@ -1494,7 +1580,7 @@ async function getOrCreateEncryptionKey({
1494
1580
  keyAltNames: [keyAltName],
1495
1581
  ...masterKey ? { masterKey } : {}
1496
1582
  });
1497
- import_logger4.logger.info("New encryption key created", {
1583
+ import_logger5.logger.info("New encryption key created", {
1498
1584
  keyVaultNamespace,
1499
1585
  keyAltName,
1500
1586
  UUID: newKey