common.js 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835
  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 (bulkOperation.retryWrites) {
  304. if (isUpdateBatch(batch)) {
  305. bulkOperation.retryWrites =
  306. bulkOperation.retryWrites && !batch.operations.some(op => op.multi);
  307. }
  308. if (isDeleteBatch(batch)) {
  309. bulkOperation.retryWrites =
  310. bulkOperation.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. this.retryWrites = collection.db.options?.retryWrites;
  507. // determine whether bulkOperation is ordered or unordered
  508. this.isOrdered = isOrdered;
  509. const topology = (0, utils_1.getTopology)(collection);
  510. options = options == null ? {} : options;
  511. // TODO Bring from driver information in hello
  512. // Get the namespace for the write operations
  513. const namespace = collection.s.namespace;
  514. // Used to mark operation as executed
  515. const executed = false;
  516. // Current item
  517. const currentOp = undefined;
  518. // Set max byte size
  519. const hello = topology.lastHello();
  520. // If we have autoEncryption on, batch-splitting must be done on 2mb chunks, but single documents
  521. // over 2mb are still allowed
  522. const usingAutoEncryption = !!(topology.s.options && topology.s.options.autoEncrypter);
  523. const maxBsonObjectSize = hello && hello.maxBsonObjectSize ? hello.maxBsonObjectSize : 1024 * 1024 * 16;
  524. const maxBatchSizeBytes = usingAutoEncryption ? 1024 * 1024 * 2 : maxBsonObjectSize;
  525. const maxWriteBatchSize = hello && hello.maxWriteBatchSize ? hello.maxWriteBatchSize : 1000;
  526. // Calculates the largest possible size of an Array key, represented as a BSON string
  527. // element. This calculation:
  528. // 1 byte for BSON type
  529. // # of bytes = length of (string representation of (maxWriteBatchSize - 1))
  530. // + 1 bytes for null terminator
  531. const maxKeySize = (maxWriteBatchSize - 1).toString(10).length + 2;
  532. // Final results
  533. const bulkResult = {
  534. ok: 1,
  535. writeErrors: [],
  536. writeConcernErrors: [],
  537. insertedIds: [],
  538. nInserted: 0,
  539. nUpserted: 0,
  540. nMatched: 0,
  541. nModified: 0,
  542. nRemoved: 0,
  543. upserted: []
  544. };
  545. // Internal state
  546. this.s = {
  547. // Final result
  548. bulkResult,
  549. // Current batch state
  550. currentBatch: undefined,
  551. currentIndex: 0,
  552. // ordered specific
  553. currentBatchSize: 0,
  554. currentBatchSizeBytes: 0,
  555. // unordered specific
  556. currentInsertBatch: undefined,
  557. currentUpdateBatch: undefined,
  558. currentRemoveBatch: undefined,
  559. batches: [],
  560. // Write concern
  561. writeConcern: write_concern_1.WriteConcern.fromOptions(options),
  562. // Max batch size options
  563. maxBsonObjectSize,
  564. maxBatchSizeBytes,
  565. maxWriteBatchSize,
  566. maxKeySize,
  567. // Namespace
  568. namespace,
  569. // Topology
  570. topology,
  571. // Options
  572. options: options,
  573. // BSON options
  574. bsonOptions: (0, bson_1.resolveBSONOptions)(options),
  575. // Current operation
  576. currentOp,
  577. // Executed
  578. executed,
  579. // Collection
  580. collection,
  581. // Fundamental error
  582. err: undefined,
  583. // check keys
  584. checkKeys: typeof options.checkKeys === 'boolean' ? options.checkKeys : false
  585. };
  586. // bypass Validation
  587. if (options.bypassDocumentValidation === true) {
  588. this.s.bypassDocumentValidation = true;
  589. }
  590. }
  591. /**
  592. * Add a single insert document to the bulk operation
  593. *
  594. * @example
  595. * ```ts
  596. * const bulkOp = collection.initializeOrderedBulkOp();
  597. *
  598. * // Adds three inserts to the bulkOp.
  599. * bulkOp
  600. * .insert({ a: 1 })
  601. * .insert({ b: 2 })
  602. * .insert({ c: 3 });
  603. * await bulkOp.execute();
  604. * ```
  605. */
  606. insert(document) {
  607. (0, utils_1.maybeAddIdToDocuments)(this.collection, document, {
  608. forceServerObjectId: this.shouldForceServerObjectId()
  609. });
  610. return this.addToOperationsList(exports.BatchType.INSERT, document);
  611. }
  612. /**
  613. * Builds a find operation for an update/updateOne/delete/deleteOne/replaceOne.
  614. * Returns a builder object used to complete the definition of the operation.
  615. *
  616. * @example
  617. * ```ts
  618. * const bulkOp = collection.initializeOrderedBulkOp();
  619. *
  620. * // Add an updateOne to the bulkOp
  621. * bulkOp.find({ a: 1 }).updateOne({ $set: { b: 2 } });
  622. *
  623. * // Add an updateMany to the bulkOp
  624. * bulkOp.find({ c: 3 }).update({ $set: { d: 4 } });
  625. *
  626. * // Add an upsert
  627. * bulkOp.find({ e: 5 }).upsert().updateOne({ $set: { f: 6 } });
  628. *
  629. * // Add a deletion
  630. * bulkOp.find({ g: 7 }).deleteOne();
  631. *
  632. * // Add a multi deletion
  633. * bulkOp.find({ h: 8 }).delete();
  634. *
  635. * // Add a replaceOne
  636. * bulkOp.find({ i: 9 }).replaceOne({writeConcern: { j: 10 }});
  637. *
  638. * // Update using a pipeline (requires Mongodb 4.2 or higher)
  639. * bulk.find({ k: 11, y: { $exists: true }, z: { $exists: true } }).updateOne([
  640. * { $set: { total: { $sum: [ '$y', '$z' ] } } }
  641. * ]);
  642. *
  643. * // All of the ops will now be executed
  644. * await bulkOp.execute();
  645. * ```
  646. */
  647. find(selector) {
  648. if (!selector) {
  649. throw new error_1.MongoInvalidArgumentError('Bulk find operation must specify a selector');
  650. }
  651. // Save a current selector
  652. this.s.currentOp = {
  653. selector: selector
  654. };
  655. return new FindOperators(this);
  656. }
  657. /** Specifies a raw operation to perform in the bulk write. */
  658. raw(op) {
  659. if (op == null || typeof op !== 'object') {
  660. throw new error_1.MongoInvalidArgumentError('Operation must be an object with an operation key');
  661. }
  662. if ('insertOne' in op) {
  663. const forceServerObjectId = this.shouldForceServerObjectId();
  664. const document = op.insertOne && op.insertOne.document == null
  665. ? // TODO(NODE-6003): remove support for omitting the `documents` subdocument in bulk inserts
  666. op.insertOne
  667. : op.insertOne.document;
  668. (0, utils_1.maybeAddIdToDocuments)(this.collection, document, { forceServerObjectId });
  669. return this.addToOperationsList(exports.BatchType.INSERT, document);
  670. }
  671. if ('replaceOne' in op || 'updateOne' in op || 'updateMany' in op) {
  672. if ('replaceOne' in op) {
  673. if ('q' in op.replaceOne) {
  674. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  675. }
  676. const updateStatement = (0, update_1.makeUpdateStatement)(op.replaceOne.filter, op.replaceOne.replacement, { ...op.replaceOne, multi: false });
  677. if ((0, utils_1.hasAtomicOperators)(updateStatement.u)) {
  678. throw new error_1.MongoInvalidArgumentError('Replacement document must not use atomic operators');
  679. }
  680. return this.addToOperationsList(exports.BatchType.UPDATE, updateStatement);
  681. }
  682. if ('updateOne' in op) {
  683. if ('q' in op.updateOne) {
  684. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  685. }
  686. const updateStatement = (0, update_1.makeUpdateStatement)(op.updateOne.filter, op.updateOne.update, {
  687. ...op.updateOne,
  688. multi: false
  689. });
  690. if (!(0, utils_1.hasAtomicOperators)(updateStatement.u, this.bsonOptions)) {
  691. throw new error_1.MongoInvalidArgumentError('Update document requires atomic operators');
  692. }
  693. return this.addToOperationsList(exports.BatchType.UPDATE, updateStatement);
  694. }
  695. if ('updateMany' in op) {
  696. if ('q' in op.updateMany) {
  697. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  698. }
  699. const updateStatement = (0, update_1.makeUpdateStatement)(op.updateMany.filter, op.updateMany.update, {
  700. ...op.updateMany,
  701. multi: true
  702. });
  703. if (!(0, utils_1.hasAtomicOperators)(updateStatement.u, this.bsonOptions)) {
  704. throw new error_1.MongoInvalidArgumentError('Update document requires atomic operators');
  705. }
  706. return this.addToOperationsList(exports.BatchType.UPDATE, updateStatement);
  707. }
  708. }
  709. if ('deleteOne' in op) {
  710. if ('q' in op.deleteOne) {
  711. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  712. }
  713. return this.addToOperationsList(exports.BatchType.DELETE, (0, delete_1.makeDeleteStatement)(op.deleteOne.filter, { ...op.deleteOne, limit: 1 }));
  714. }
  715. if ('deleteMany' in op) {
  716. if ('q' in op.deleteMany) {
  717. throw new error_1.MongoInvalidArgumentError('Raw operations are not allowed');
  718. }
  719. return this.addToOperationsList(exports.BatchType.DELETE, (0, delete_1.makeDeleteStatement)(op.deleteMany.filter, { ...op.deleteMany, limit: 0 }));
  720. }
  721. // otherwise an unknown operation was provided
  722. throw new error_1.MongoInvalidArgumentError('bulkWrite only supports insertOne, updateOne, updateMany, deleteOne, deleteMany');
  723. }
  724. get length() {
  725. return this.s.currentIndex;
  726. }
  727. get bsonOptions() {
  728. return this.s.bsonOptions;
  729. }
  730. get writeConcern() {
  731. return this.s.writeConcern;
  732. }
  733. get batches() {
  734. const batches = [...this.s.batches];
  735. if (this.isOrdered) {
  736. if (this.s.currentBatch)
  737. batches.push(this.s.currentBatch);
  738. }
  739. else {
  740. if (this.s.currentInsertBatch)
  741. batches.push(this.s.currentInsertBatch);
  742. if (this.s.currentUpdateBatch)
  743. batches.push(this.s.currentUpdateBatch);
  744. if (this.s.currentRemoveBatch)
  745. batches.push(this.s.currentRemoveBatch);
  746. }
  747. return batches;
  748. }
  749. async execute(options = {}) {
  750. if (this.s.executed) {
  751. throw new error_1.MongoBatchReExecutionError();
  752. }
  753. const writeConcern = write_concern_1.WriteConcern.fromOptions(options);
  754. if (writeConcern) {
  755. this.s.writeConcern = writeConcern;
  756. }
  757. // If we have current batch
  758. if (this.isOrdered) {
  759. if (this.s.currentBatch)
  760. this.s.batches.push(this.s.currentBatch);
  761. }
  762. else {
  763. if (this.s.currentInsertBatch)
  764. this.s.batches.push(this.s.currentInsertBatch);
  765. if (this.s.currentUpdateBatch)
  766. this.s.batches.push(this.s.currentUpdateBatch);
  767. if (this.s.currentRemoveBatch)
  768. this.s.batches.push(this.s.currentRemoveBatch);
  769. }
  770. // If we have no operations in the bulk raise an error
  771. if (this.s.batches.length === 0) {
  772. throw new error_1.MongoInvalidArgumentError('Invalid BulkOperation, Batch cannot be empty');
  773. }
  774. this.s.executed = true;
  775. const finalOptions = (0, utils_1.resolveOptions)(this.collection, { ...this.s.options, ...options });
  776. // if there is no timeoutContext provided, create a timeoutContext and use it for
  777. // all batches in the bulk operation
  778. finalOptions.timeoutContext ??= timeout_1.TimeoutContext.create({
  779. session: finalOptions.session,
  780. timeoutMS: finalOptions.timeoutMS,
  781. serverSelectionTimeoutMS: this.collection.client.s.options.serverSelectionTimeoutMS,
  782. waitQueueTimeoutMS: this.collection.client.s.options.waitQueueTimeoutMS
  783. });
  784. if (finalOptions.session == null) {
  785. // if there is not an explicit session provided to `execute()`, create
  786. // an implicit session and use that for all batches in the bulk operation
  787. return await this.collection.client.withSession({ explicit: false }, async (session) => {
  788. return await executeCommands(this, { ...finalOptions, session });
  789. });
  790. }
  791. return await executeCommands(this, { ...finalOptions });
  792. }
  793. /**
  794. * Handles the write error before executing commands
  795. * @internal
  796. */
  797. handleWriteError(writeResult) {
  798. if (this.s.bulkResult.writeErrors.length > 0) {
  799. const msg = this.s.bulkResult.writeErrors[0].errmsg
  800. ? this.s.bulkResult.writeErrors[0].errmsg
  801. : 'write operation failed';
  802. throw new MongoBulkWriteError({
  803. message: msg,
  804. code: this.s.bulkResult.writeErrors[0].code,
  805. writeErrors: this.s.bulkResult.writeErrors
  806. }, writeResult);
  807. }
  808. const writeConcernError = writeResult.getWriteConcernError();
  809. if (writeConcernError) {
  810. throw new MongoBulkWriteError(writeConcernError, writeResult);
  811. }
  812. }
  813. shouldForceServerObjectId() {
  814. return (this.s.options.forceServerObjectId === true ||
  815. this.s.collection.db.options?.forceServerObjectId === true);
  816. }
  817. }
  818. exports.BulkOperationBase = BulkOperationBase;
  819. function isInsertBatch(batch) {
  820. return batch.batchType === exports.BatchType.INSERT;
  821. }
  822. function isUpdateBatch(batch) {
  823. return batch.batchType === exports.BatchType.UPDATE;
  824. }
  825. function isDeleteBatch(batch) {
  826. return batch.batchType === exports.BatchType.DELETE;
  827. }
  828. function buildCurrentOp(bulkOp) {
  829. let { currentOp } = bulkOp.s;
  830. bulkOp.s.currentOp = undefined;
  831. if (!currentOp)
  832. currentOp = {};
  833. return currentOp;
  834. }
  835. //# sourceMappingURL=common.js.map