/node_modules/mongoose/node_modules/mongodb/lib/bulk/ordered.js

https://bitbucket.org/coleman333/smartsite · JavaScript · 617 lines · 371 code · 75 blank · 171 comment · 84 complexity · 94ed0b9c8fe92554f00453d28d5aaeca MD5 · raw file

  1. 'use strict';
  2. var common = require('./common'),
  3. utils = require('../utils'),
  4. toError = require('../utils').toError,
  5. handleCallback = require('../utils').handleCallback,
  6. shallowClone = utils.shallowClone,
  7. BulkWriteResult = common.BulkWriteResult,
  8. ObjectID = require('mongodb-core').BSON.ObjectID,
  9. Define = require('../metadata'),
  10. BSON = require('mongodb-core').BSON,
  11. Batch = common.Batch,
  12. mergeBatchResults = common.mergeBatchResults,
  13. executeOperation = require('../utils').executeOperation,
  14. BulkWriteError = require('./common').BulkWriteError;
  15. var bson = new BSON([
  16. BSON.Binary,
  17. BSON.Code,
  18. BSON.DBRef,
  19. BSON.Decimal128,
  20. BSON.Double,
  21. BSON.Int32,
  22. BSON.Long,
  23. BSON.Map,
  24. BSON.MaxKey,
  25. BSON.MinKey,
  26. BSON.ObjectId,
  27. BSON.BSONRegExp,
  28. BSON.Symbol,
  29. BSON.Timestamp
  30. ]);
  31. /**
  32. * Create a FindOperatorsOrdered instance (INTERNAL TYPE, do not instantiate directly)
  33. * @class
  34. * @return {FindOperatorsOrdered} a FindOperatorsOrdered instance.
  35. */
  36. var FindOperatorsOrdered = function(self) {
  37. this.s = self.s;
  38. };
  39. /**
  40. * Add a single update document to the bulk operation
  41. *
  42. * @method
  43. * @param {object} doc update operations
  44. * @throws {MongoError}
  45. * @return {OrderedBulkOperation}
  46. */
  47. FindOperatorsOrdered.prototype.update = function(updateDocument) {
  48. // Perform upsert
  49. var upsert = typeof this.s.currentOp.upsert === 'boolean' ? this.s.currentOp.upsert : false;
  50. // Establish the update command
  51. var document = {
  52. q: this.s.currentOp.selector,
  53. u: updateDocument,
  54. multi: true,
  55. upsert: upsert
  56. };
  57. // Clear out current Op
  58. this.s.currentOp = null;
  59. // Add the update document to the list
  60. return addToOperationsList(this, common.UPDATE, document);
  61. };
  62. /**
  63. * Add a single update one document to the bulk operation
  64. *
  65. * @method
  66. * @param {object} doc update operations
  67. * @throws {MongoError}
  68. * @return {OrderedBulkOperation}
  69. */
  70. FindOperatorsOrdered.prototype.updateOne = function(updateDocument) {
  71. // Perform upsert
  72. var upsert = typeof this.s.currentOp.upsert === 'boolean' ? this.s.currentOp.upsert : false;
  73. // Establish the update command
  74. var document = {
  75. q: this.s.currentOp.selector,
  76. u: updateDocument,
  77. multi: false,
  78. upsert: upsert
  79. };
  80. // Clear out current Op
  81. this.s.currentOp = null;
  82. // Add the update document to the list
  83. return addToOperationsList(this, common.UPDATE, document);
  84. };
  85. /**
  86. * Add a replace one operation to the bulk operation
  87. *
  88. * @method
  89. * @param {object} doc the new document to replace the existing one with
  90. * @throws {MongoError}
  91. * @return {OrderedBulkOperation}
  92. */
  93. FindOperatorsOrdered.prototype.replaceOne = function(updateDocument) {
  94. this.updateOne(updateDocument);
  95. };
  96. /**
  97. * Upsert modifier for update bulk operation
  98. *
  99. * @method
  100. * @throws {MongoError}
  101. * @return {FindOperatorsOrdered}
  102. */
  103. FindOperatorsOrdered.prototype.upsert = function() {
  104. this.s.currentOp.upsert = true;
  105. return this;
  106. };
  107. /**
  108. * Add a remove one operation to the bulk operation
  109. *
  110. * @method
  111. * @throws {MongoError}
  112. * @return {OrderedBulkOperation}
  113. */
  114. FindOperatorsOrdered.prototype.deleteOne = function() {
  115. // Establish the update command
  116. var document = {
  117. q: this.s.currentOp.selector,
  118. limit: 1
  119. };
  120. // Clear out current Op
  121. this.s.currentOp = null;
  122. // Add the remove document to the list
  123. return addToOperationsList(this, common.REMOVE, document);
  124. };
  125. // Backward compatibility
  126. FindOperatorsOrdered.prototype.removeOne = FindOperatorsOrdered.prototype.deleteOne;
  127. /**
  128. * Add a remove operation to the bulk operation
  129. *
  130. * @method
  131. * @throws {MongoError}
  132. * @return {OrderedBulkOperation}
  133. */
  134. FindOperatorsOrdered.prototype.delete = function() {
  135. // Establish the update command
  136. var document = {
  137. q: this.s.currentOp.selector,
  138. limit: 0
  139. };
  140. // Clear out current Op
  141. this.s.currentOp = null;
  142. // Add the remove document to the list
  143. return addToOperationsList(this, common.REMOVE, document);
  144. };
  145. // Backward compatibility
  146. FindOperatorsOrdered.prototype.remove = FindOperatorsOrdered.prototype.delete;
  147. // Add to internal list of documents
  148. var addToOperationsList = function(_self, docType, document) {
  149. // Get the bsonSize
  150. var bsonSize = bson.calculateObjectSize(document, {
  151. checkKeys: false
  152. });
  153. // Throw error if the doc is bigger than the max BSON size
  154. if (bsonSize >= _self.s.maxBatchSizeBytes) {
  155. throw toError('document is larger than the maximum size ' + _self.s.maxBatchSizeBytes);
  156. }
  157. // Create a new batch object if we don't have a current one
  158. if (_self.s.currentBatch == null) _self.s.currentBatch = new Batch(docType, _self.s.currentIndex);
  159. // Check if we need to create a new batch
  160. if (
  161. _self.s.currentBatchSize + 1 >= _self.s.maxWriteBatchSize ||
  162. _self.s.currentBatchSizeBytes + _self.s.currentBatchSizeBytes >= _self.s.maxBatchSizeBytes ||
  163. _self.s.currentBatch.batchType !== docType
  164. ) {
  165. // Save the batch to the execution stack
  166. _self.s.batches.push(_self.s.currentBatch);
  167. // Create a new batch
  168. _self.s.currentBatch = new Batch(docType, _self.s.currentIndex);
  169. // Reset the current size trackers
  170. _self.s.currentBatchSize = 0;
  171. _self.s.currentBatchSizeBytes = 0;
  172. } else {
  173. // Update current batch size
  174. _self.s.currentBatchSize = _self.s.currentBatchSize + 1;
  175. _self.s.currentBatchSizeBytes = _self.s.currentBatchSizeBytes + bsonSize;
  176. }
  177. if (docType === common.INSERT) {
  178. _self.s.bulkResult.insertedIds.push({ index: _self.s.currentIndex, _id: document._id });
  179. }
  180. // We have an array of documents
  181. if (Array.isArray(document)) {
  182. throw toError('operation passed in cannot be an Array');
  183. } else {
  184. _self.s.currentBatch.originalIndexes.push(_self.s.currentIndex);
  185. _self.s.currentBatch.operations.push(document);
  186. _self.s.currentBatchSizeBytes = _self.s.currentBatchSizeBytes + bsonSize;
  187. _self.s.currentIndex = _self.s.currentIndex + 1;
  188. }
  189. // Return self
  190. return _self;
  191. };
  192. /**
  193. * Create a new OrderedBulkOperation instance (INTERNAL TYPE, do not instantiate directly)
  194. * @class
  195. * @property {number} length Get the number of operations in the bulk.
  196. * @return {OrderedBulkOperation} a OrderedBulkOperation instance.
  197. */
  198. function OrderedBulkOperation(topology, collection, options) {
  199. options = options == null ? {} : options;
  200. // TODO Bring from driver information in isMaster
  201. var executed = false;
  202. // Current item
  203. var currentOp = null;
  204. // Handle to the bson serializer, used to calculate running sizes
  205. var bson = topology.bson;
  206. // Namespace for the operation
  207. var namespace = collection.collectionName;
  208. // Set max byte size
  209. var maxBatchSizeBytes =
  210. topology.isMasterDoc && topology.isMasterDoc.maxBsonObjectSize
  211. ? topology.isMasterDoc.maxBsonObjectSize
  212. : 1024 * 1025 * 16;
  213. var maxWriteBatchSize =
  214. topology.isMasterDoc && topology.isMasterDoc.maxWriteBatchSize
  215. ? topology.isMasterDoc.maxWriteBatchSize
  216. : 1000;
  217. // Get the write concern
  218. var writeConcern = common.writeConcern(shallowClone(options), collection, options);
  219. // Get the promiseLibrary
  220. var promiseLibrary = options.promiseLibrary || Promise;
  221. // Final results
  222. var bulkResult = {
  223. ok: 1,
  224. writeErrors: [],
  225. writeConcernErrors: [],
  226. insertedIds: [],
  227. nInserted: 0,
  228. nUpserted: 0,
  229. nMatched: 0,
  230. nModified: 0,
  231. nRemoved: 0,
  232. upserted: []
  233. };
  234. // Internal state
  235. this.s = {
  236. // Final result
  237. bulkResult: bulkResult,
  238. // Current batch state
  239. currentBatch: null,
  240. currentIndex: 0,
  241. currentBatchSize: 0,
  242. currentBatchSizeBytes: 0,
  243. batches: [],
  244. // Write concern
  245. writeConcern: writeConcern,
  246. // Max batch size options
  247. maxBatchSizeBytes: maxBatchSizeBytes,
  248. maxWriteBatchSize: maxWriteBatchSize,
  249. // Namespace
  250. namespace: namespace,
  251. // BSON
  252. bson: bson,
  253. // Topology
  254. topology: topology,
  255. // Options
  256. options: options,
  257. // Current operation
  258. currentOp: currentOp,
  259. // Executed
  260. executed: executed,
  261. // Collection
  262. collection: collection,
  263. // Promise Library
  264. promiseLibrary: promiseLibrary,
  265. // Fundamental error
  266. err: null,
  267. // Bypass validation
  268. bypassDocumentValidation:
  269. typeof options.bypassDocumentValidation === 'boolean'
  270. ? options.bypassDocumentValidation
  271. : false,
  272. // check keys
  273. checkKeys: typeof options.checkKeys === 'boolean' ? options.checkKeys : true
  274. };
  275. }
  276. var define = (OrderedBulkOperation.define = new Define(
  277. 'OrderedBulkOperation',
  278. OrderedBulkOperation,
  279. false
  280. ));
  281. OrderedBulkOperation.prototype.raw = function(op) {
  282. var key = Object.keys(op)[0];
  283. // Set up the force server object id
  284. var forceServerObjectId =
  285. typeof this.s.options.forceServerObjectId === 'boolean'
  286. ? this.s.options.forceServerObjectId
  287. : this.s.collection.s.db.options.forceServerObjectId;
  288. // Update operations
  289. if (
  290. (op.updateOne && op.updateOne.q) ||
  291. (op.updateMany && op.updateMany.q) ||
  292. (op.replaceOne && op.replaceOne.q)
  293. ) {
  294. op[key].multi = op.updateOne || op.replaceOne ? false : true;
  295. return addToOperationsList(this, common.UPDATE, op[key]);
  296. }
  297. // Crud spec update format
  298. if (op.updateOne || op.updateMany || op.replaceOne) {
  299. var multi = op.updateOne || op.replaceOne ? false : true;
  300. var operation = { q: op[key].filter, u: op[key].update || op[key].replacement, multi: multi };
  301. operation.upsert = op[key].upsert ? true : false;
  302. if (op.collation) operation.collation = op.collation;
  303. if (op[key].arrayFilters) operation.arrayFilters = op[key].arrayFilters;
  304. return addToOperationsList(this, common.UPDATE, operation);
  305. }
  306. // Remove operations
  307. if (
  308. op.removeOne ||
  309. op.removeMany ||
  310. (op.deleteOne && op.deleteOne.q) ||
  311. (op.deleteMany && op.deleteMany.q)
  312. ) {
  313. op[key].limit = op.removeOne ? 1 : 0;
  314. return addToOperationsList(this, common.REMOVE, op[key]);
  315. }
  316. // Crud spec delete operations, less efficient
  317. if (op.deleteOne || op.deleteMany) {
  318. var limit = op.deleteOne ? 1 : 0;
  319. operation = { q: op[key].filter, limit: limit };
  320. if (op.collation) operation.collation = op.collation;
  321. return addToOperationsList(this, common.REMOVE, operation);
  322. }
  323. // Insert operations
  324. if (op.insertOne && op.insertOne.document == null) {
  325. if (forceServerObjectId !== true && op.insertOne._id == null) op.insertOne._id = new ObjectID();
  326. return addToOperationsList(this, common.INSERT, op.insertOne);
  327. } else if (op.insertOne && op.insertOne.document) {
  328. if (forceServerObjectId !== true && op.insertOne.document._id == null)
  329. op.insertOne.document._id = new ObjectID();
  330. return addToOperationsList(this, common.INSERT, op.insertOne.document);
  331. }
  332. if (op.insertMany) {
  333. for (var i = 0; i < op.insertMany.length; i++) {
  334. if (forceServerObjectId !== true && op.insertMany[i]._id == null)
  335. op.insertMany[i]._id = new ObjectID();
  336. addToOperationsList(this, common.INSERT, op.insertMany[i]);
  337. }
  338. return;
  339. }
  340. // No valid type of operation
  341. throw toError(
  342. 'bulkWrite only supports insertOne, insertMany, updateOne, updateMany, removeOne, removeMany, deleteOne, deleteMany'
  343. );
  344. };
  345. /**
  346. * Add a single insert document to the bulk operation
  347. *
  348. * @param {object} doc the document to insert
  349. * @throws {MongoError}
  350. * @return {OrderedBulkOperation}
  351. */
  352. OrderedBulkOperation.prototype.insert = function(document) {
  353. if (this.s.collection.s.db.options.forceServerObjectId !== true && document._id == null)
  354. document._id = new ObjectID();
  355. return addToOperationsList(this, common.INSERT, document);
  356. };
  357. /**
  358. * Initiate a find operation for an update/updateOne/remove/removeOne/replaceOne
  359. *
  360. * @method
  361. * @param {object} selector The selector for the bulk operation.
  362. * @throws {MongoError}
  363. * @return {FindOperatorsOrdered}
  364. */
  365. OrderedBulkOperation.prototype.find = function(selector) {
  366. if (!selector) {
  367. throw toError('Bulk find operation must specify a selector');
  368. }
  369. // Save a current selector
  370. this.s.currentOp = {
  371. selector: selector
  372. };
  373. return new FindOperatorsOrdered(this);
  374. };
  375. Object.defineProperty(OrderedBulkOperation.prototype, 'length', {
  376. enumerable: true,
  377. get: function() {
  378. return this.s.currentIndex;
  379. }
  380. });
  381. //
  382. // Execute next write command in a chain
  383. var executeCommands = function(self, options, callback) {
  384. if (self.s.batches.length === 0) {
  385. return handleCallback(callback, null, new BulkWriteResult(self.s.bulkResult));
  386. }
  387. // Ordered execution of the command
  388. var batch = self.s.batches.shift();
  389. var resultHandler = function(err, result) {
  390. // Error is a driver related error not a bulk op error, terminate
  391. if ((err && err.driver) || (err && err.message)) {
  392. return handleCallback(callback, err);
  393. }
  394. // If we have and error
  395. if (err) err.ok = 0;
  396. // Merge the results together
  397. var mergeResult = mergeBatchResults(true, batch, self.s.bulkResult, err, result);
  398. const writeResult = new BulkWriteResult(self.s.bulkResult);
  399. if (mergeResult != null) {
  400. return handleCallback(callback, null, writeResult);
  401. }
  402. // If we are ordered and have errors and they are
  403. // not all replication errors terminate the operation
  404. if (self.s.bulkResult.writeErrors.length > 0) {
  405. if (self.s.bulkResult.writeErrors.length === 1) {
  406. return handleCallback(
  407. callback,
  408. new BulkWriteError(toError(self.s.bulkResult.writeErrors[0]), writeResult),
  409. null
  410. );
  411. }
  412. return handleCallback(
  413. callback,
  414. new BulkWriteError(
  415. toError({
  416. message: 'write operation failed',
  417. code: self.s.bulkResult.writeErrors[0].code,
  418. writeErrors: self.s.bulkResult.writeErrors
  419. }),
  420. writeResult
  421. ),
  422. null
  423. );
  424. } else if (writeResult.getWriteConcernError()) {
  425. return handleCallback(
  426. callback,
  427. new BulkWriteError(toError(writeResult.getWriteConcernError()), writeResult),
  428. null
  429. );
  430. }
  431. // Execute the next command in line
  432. executeCommands(self, options, callback);
  433. };
  434. var finalOptions = Object.assign({ ordered: true }, options);
  435. if (self.s.writeConcern != null) {
  436. finalOptions.writeConcern = self.s.writeConcern;
  437. }
  438. // Set an operationIf if provided
  439. if (self.operationId) {
  440. resultHandler.operationId = self.operationId;
  441. }
  442. // Serialize functions
  443. if (self.s.options.serializeFunctions) {
  444. finalOptions.serializeFunctions = true;
  445. }
  446. // Ignore undefined
  447. if (self.s.options.ignoreUndefined) {
  448. finalOptions.ignoreUndefined = true;
  449. }
  450. // Is the bypassDocumentValidation options specific
  451. if (self.s.bypassDocumentValidation === true) {
  452. finalOptions.bypassDocumentValidation = true;
  453. }
  454. // Is the checkKeys option disabled
  455. if (self.s.checkKeys === false) {
  456. finalOptions.checkKeys = false;
  457. }
  458. try {
  459. if (batch.batchType === common.INSERT) {
  460. self.s.topology.insert(
  461. self.s.collection.namespace,
  462. batch.operations,
  463. finalOptions,
  464. resultHandler
  465. );
  466. } else if (batch.batchType === common.UPDATE) {
  467. self.s.topology.update(
  468. self.s.collection.namespace,
  469. batch.operations,
  470. finalOptions,
  471. resultHandler
  472. );
  473. } else if (batch.batchType === common.REMOVE) {
  474. self.s.topology.remove(
  475. self.s.collection.namespace,
  476. batch.operations,
  477. finalOptions,
  478. resultHandler
  479. );
  480. }
  481. } catch (err) {
  482. // Force top level error
  483. err.ok = 0;
  484. // Merge top level error and return
  485. handleCallback(callback, null, mergeBatchResults(false, batch, self.s.bulkResult, err, null));
  486. }
  487. };
  488. /**
  489. * The callback format for results
  490. * @callback OrderedBulkOperation~resultCallback
  491. * @param {MongoError} error An error instance representing the error during the execution.
  492. * @param {BulkWriteResult} result The bulk write result.
  493. */
  494. /**
  495. * Execute the ordered bulk operation
  496. *
  497. * @method
  498. * @param {object} [options=null] Optional settings.
  499. * @param {(number|string)} [options.w=null] The write concern.
  500. * @param {number} [options.wtimeout=null] The write concern timeout.
  501. * @param {boolean} [options.j=false] Specify a journal write concern.
  502. * @param {boolean} [options.fsync=false] Specify a file sync write concern.
  503. * @param {OrderedBulkOperation~resultCallback} [callback] The result callback
  504. * @throws {MongoError}
  505. * @return {Promise} returns Promise if no callback passed
  506. */
  507. OrderedBulkOperation.prototype.execute = function(_writeConcern, options, callback) {
  508. if (typeof options === 'function') (callback = options), (options = {});
  509. options = options || {};
  510. if (this.s.executed) {
  511. var executedError = toError('batch cannot be re-executed');
  512. return typeof callback === 'function'
  513. ? callback(executedError, null)
  514. : this.s.promiseLibrary.reject(executedError);
  515. }
  516. if (typeof _writeConcern === 'function') {
  517. callback = _writeConcern;
  518. } else if (_writeConcern && typeof _writeConcern === 'object') {
  519. this.s.writeConcern = _writeConcern;
  520. }
  521. // If we have current batch
  522. if (this.s.currentBatch) this.s.batches.push(this.s.currentBatch);
  523. // If we have no operations in the bulk raise an error
  524. if (this.s.batches.length === 0) {
  525. var emptyBatchError = toError('Invalid Operation, no operations specified');
  526. return typeof callback === 'function'
  527. ? callback(emptyBatchError, null)
  528. : this.s.promiseLibrary.reject(emptyBatchError);
  529. }
  530. return executeOperation(this.s.topology, executeCommands, [this, options, callback]);
  531. };
  532. define.classMethod('execute', { callback: true, promise: false });
  533. /**
  534. * Returns an unordered batch object
  535. * @ignore
  536. */
  537. var initializeOrderedBulkOp = function(topology, collection, options) {
  538. return new OrderedBulkOperation(topology, collection, options);
  539. };
  540. initializeOrderedBulkOp.OrderedBulkOperation = OrderedBulkOperation;
  541. module.exports = initializeOrderedBulkOp;
  542. module.exports.Bulk = OrderedBulkOperation;