common.js 36 KB

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