common.js 33 KB

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