common.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.BulkOperationBase = exports.FindOperators = exports.MongoBulkWriteError = exports.WriteError = exports.WriteConcernError = exports.BulkWriteResult = exports.Batch = exports.BatchType = void 0;
  4. exports.mergeBatchResults = mergeBatchResults;
  5. const bson_1 = require("../bson");
  6. const error_1 = require("../error");
  7. const delete_1 = require("../operations/delete");
  8. const execute_operation_1 = require("../operations/execute_operation");
  9. const insert_1 = require("../operations/insert");
  10. const update_1 = require("../operations/update");
  11. const timeout_1 = require("../timeout");
  12. const utils_1 = require("../utils");
  13. const write_concern_1 = require("../write_concern");
  14. /** @public */
  15. exports.BatchType = Object.freeze({
  16. INSERT: 1,
  17. UPDATE: 2,
  18. DELETE: 3
  19. });
  20. /**
  21. * Keeps the state of a unordered batch so we can rewrite the results
  22. * correctly after command execution
  23. *
  24. * @public
  25. */
  26. class Batch {
  27. constructor(batchType, originalZeroIndex) {
  28. this.originalZeroIndex = originalZeroIndex;
  29. this.currentIndex = 0;
  30. this.originalIndexes = [];
  31. this.batchType = batchType;
  32. this.operations = [];
  33. this.size = 0;
  34. this.sizeBytes = 0;
  35. }
  36. }
  37. exports.Batch = Batch;
  38. /**
  39. * @public
  40. * The result of a bulk write.
  41. */
  42. class BulkWriteResult {
  43. static generateIdMap(ids) {
  44. const idMap = {};
  45. for (const doc of ids) {
  46. idMap[doc.index] = doc._id;
  47. }
  48. return idMap;
  49. }
  50. /**
  51. * Create a new BulkWriteResult instance
  52. * @internal
  53. */
  54. constructor(bulkResult, isOrdered) {
  55. this.result = bulkResult;
  56. this.insertedCount = this.result.nInserted ?? 0;
  57. this.matchedCount = this.result.nMatched ?? 0;
  58. this.modifiedCount = this.result.nModified ?? 0;
  59. this.deletedCount = this.result.nRemoved ?? 0;
  60. this.upsertedCount = this.result.upserted.length ?? 0;
  61. this.upsertedIds = BulkWriteResult.generateIdMap(this.result.upserted);
  62. this.insertedIds = BulkWriteResult.generateIdMap(this.getSuccessfullyInsertedIds(bulkResult, isOrdered));
  63. Object.defineProperty(this, 'result', { value: this.result, enumerable: false });
  64. }
  65. /** Evaluates to true if the bulk operation correctly executes */
  66. get ok() {
  67. return this.result.ok;
  68. }
  69. /**
  70. * Returns document_ids that were actually inserted
  71. * @internal
  72. */
  73. getSuccessfullyInsertedIds(bulkResult, isOrdered) {
  74. if (bulkResult.writeErrors.length === 0)
  75. return bulkResult.insertedIds;
  76. if (isOrdered) {
  77. return bulkResult.insertedIds.slice(0, bulkResult.writeErrors[0].index);
  78. }
  79. return bulkResult.insertedIds.filter(({ index }) => !bulkResult.writeErrors.some(writeError => index === writeError.index));
  80. }
  81. /** Returns the upserted id at the given index */
  82. getUpsertedIdAt(index) {
  83. return this.result.upserted[index];
  84. }
  85. /** Returns raw internal result */
  86. getRawResponse() {
  87. return this.result;
  88. }
  89. /** Returns true if the bulk operation contains a write error */
  90. hasWriteErrors() {
  91. return this.result.writeErrors.length > 0;
  92. }
  93. /** Returns the number of write errors from the bulk operation */
  94. getWriteErrorCount() {
  95. return this.result.writeErrors.length;
  96. }
  97. /** Returns a specific write error object */
  98. getWriteErrorAt(index) {
  99. return index < this.result.writeErrors.length ? this.result.writeErrors[index] : undefined;
  100. }
  101. /** Retrieve all write errors */
  102. getWriteErrors() {
  103. return this.result.writeErrors;
  104. }
  105. /** Retrieve the write concern error if one exists */
  106. getWriteConcernError() {
  107. if (this.result.writeConcernErrors.length === 0) {
  108. return;
  109. }
  110. else if (this.result.writeConcernErrors.length === 1) {
  111. // Return the error
  112. return this.result.writeConcernErrors[0];
  113. }
  114. else {
  115. // Combine the errors
  116. let errmsg = '';
  117. for (let i = 0; i < this.result.writeConcernErrors.length; i++) {
  118. const err = this.result.writeConcernErrors[i];
  119. errmsg = errmsg + err.errmsg;
  120. // TODO: Something better
  121. if (i === 0)
  122. errmsg = errmsg + ' and ';
  123. }
  124. return new WriteConcernError({ errmsg, code: error_1.MONGODB_ERROR_CODES.WriteConcernTimeout });
  125. }
  126. }
  127. toString() {
  128. return `BulkWriteResult(${bson_1.EJSON.stringify(this.result)})`;
  129. }
  130. isOk() {
  131. return this.result.ok === 1;
  132. }
  133. }
  134. exports.BulkWriteResult = BulkWriteResult;
  135. /**
  136. * An error representing a failure by the server to apply the requested write concern to the bulk operation.
  137. * @public
  138. * @category Error
  139. */
  140. class WriteConcernError {
  141. constructor(error) {
  142. this.serverError = error;
  143. }
  144. /** Write concern error code. */
  145. get code() {
  146. return this.serverError.code;
  147. }
  148. /** Write concern error message. */
  149. get errmsg() {
  150. return this.serverError.errmsg;
  151. }
  152. /** Write concern error info. */
  153. get errInfo() {
  154. return this.serverError.errInfo;
  155. }
  156. toJSON() {
  157. return this.serverError;
  158. }
  159. toString() {
  160. return `WriteConcernError(${this.errmsg})`;
  161. }
  162. }
  163. exports.WriteConcernError = WriteConcernError;
  164. /**
  165. * An error that occurred during a BulkWrite on the server.
  166. * @public
  167. * @category Error
  168. */
  169. class WriteError {
  170. constructor(err) {
  171. this.err = err;
  172. }
  173. /** WriteError code. */
  174. get code() {
  175. return this.err.code;
  176. }
  177. /** WriteError original bulk operation index. */
  178. get index() {
  179. return this.err.index;
  180. }
  181. /** WriteError message. */
  182. get errmsg() {
  183. return this.err.errmsg;
  184. }
  185. /** WriteError details. */
  186. get errInfo() {
  187. return this.err.errInfo;
  188. }
  189. /** Returns the underlying operation that caused the error */
  190. getOperation() {
  191. return this.err.op;
  192. }
  193. toJSON() {
  194. return { code: this.err.code, index: this.err.index, errmsg: this.err.errmsg, op: this.err.op };
  195. }
  196. toString() {
  197. return `WriteError(${JSON.stringify(this.toJSON())})`;
  198. }
  199. }
  200. exports.WriteError = WriteError;
  201. /** Merges results into shared data structure */
  202. function mergeBatchResults(batch, bulkResult, err, result) {
  203. // If we have an error set the result to be the err object
  204. if (err) {
  205. result = err;
  206. }
  207. else if (result && result.result) {
  208. result = result.result;
  209. }
  210. if (result == null) {
  211. return;
  212. }
  213. // Do we have a top level error stop processing and return
  214. if (result.ok === 0 && bulkResult.ok === 1) {
  215. bulkResult.ok = 0;
  216. const writeError = {
  217. index: 0,
  218. code: result.code || 0,
  219. errmsg: result.message,
  220. errInfo: result.errInfo,
  221. op: batch.operations[0]
  222. };
  223. bulkResult.writeErrors.push(new WriteError(writeError));
  224. return;
  225. }
  226. else if (result.ok === 0 && bulkResult.ok === 0) {
  227. return;
  228. }
  229. // If we have an insert Batch type
  230. if (isInsertBatch(batch) && result.n) {
  231. bulkResult.nInserted = bulkResult.nInserted + result.n;
  232. }
  233. // If we have an insert Batch type
  234. if (isDeleteBatch(batch) && result.n) {
  235. bulkResult.nRemoved = bulkResult.nRemoved + result.n;
  236. }
  237. let nUpserted = 0;
  238. // We have an array of upserted values, we need to rewrite the indexes
  239. if (Array.isArray(result.upserted)) {
  240. nUpserted = result.upserted.length;
  241. for (let i = 0; i < result.upserted.length; i++) {
  242. bulkResult.upserted.push({
  243. index: result.upserted[i].index + batch.originalZeroIndex,
  244. _id: result.upserted[i]._id
  245. });
  246. }
  247. }
  248. else if (result.upserted) {
  249. nUpserted = 1;
  250. bulkResult.upserted.push({
  251. index: batch.originalZeroIndex,
  252. _id: result.upserted
  253. });
  254. }
  255. // If we have an update Batch type
  256. if (isUpdateBatch(batch) && result.n) {
  257. const nModified = result.nModified;
  258. bulkResult.nUpserted = bulkResult.nUpserted + nUpserted;
  259. bulkResult.nMatched = bulkResult.nMatched + (result.n - nUpserted);
  260. if (typeof nModified === 'number') {
  261. bulkResult.nModified = bulkResult.nModified + nModified;
  262. }
  263. else {
  264. bulkResult.nModified = 0;
  265. }
  266. }
  267. if (Array.isArray(result.writeErrors)) {
  268. for (let i = 0; i < result.writeErrors.length; i++) {
  269. const writeError = {
  270. index: batch.originalIndexes[result.writeErrors[i].index],
  271. code: result.writeErrors[i].code,
  272. errmsg: result.writeErrors[i].errmsg,
  273. errInfo: result.writeErrors[i].errInfo,
  274. op: batch.operations[result.writeErrors[i].index]
  275. };
  276. bulkResult.writeErrors.push(new WriteError(writeError));
  277. }
  278. }
  279. if (result.writeConcernError) {
  280. bulkResult.writeConcernErrors.push(new WriteConcernError(result.writeConcernError));
  281. }
  282. }
  283. async function executeCommands(bulkOperation, options) {
  284. if (bulkOperation.s.batches.length === 0) {
  285. return new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered);
  286. }
  287. for (const batch of bulkOperation.s.batches) {
  288. const finalOptions = (0, utils_1.resolveOptions)(bulkOperation, {
  289. ...options,
  290. ordered: bulkOperation.isOrdered
  291. });
  292. if (finalOptions.bypassDocumentValidation !== true) {
  293. delete finalOptions.bypassDocumentValidation;
  294. }
  295. // Is the bypassDocumentValidation options specific
  296. if (bulkOperation.s.bypassDocumentValidation === true) {
  297. finalOptions.bypassDocumentValidation = true;
  298. }
  299. // Is the checkKeys option disabled
  300. if (bulkOperation.s.checkKeys === false) {
  301. finalOptions.checkKeys = false;
  302. }
  303. if (finalOptions.retryWrites) {
  304. if (isUpdateBatch(batch)) {
  305. finalOptions.retryWrites =
  306. finalOptions.retryWrites && !batch.operations.some(op => op.multi);
  307. }
  308. if (isDeleteBatch(batch)) {
  309. finalOptions.retryWrites =
  310. finalOptions.retryWrites && !batch.operations.some(op => op.limit === 0);
  311. }
  312. }
  313. const operation = isInsertBatch(batch)
  314. ? new insert_1.InsertOperation(bulkOperation.s.namespace, batch.operations, finalOptions)
  315. : isUpdateBatch(batch)
  316. ? new update_1.UpdateOperation(bulkOperation.s.namespace, batch.operations, finalOptions)
  317. : isDeleteBatch(batch)
  318. ? new delete_1.DeleteOperation(bulkOperation.s.namespace, batch.operations, finalOptions)
  319. : null;
  320. if (operation == null)
  321. throw new error_1.MongoRuntimeError(`Unknown batchType: ${batch.batchType}`);
  322. let thrownError = null;
  323. let result;
  324. try {
  325. result = await (0, execute_operation_1.executeOperation)(bulkOperation.s.collection.client, operation, finalOptions.timeoutContext);
  326. }
  327. catch (error) {
  328. thrownError = error;
  329. }
  330. if (thrownError != null) {
  331. if (thrownError instanceof error_1.MongoWriteConcernError) {
  332. mergeBatchResults(batch, bulkOperation.s.bulkResult, thrownError, result);
  333. const writeResult = new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered);
  334. throw new MongoBulkWriteError({
  335. message: thrownError.result.writeConcernError.errmsg,
  336. code: thrownError.result.writeConcernError.code
  337. }, writeResult);
  338. }
  339. else {
  340. // Error is a driver related error not a bulk op error, return early
  341. throw new MongoBulkWriteError(thrownError, new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered));
  342. }
  343. }
  344. mergeBatchResults(batch, bulkOperation.s.bulkResult, thrownError, result);
  345. const writeResult = new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered);
  346. bulkOperation.handleWriteError(writeResult);
  347. }
  348. bulkOperation.s.batches.length = 0;
  349. const writeResult = new BulkWriteResult(bulkOperation.s.bulkResult, bulkOperation.isOrdered);
  350. bulkOperation.handleWriteError(writeResult);
  351. return writeResult;
  352. }
  353. /**
  354. * An error indicating an unsuccessful Bulk Write
  355. * @public
  356. * @category Error
  357. */
  358. class MongoBulkWriteError extends error_1.MongoServerError {
  359. /**
  360. * **Do not use this constructor!**
  361. *
  362. * Meant for internal use only.
  363. *
  364. * @remarks
  365. * This class is only meant to be constructed within the driver. This constructor is
  366. * not subject to semantic versioning compatibility guarantees and may change at any time.
  367. *
  368. * @public
  369. **/
  370. constructor(error, result) {
  371. super(error);
  372. this.writeErrors = [];
  373. if (error instanceof WriteConcernError)
  374. this.err = error;
  375. else if (!(error instanceof Error)) {
  376. this.message = error.message;
  377. this.code = error.code;
  378. this.writeErrors = error.writeErrors ?? [];
  379. }
  380. this.result = result;
  381. Object.assign(this, error);
  382. }
  383. get name() {
  384. return 'MongoBulkWriteError';
  385. }
  386. /** Number of documents inserted. */
  387. get insertedCount() {
  388. return this.result.insertedCount;
  389. }
  390. /** Number of documents matched for update. */
  391. get matchedCount() {
  392. return this.result.matchedCount;
  393. }
  394. /** Number of documents modified. */
  395. get modifiedCount() {
  396. return this.result.modifiedCount;
  397. }
  398. /** Number of documents deleted. */
  399. get deletedCount() {
  400. return this.result.deletedCount;
  401. }
  402. /** Number of documents upserted. */
  403. get upsertedCount() {
  404. return this.result.upsertedCount;
  405. }
  406. /** Inserted document generated Id's, hash key is the index of the originating operation */
  407. get insertedIds() {
  408. return this.result.insertedIds;
  409. }
  410. /** Upserted document generated Id's, hash key is the index of the originating operation */
  411. get upsertedIds() {
  412. return this.result.upsertedIds;
  413. }
  414. }
  415. exports.MongoBulkWriteError = MongoBulkWriteError;
  416. /**
  417. * A builder object that is returned from {@link BulkOperationBase#find}.
  418. * Is used to build a write operation that involves a query filter.
  419. *
  420. * @public
  421. */
  422. class FindOperators {
  423. /**
  424. * Creates a new FindOperators object.
  425. * @internal
  426. */
  427. constructor(bulkOperation) {
  428. this.bulkOperation = bulkOperation;
  429. }
  430. /** Add a multiple update operation to the bulk operation */
  431. update(updateDocument) {
  432. const currentOp = buildCurrentOp(this.bulkOperation);
  433. return this.bulkOperation.addToOperationsList(exports.BatchType.UPDATE, (0, update_1.makeUpdateStatement)(currentOp.selector, updateDocument, {
  434. ...currentOp,
  435. multi: true
  436. }));
  437. }
  438. /** Add a single update operation to the bulk operation */
  439. updateOne(updateDocument) {
  440. if (!(0, utils_1.hasAtomicOperators)(updateDocument, this.bulkOperation.bsonOptions)) {
  441. throw new error_1.MongoInvalidArgumentError('Update document requires atomic operators');
  442. }
  443. const currentOp = buildCurrentOp(this.bulkOperation);
  444. return this.bulkOperation.addToOperationsList(exports.BatchType.UPDATE, (0, update_1.makeUpdateStatement)(currentOp.selector, updateDocument, { ...currentOp, multi: false }));
  445. }
  446. /** Add a replace one operation to the bulk operation */
  447. replaceOne(replacement) {
  448. if ((0, utils_1.hasAtomicOperators)(replacement)) {
  449. throw new error_1.MongoInvalidArgumentError('Replacement document must not use atomic operators');
  450. }
  451. const currentOp = buildCurrentOp(this.bulkOperation);
  452. return this.bulkOperation.addToOperationsList(exports.BatchType.UPDATE, (0, update_1.makeUpdateStatement)(currentOp.selector, replacement, { ...currentOp, multi: false }));
  453. }
  454. /** Add a delete one operation to the bulk operation */
  455. deleteOne() {
  456. const currentOp = buildCurrentOp(this.bulkOperation);
  457. return this.bulkOperation.addToOperationsList(exports.BatchType.DELETE, (0, delete_1.makeDeleteStatement)(currentOp.selector, { ...currentOp, limit: 1 }));
  458. }
  459. /** Add a delete many operation to the bulk operation */
  460. delete() {
  461. const currentOp = buildCurrentOp(this.bulkOperation);
  462. return this.bulkOperation.addToOperationsList(exports.BatchType.DELETE, (0, delete_1.makeDeleteStatement)(currentOp.selector, { ...currentOp, limit: 0 }));
  463. }
  464. /** Upsert modifier for update bulk operation, noting that this operation is an upsert. */
  465. upsert() {
  466. if (!this.bulkOperation.s.currentOp) {
  467. this.bulkOperation.s.currentOp = {};
  468. }
  469. this.bulkOperation.s.currentOp.upsert = true;
  470. return this;
  471. }
  472. /** Specifies the collation for the query condition. */
  473. collation(collation) {
  474. if (!this.bulkOperation.s.currentOp) {
  475. this.bulkOperation.s.currentOp = {};
  476. }
  477. this.bulkOperation.s.currentOp.collation = collation;
  478. return this;
  479. }
  480. /** Specifies arrayFilters for UpdateOne or UpdateMany bulk operations. */
  481. arrayFilters(arrayFilters) {
  482. if (!this.bulkOperation.s.currentOp) {
  483. this.bulkOperation.s.currentOp = {};
  484. }
  485. this.bulkOperation.s.currentOp.arrayFilters = arrayFilters;
  486. return this;
  487. }
  488. /** Specifies hint for the bulk operation. */
  489. hint(hint) {
  490. if (!this.bulkOperation.s.currentOp) {
  491. this.bulkOperation.s.currentOp = {};
  492. }
  493. this.bulkOperation.s.currentOp.hint = hint;
  494. return this;
  495. }
  496. }
  497. exports.FindOperators = FindOperators;
  498. /** @public */
  499. class BulkOperationBase {
  500. /**
  501. * Create a new OrderedBulkOperation or UnorderedBulkOperation instance
  502. * @internal
  503. */
  504. constructor(collection, options, isOrdered) {
  505. this.collection = collection;
  506. // determine whether bulkOperation is ordered or unordered
  507. this.isOrdered = isOrdered;
  508. const topology = (0, utils_1.getTopology)(collection);
  509. options = options == null ? {} : options;
  510. // TODO Bring from driver information in hello
  511. // Get the namespace for the write operations
  512. const namespace = collection.s.namespace;
  513. // Used to mark operation as executed
  514. const executed = false;
  515. // Current item
  516. const currentOp = undefined;
  517. // Set max byte size
  518. const hello = topology.lastHello();
  519. // If we have autoEncryption on, batch-splitting must be done on 2mb chunks, but single documents
  520. // over 2mb are still allowed
  521. const usingAutoEncryption = !!(topology.s.options && topology.s.options.autoEncrypter);
  522. const maxBsonObjectSize = hello && hello.maxBsonObjectSize ? hello.maxBsonObjectSize : 1024 * 1024 * 16;
  523. const maxBatchSizeBytes = usingAutoEncryption ? 1024 * 1024 * 2 : maxBsonObjectSize;
  524. const maxWriteBatchSize = hello && hello.maxWriteBatchSize ? hello.maxWriteBatchSize : 1000;
  525. // Calculates the largest possible size of an Array key, represented as a BSON string
  526. // element. This calculation:
  527. // 1 byte for BSON type
  528. // # of bytes = length of (string representation of (maxWriteBatchSize - 1))
  529. // + 1 bytes for null terminator
  530. const maxKeySize = (maxWriteBatchSize - 1).toString(10).length + 2;
  531. // Final options for retryable writes
  532. let finalOptions = Object.assign({}, options);
  533. finalOptions = (0, utils_1.applyRetryableWrites)(finalOptions, collection.db);
  534. // Final results
  535. const bulkResult = {
  536. ok: 1,
  537. writeErrors: [],
  538. writeConcernErrors: [],
  539. insertedIds: [],
  540. nInserted: 0,
  541. nUpserted: 0,
  542. nMatched: 0,
  543. nModified: 0,
  544. nRemoved: 0,
  545. upserted: []
  546. };
  547. // Internal state
  548. this.s = {
  549. // Final result
  550. bulkResult,
  551. // Current batch state
  552. currentBatch: undefined,
  553. currentIndex: 0,
  554. // ordered specific
  555. currentBatchSize: 0,
  556. currentBatchSizeBytes: 0,
  557. // unordered specific
  558. currentInsertBatch: undefined,
  559. currentUpdateBatch: undefined,
  560. currentRemoveBatch: undefined,
  561. batches: [],
  562. // Write concern
  563. writeConcern: write_concern_1.WriteConcern.fromOptions(options),
  564. // Max batch size options
  565. maxBsonObjectSize,
  566. maxBatchSizeBytes,
  567. maxWriteBatchSize,
  568. maxKeySize,
  569. // Namespace
  570. namespace,
  571. // Topology
  572. topology,
  573. // Options
  574. options: finalOptions,
  575. // BSON options
  576. bsonOptions: (0, bson_1.resolveBSONOptions)(options),
  577. // Current operation
  578. currentOp,
  579. // Executed
  580. executed,
  581. // Collection
  582. collection,
  583. // Fundamental error
  584. err: undefined,
  585. // check keys
  586. checkKeys: typeof options.checkKeys === 'boolean' ? options.checkKeys : false
  587. };
  588. // bypass Validation
  589. if (options.bypassDocumentValidation === true) {
  590. this.s.bypassDocumentValidation = true;
  591. }
  592. }
  593. /**
  594. * Add a single insert document to the bulk operation
  595. *
  596. * @example
  597. * ```ts
  598. * const bulkOp = collection.initializeOrderedBulkOp();
  599. *
  600. * // Adds three inserts to the bulkOp.
  601. * bulkOp
  602. * .insert({ a: 1 })
  603. * .insert({ b: 2 })
  604. * .insert({ c: 3 });
  605. * await bulkOp.execute();
  606. * ```
  607. */
  608. insert(document) {
  609. (0, utils_1.maybeAddIdToDocuments)(this.collection, document, {
  610. forceServerObjectId: this.shouldForceServerObjectId()
  611. });
  612. return this.addToOperationsList(exports.BatchType.INSERT, document);
  613. }
  614. /**
  615. * Builds a find operation for an update/updateOne/delete/deleteOne/replaceOne.
  616. * Returns a builder object used to complete the definition of the operation.
  617. *
  618. * @example
  619. * ```ts
  620. * const bulkOp = collection.initializeOrderedBulkOp();
  621. *
  622. * // Add an updateOne to the bulkOp
  623. * bulkOp.find({ a: 1 }).updateOne({ $set: { b: 2 } });
  624. *
  625. * // Add an updateMany to the bulkOp
  626. * bulkOp.find({ c: 3 }).update({ $set: { d: 4 } });
  627. *
  628. * // Add an upsert
  629. * bulkOp.find({ e: 5 }).upsert().updateOne({ $set: { f: 6 } });
  630. *
  631. * // Add a deletion
  632. * bulkOp.find({ g: 7 }).deleteOne();
  633. *
  634. * // Add a multi deletion
  635. * bulkOp.find({ h: 8 }).delete();
  636. *
  637. * // Add a replaceOne
  638. * bulkOp.find({ i: 9 }).replaceOne({writeConcern: { j: 10 }});
  639. *
  640. * // Update using a pipeline (requires Mongodb 4.2 or higher)
  641. * bulk.find({ k: 11, y: { $exists: true }, z: { $exists: true } }).updateOne([
  642. * { $set: { total: { $sum: [ '$y', '$z' ] } } }
  643. * ]);
  644. *
  645. * // All of the ops will now be executed
  646. * await bulkOp.execute();
  647. * ```
  648. */
  649. find(selector) {
  650. if (!selector) {
  651. throw new error_1.MongoInvalidArgumentError('Bulk find operation must specify a selector');
  652. }
  653. // Save a current selector
  654. this.s.currentOp = {
  655. selector: selector
  656. };
  657. return new FindOperators(this);
  658. }
  659. /** Specifies a raw operation to perform in the bulk write. */
  660. raw(op) {
  661. if (op == null || typeof op !== 'object') {
  662. throw new error_1.MongoInvalidArgumentError('Operation must be an object with an operation key');
  663. }
  664. if ('insertOne' in op) {
  665. const forceServerObjectId = this.shouldForceServerObjectId();
  666. const document = op.insertOne && op.insertOne.document == null
  667. ? // TODO(NODE-6003): remove support for omitting the `documents` subdocument in bulk inserts
  668. op.insertOne
  669. : op.insertOne.document;
  670. (0, utils_1.maybeAddIdToDocuments)(this.collection, document, { forceServerObjectId });
  671. return this.addToOperationsList(exports.BatchType.INSERT, document);
  672. }
  673. if ('replaceOne' in op || 'updateOne' in op || 'updateMany' in op) {
  674. if ('replaceOne' in op) {
  675. if ('q' in op.replaceOne) {
  676. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  677. }
  678. const updateStatement = (0, update_1.makeUpdateStatement)(op.replaceOne.filter, op.replaceOne.replacement, { ...op.replaceOne, multi: false });
  679. if ((0, utils_1.hasAtomicOperators)(updateStatement.u)) {
  680. throw new error_1.MongoInvalidArgumentError('Replacement document must not use atomic operators');
  681. }
  682. return this.addToOperationsList(exports.BatchType.UPDATE, updateStatement);
  683. }
  684. if ('updateOne' in op) {
  685. if ('q' in op.updateOne) {
  686. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  687. }
  688. const updateStatement = (0, update_1.makeUpdateStatement)(op.updateOne.filter, op.updateOne.update, {
  689. ...op.updateOne,
  690. multi: false
  691. });
  692. if (!(0, utils_1.hasAtomicOperators)(updateStatement.u, this.bsonOptions)) {
  693. throw new error_1.MongoInvalidArgumentError('Update document requires atomic operators');
  694. }
  695. return this.addToOperationsList(exports.BatchType.UPDATE, updateStatement);
  696. }
  697. if ('updateMany' in op) {
  698. if ('q' in op.updateMany) {
  699. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  700. }
  701. const updateStatement = (0, update_1.makeUpdateStatement)(op.updateMany.filter, op.updateMany.update, {
  702. ...op.updateMany,
  703. multi: true
  704. });
  705. if (!(0, utils_1.hasAtomicOperators)(updateStatement.u, this.bsonOptions)) {
  706. throw new error_1.MongoInvalidArgumentError('Update document requires atomic operators');
  707. }
  708. return this.addToOperationsList(exports.BatchType.UPDATE, updateStatement);
  709. }
  710. }
  711. if ('deleteOne' in op) {
  712. if ('q' in op.deleteOne) {
  713. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  714. }
  715. return this.addToOperationsList(exports.BatchType.DELETE, (0, delete_1.makeDeleteStatement)(op.deleteOne.filter, { ...op.deleteOne, limit: 1 }));
  716. }
  717. if ('deleteMany' in op) {
  718. if ('q' in op.deleteMany) {
  719. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  720. }
  721. return this.addToOperationsList(exports.BatchType.DELETE, (0, delete_1.makeDeleteStatement)(op.deleteMany.filter, { ...op.deleteMany, limit: 0 }));
  722. }
  723. // otherwise an unknown operation was provided
  724. throw new error_1.MongoInvalidArgumentError('bulkWrite only supports insertOne, updateOne, updateMany, deleteOne, deleteMany');
  725. }
  726. get length() {
  727. return this.s.currentIndex;
  728. }
  729. get bsonOptions() {
  730. return this.s.bsonOptions;
  731. }
  732. get writeConcern() {
  733. return this.s.writeConcern;
  734. }
  735. get batches() {
  736. const batches = [...this.s.batches];
  737. if (this.isOrdered) {
  738. if (this.s.currentBatch)
  739. batches.push(this.s.currentBatch);
  740. }
  741. else {
  742. if (this.s.currentInsertBatch)
  743. batches.push(this.s.currentInsertBatch);
  744. if (this.s.currentUpdateBatch)
  745. batches.push(this.s.currentUpdateBatch);
  746. if (this.s.currentRemoveBatch)
  747. batches.push(this.s.currentRemoveBatch);
  748. }
  749. return batches;
  750. }
  751. async execute(options = {}) {
  752. if (this.s.executed) {
  753. throw new error_1.MongoBatchReExecutionError();
  754. }
  755. const writeConcern = write_concern_1.WriteConcern.fromOptions(options);
  756. if (writeConcern) {
  757. this.s.writeConcern = writeConcern;
  758. }
  759. // If we have current batch
  760. if (this.isOrdered) {
  761. if (this.s.currentBatch)
  762. this.s.batches.push(this.s.currentBatch);
  763. }
  764. else {
  765. if (this.s.currentInsertBatch)
  766. this.s.batches.push(this.s.currentInsertBatch);
  767. if (this.s.currentUpdateBatch)
  768. this.s.batches.push(this.s.currentUpdateBatch);
  769. if (this.s.currentRemoveBatch)
  770. this.s.batches.push(this.s.currentRemoveBatch);
  771. }
  772. // If we have no operations in the bulk raise an error
  773. if (this.s.batches.length === 0) {
  774. throw new error_1.MongoInvalidArgumentError('Invalid BulkOperation, Batch cannot be empty');
  775. }
  776. this.s.executed = true;
  777. const finalOptions = (0, utils_1.resolveOptions)(this.collection, { ...this.s.options, ...options });
  778. // if there is no timeoutContext provided, create a timeoutContext and use it for
  779. // all batches in the bulk operation
  780. finalOptions.timeoutContext ??= timeout_1.TimeoutContext.create({
  781. session: finalOptions.session,
  782. timeoutMS: finalOptions.timeoutMS,
  783. serverSelectionTimeoutMS: this.collection.client.s.options.serverSelectionTimeoutMS,
  784. waitQueueTimeoutMS: this.collection.client.s.options.waitQueueTimeoutMS
  785. });
  786. if (finalOptions.session == null) {
  787. // if there is not an explicit session provided to `execute()`, create
  788. // an implicit session and use that for all batches in the bulk operation
  789. return await this.collection.client.withSession({ explicit: false }, async (session) => {
  790. return await executeCommands(this, { ...finalOptions, session });
  791. });
  792. }
  793. return await executeCommands(this, { ...finalOptions });
  794. }
  795. /**
  796. * Handles the write error before executing commands
  797. * @internal
  798. */
  799. handleWriteError(writeResult) {
  800. if (this.s.bulkResult.writeErrors.length > 0) {
  801. const msg = this.s.bulkResult.writeErrors[0].errmsg
  802. ? this.s.bulkResult.writeErrors[0].errmsg
  803. : 'write operation failed';
  804. throw new MongoBulkWriteError({
  805. message: msg,
  806. code: this.s.bulkResult.writeErrors[0].code,
  807. writeErrors: this.s.bulkResult.writeErrors
  808. }, writeResult);
  809. }
  810. const writeConcernError = writeResult.getWriteConcernError();
  811. if (writeConcernError) {
  812. throw new MongoBulkWriteError(writeConcernError, writeResult);
  813. }
  814. }
  815. shouldForceServerObjectId() {
  816. return (this.s.options.forceServerObjectId === true ||
  817. this.s.collection.db.options?.forceServerObjectId === true);
  818. }
  819. }
  820. exports.BulkOperationBase = BulkOperationBase;
  821. function isInsertBatch(batch) {
  822. return batch.batchType === exports.BatchType.INSERT;
  823. }
  824. function isUpdateBatch(batch) {
  825. return batch.batchType === exports.BatchType.UPDATE;
  826. }
  827. function isDeleteBatch(batch) {
  828. return batch.batchType === exports.BatchType.DELETE;
  829. }
  830. function buildCurrentOp(bulkOp) {
  831. let { currentOp } = bulkOp.s;
  832. bulkOp.s.currentOp = undefined;
  833. if (!currentOp)
  834. currentOp = {};
  835. return currentOp;
  836. }
  837. //# sourceMappingURL=common.js.map