common.js 33 KB

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