@orion-js/mongodb 4.1.8 → 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
@@ -250,6 +250,7 @@ var getMongoConnection = ({ name, uri }) => {
250
250
  // src/types/index.ts
251
251
  var MongoDB = __toESM(require("mongodb"), 1);
252
252
  var import_schema = require("@orion-js/schema");
253
+ var import_helpers2 = require("@orion-js/helpers");
253
254
  var BaseCollection = class {
254
255
  name;
255
256
  connectionName;
@@ -294,6 +295,12 @@ var BaseCollection = class {
294
295
  */
295
296
  createIndexes;
296
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;
297
304
  /**
298
305
  * @deprecated Use startConnection() instead. This property is not guaranteed to be resolved if the connection is not started.
299
306
  * When using async calls startConnection or connectionPromise is no longer needed. Orion will automatically start the connection if it is not already started.
@@ -310,7 +317,8 @@ function typedId(prefix) {
310
317
  return {
311
318
  ...import_schema.fieldTypes.string,
312
319
  name: `typedId:${prefix}`,
313
- toSerializedType: async () => "string"
320
+ toSerializedType: async () => "string",
321
+ generateId: () => (0, import_helpers2.generateUUIDWithPrefix)(prefix)
314
322
  };
315
323
  }
316
324
 
@@ -947,13 +955,13 @@ var insertOne_default = (collection) => {
947
955
  };
948
956
 
949
957
  // src/createCollection/getMethods/insertMany.ts
950
- var import_helpers2 = require("@orion-js/helpers");
958
+ var import_helpers3 = require("@orion-js/helpers");
951
959
  var import_schema10 = require("@orion-js/schema");
952
960
  var insertMany_default = (collection) => {
953
961
  const insertMany = async (docs, options = {}) => {
954
962
  await collection.connectionPromise;
955
963
  for (let index = 0; index < docs.length; index++) {
956
- let doc = (0, import_helpers2.clone)(docs[index]);
964
+ let doc = (0, import_helpers3.clone)(docs[index]);
957
965
  if (!doc || type(doc) !== "Object") {
958
966
  throw new Error(`Item at index ${index} is not a document`);
959
967
  }
@@ -1070,7 +1078,7 @@ function loadOne_default(collection) {
1070
1078
  }
1071
1079
 
1072
1080
  // src/createCollection/getMethods/dataLoader/loadData.ts
1073
- var import_helpers4 = require("@orion-js/helpers");
1081
+ var import_helpers5 = require("@orion-js/helpers");
1074
1082
 
1075
1083
  // src/createCollection/getMethods/dataLoader/dataLoad/getDataLoader.ts
1076
1084
  var import_dataloader = __toESM(require("dataloader"), 1);
@@ -1092,10 +1100,10 @@ var getDataLoader = (params) => {
1092
1100
  };
1093
1101
 
1094
1102
  // src/createCollection/getMethods/dataLoader/dataLoad/index.ts
1095
- var import_helpers3 = require("@orion-js/helpers");
1103
+ var import_helpers4 = require("@orion-js/helpers");
1096
1104
  var dataLoad = async (options) => {
1097
1105
  const dataLoader = getDataLoader({
1098
- key: (0, import_helpers3.hashObject)(options.loaderKey),
1106
+ key: (0, import_helpers4.hashObject)(options.loaderKey),
1099
1107
  func: options.load,
1100
1108
  timeout: options.timeout || 5
1101
1109
  });
@@ -1124,10 +1132,10 @@ function loadData_default(collection) {
1124
1132
  timeout: options.timeout,
1125
1133
  load: async (values) => {
1126
1134
  const query = {
1127
- ...(0, import_helpers4.clone)(options.match),
1135
+ ...(0, import_helpers5.clone)(options.match),
1128
1136
  [options.key]: { $in: values }
1129
1137
  };
1130
- const cursor = collection.find(query);
1138
+ const cursor = collection.find(query, { readPreference: "secondaryPreferred" });
1131
1139
  if (options.sort) {
1132
1140
  cursor.sort(options.sort);
1133
1141
  }
@@ -1138,7 +1146,7 @@ function loadData_default(collection) {
1138
1146
  console.info(`Will execute data loading query now on ${collection.name}: `, query);
1139
1147
  }
1140
1148
  const items = await cursor.toArray();
1141
- const itemsMap = (0, import_helpers4.createMapArray)(items, options.key);
1149
+ const itemsMap = (0, import_helpers5.createMapArray)(items, options.key);
1142
1150
  return values.map((value) => {
1143
1151
  return itemsMap[value] || [];
1144
1152
  });
@@ -1150,7 +1158,7 @@ function loadData_default(collection) {
1150
1158
  }
1151
1159
 
1152
1160
  // src/createCollection/generateId.ts
1153
- var import_helpers5 = require("@orion-js/helpers");
1161
+ var import_helpers6 = require("@orion-js/helpers");
1154
1162
  var import_bson = require("bson");
1155
1163
  var getIdGenerator = (options) => {
1156
1164
  var _a, _b;
@@ -1158,23 +1166,21 @@ var getIdGenerator = (options) => {
1158
1166
  const idField = options.schema._id.type;
1159
1167
  if ((_b = idField.name) == null ? void 0 : _b.startsWith("typedId:")) {
1160
1168
  return () => {
1161
- const prefix = idField.name.split(":")[1];
1162
- const random = (0, import_helpers5.generateUUID)();
1163
- return `${prefix}-${random}`;
1169
+ return idField.generateId();
1164
1170
  };
1165
1171
  }
1166
1172
  }
1167
1173
  if (options.idPrefix || options.idGeneration === "uuid") {
1168
1174
  return () => {
1169
1175
  const prefix = options.idPrefix || "";
1170
- const random = (0, import_helpers5.generateUUID)();
1176
+ const random = (0, import_helpers6.generateUUID)();
1171
1177
  return `${prefix}${random}`;
1172
1178
  };
1173
1179
  }
1174
1180
  if (options.idGeneration === "random") {
1175
1181
  return () => {
1176
1182
  const prefix = options.idPrefix || "";
1177
- const random = (0, import_helpers5.generateId)();
1183
+ const random = (0, import_helpers6.generateId)();
1178
1184
  return `${prefix}${random}`;
1179
1185
  };
1180
1186
  }
@@ -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;
1280
+ }
1281
+ if (!collection.indexes || collection.indexes.length === 0) {
1282
+ return;
1203
1283
  }
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`
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) {
@@ -1246,7 +1328,7 @@ async function loadIndexes(collection) {
1246
1328
  }
1247
1329
 
1248
1330
  // src/createCollection/getSchemaAndModel.ts
1249
- var import_helpers6 = require("@orion-js/helpers");
1331
+ var import_helpers7 = require("@orion-js/helpers");
1250
1332
  Symbol.metadata ?? (Symbol.metadata = Symbol("Symbol.metadata"));
1251
1333
  function prepareShema(schema) {
1252
1334
  if (!schema._id) {
@@ -1268,7 +1350,7 @@ function getSchema(options) {
1268
1350
  }
1269
1351
  if (options.schema.getModel) {
1270
1352
  const model = options.schema.getModel();
1271
- const schema = model ? (0, import_helpers6.clone)(model.getSchema()) : {};
1353
+ const schema = model ? (0, import_helpers7.clone)(model.getSchema()) : {};
1272
1354
  return prepareShema(schema);
1273
1355
  }
1274
1356
  if (type(options.schema) === "Object") {
@@ -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