common.js 35 KB

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