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

https://bitbucket.org/coleman333/smartsite · JavaScript · 465 lines · 271 code · 60 blank · 134 comment · 84 complexity · 56ecd331584bb0287aca61a5bdf1e9da MD5 · raw file

  1. 'use strict';
  2. var Long = require('mongodb-core').BSON.Long,
  3. MongoError = require('mongodb-core').MongoError,
  4. util = require('util');
  5. // Error codes
  6. var UNKNOWN_ERROR = 8;
  7. var INVALID_BSON_ERROR = 22;
  8. var WRITE_CONCERN_ERROR = 64;
  9. var MULTIPLE_ERROR = 65;
  10. // Insert types
  11. var INSERT = 1;
  12. var UPDATE = 2;
  13. var REMOVE = 3;
  14. // Get write concern
  15. var writeConcern = function(target, col, options) {
  16. var writeConcern = {};
  17. // Collection level write concern
  18. if (col.writeConcern && col.writeConcern.w != null) writeConcern.w = col.writeConcern.w;
  19. if (col.writeConcern && col.writeConcern.j != null) writeConcern.j = col.writeConcern.j;
  20. if (col.writeConcern && col.writeConcern.fsync != null)
  21. writeConcern.fsync = col.writeConcern.fsync;
  22. if (col.writeConcern && col.writeConcern.wtimeout != null)
  23. writeConcern.wtimeout = col.writeConcern.wtimeout;
  24. // Options level write concern
  25. if (options && options.w != null) writeConcern.w = options.w;
  26. if (options && options.wtimeout != null) writeConcern.wtimeout = options.wtimeout;
  27. if (options && options.j != null) writeConcern.j = options.j;
  28. if (options && options.fsync != null) writeConcern.fsync = options.fsync;
  29. // Return write concern
  30. return writeConcern;
  31. };
  32. /**
  33. * Helper function to define properties
  34. * @ignore
  35. */
  36. var defineReadOnlyProperty = function(self, name, value) {
  37. Object.defineProperty(self, name, {
  38. enumerable: true,
  39. get: function() {
  40. return value;
  41. }
  42. });
  43. };
  44. /**
  45. * Keeps the state of a unordered batch so we can rewrite the results
  46. * correctly after command execution
  47. * @ignore
  48. */
  49. var Batch = function(batchType, originalZeroIndex) {
  50. this.originalZeroIndex = originalZeroIndex;
  51. this.currentIndex = 0;
  52. this.originalIndexes = [];
  53. this.batchType = batchType;
  54. this.operations = [];
  55. this.size = 0;
  56. this.sizeBytes = 0;
  57. };
  58. /**
  59. * Wraps a legacy operation so we can correctly rewrite it's error
  60. * @ignore
  61. */
  62. var LegacyOp = function(batchType, operation, index) {
  63. this.batchType = batchType;
  64. this.index = index;
  65. this.operation = operation;
  66. };
  67. /**
  68. * Create a new BulkWriteResult instance (INTERNAL TYPE, do not instantiate directly)
  69. *
  70. * @class
  71. * @property {boolean} ok Did bulk operation correctly execute
  72. * @property {number} nInserted number of inserted documents
  73. * @property {number} nUpdated number of documents updated logically
  74. * @property {number} nUpserted Number of upserted documents
  75. * @property {number} nModified Number of documents updated physically on disk
  76. * @property {number} nRemoved Number of removed documents
  77. * @return {BulkWriteResult} a BulkWriteResult instance
  78. */
  79. var BulkWriteResult = function(bulkResult) {
  80. defineReadOnlyProperty(this, 'ok', bulkResult.ok);
  81. defineReadOnlyProperty(this, 'nInserted', bulkResult.nInserted);
  82. defineReadOnlyProperty(this, 'nUpserted', bulkResult.nUpserted);
  83. defineReadOnlyProperty(this, 'nMatched', bulkResult.nMatched);
  84. defineReadOnlyProperty(this, 'nModified', bulkResult.nModified);
  85. defineReadOnlyProperty(this, 'nRemoved', bulkResult.nRemoved);
  86. /**
  87. * Return an array of inserted ids
  88. *
  89. * @return {object[]}
  90. */
  91. this.getInsertedIds = function() {
  92. return bulkResult.insertedIds;
  93. };
  94. /**
  95. * Return an array of upserted ids
  96. *
  97. * @return {object[]}
  98. */
  99. this.getUpsertedIds = function() {
  100. return bulkResult.upserted;
  101. };
  102. /**
  103. * Return the upserted id at position x
  104. *
  105. * @param {number} index the number of the upserted id to return, returns undefined if no result for passed in index
  106. * @return {object}
  107. */
  108. this.getUpsertedIdAt = function(index) {
  109. return bulkResult.upserted[index];
  110. };
  111. /**
  112. * Return raw internal result
  113. *
  114. * @return {object}
  115. */
  116. this.getRawResponse = function() {
  117. return bulkResult;
  118. };
  119. /**
  120. * Returns true if the bulk operation contains a write error
  121. *
  122. * @return {boolean}
  123. */
  124. this.hasWriteErrors = function() {
  125. return bulkResult.writeErrors.length > 0;
  126. };
  127. /**
  128. * Returns the number of write errors off the bulk operation
  129. *
  130. * @return {number}
  131. */
  132. this.getWriteErrorCount = function() {
  133. return bulkResult.writeErrors.length;
  134. };
  135. /**
  136. * Returns a specific write error object
  137. *
  138. * @param {number} index of the write error to return, returns null if there is no result for passed in index
  139. * @return {WriteError}
  140. */
  141. this.getWriteErrorAt = function(index) {
  142. if (index < bulkResult.writeErrors.length) {
  143. return bulkResult.writeErrors[index];
  144. }
  145. return null;
  146. };
  147. /**
  148. * Retrieve all write errors
  149. *
  150. * @return {object[]}
  151. */
  152. this.getWriteErrors = function() {
  153. return bulkResult.writeErrors;
  154. };
  155. /**
  156. * Retrieve lastOp if available
  157. *
  158. * @return {object}
  159. */
  160. this.getLastOp = function() {
  161. return bulkResult.lastOp;
  162. };
  163. /**
  164. * Retrieve the write concern error if any
  165. *
  166. * @return {WriteConcernError}
  167. */
  168. this.getWriteConcernError = function() {
  169. if (bulkResult.writeConcernErrors.length === 0) {
  170. return null;
  171. } else if (bulkResult.writeConcernErrors.length === 1) {
  172. // Return the error
  173. return bulkResult.writeConcernErrors[0];
  174. } else {
  175. // Combine the errors
  176. var errmsg = '';
  177. for (var i = 0; i < bulkResult.writeConcernErrors.length; i++) {
  178. var err = bulkResult.writeConcernErrors[i];
  179. errmsg = errmsg + err.errmsg;
  180. // TODO: Something better
  181. if (i === 0) errmsg = errmsg + ' and ';
  182. }
  183. return new WriteConcernError({ errmsg: errmsg, code: WRITE_CONCERN_ERROR });
  184. }
  185. };
  186. this.toJSON = function() {
  187. return bulkResult;
  188. };
  189. this.toString = function() {
  190. return 'BulkWriteResult(' + this.toJSON(bulkResult) + ')';
  191. };
  192. this.isOk = function() {
  193. return bulkResult.ok === 1;
  194. };
  195. };
  196. /**
  197. * Create a new WriteConcernError instance (INTERNAL TYPE, do not instantiate directly)
  198. *
  199. * @class
  200. * @property {number} code Write concern error code.
  201. * @property {string} errmsg Write concern error message.
  202. * @return {WriteConcernError} a WriteConcernError instance
  203. */
  204. var WriteConcernError = function(err) {
  205. if (!(this instanceof WriteConcernError)) return new WriteConcernError(err);
  206. // Define properties
  207. defineReadOnlyProperty(this, 'code', err.code);
  208. defineReadOnlyProperty(this, 'errmsg', err.errmsg);
  209. this.toJSON = function() {
  210. return { code: err.code, errmsg: err.errmsg };
  211. };
  212. this.toString = function() {
  213. return 'WriteConcernError(' + err.errmsg + ')';
  214. };
  215. };
  216. /**
  217. * Create a new WriteError instance (INTERNAL TYPE, do not instantiate directly)
  218. *
  219. * @class
  220. * @property {number} code Write concern error code.
  221. * @property {number} index Write concern error original bulk operation index.
  222. * @property {string} errmsg Write concern error message.
  223. * @return {WriteConcernError} a WriteConcernError instance
  224. */
  225. var WriteError = function(err) {
  226. if (!(this instanceof WriteError)) return new WriteError(err);
  227. // Define properties
  228. defineReadOnlyProperty(this, 'code', err.code);
  229. defineReadOnlyProperty(this, 'index', err.index);
  230. defineReadOnlyProperty(this, 'errmsg', err.errmsg);
  231. //
  232. // Define access methods
  233. this.getOperation = function() {
  234. return err.op;
  235. };
  236. this.toJSON = function() {
  237. return { code: err.code, index: err.index, errmsg: err.errmsg, op: err.op };
  238. };
  239. this.toString = function() {
  240. return 'WriteError(' + JSON.stringify(this.toJSON()) + ')';
  241. };
  242. };
  243. /**
  244. * Merges results into shared data structure
  245. * @ignore
  246. */
  247. var mergeBatchResults = function(ordered, batch, bulkResult, err, result) {
  248. // If we have an error set the result to be the err object
  249. if (err) {
  250. result = err;
  251. } else if (result && result.result) {
  252. result = result.result;
  253. } else if (result == null) {
  254. return;
  255. }
  256. // Do we have a top level error stop processing and return
  257. if (result.ok === 0 && bulkResult.ok === 1) {
  258. bulkResult.ok = 0;
  259. var writeError = {
  260. index: 0,
  261. code: result.code || 0,
  262. errmsg: result.message,
  263. op: batch.operations[0]
  264. };
  265. bulkResult.writeErrors.push(new WriteError(writeError));
  266. return;
  267. } else if (result.ok === 0 && bulkResult.ok === 0) {
  268. return;
  269. }
  270. // Deal with opTime if available
  271. if (result.opTime || result.lastOp) {
  272. var opTime = result.lastOp || result.opTime;
  273. var lastOpTS = null;
  274. var lastOpT = null;
  275. // We have a time stamp
  276. if (opTime && opTime._bsontype === 'Timestamp') {
  277. if (bulkResult.lastOp == null) {
  278. bulkResult.lastOp = opTime;
  279. } else if (opTime.greaterThan(bulkResult.lastOp)) {
  280. bulkResult.lastOp = opTime;
  281. }
  282. } else {
  283. // Existing TS
  284. if (bulkResult.lastOp) {
  285. lastOpTS =
  286. typeof bulkResult.lastOp.ts === 'number'
  287. ? Long.fromNumber(bulkResult.lastOp.ts)
  288. : bulkResult.lastOp.ts;
  289. lastOpT =
  290. typeof bulkResult.lastOp.t === 'number'
  291. ? Long.fromNumber(bulkResult.lastOp.t)
  292. : bulkResult.lastOp.t;
  293. }
  294. // Current OpTime TS
  295. var opTimeTS = typeof opTime.ts === 'number' ? Long.fromNumber(opTime.ts) : opTime.ts;
  296. var opTimeT = typeof opTime.t === 'number' ? Long.fromNumber(opTime.t) : opTime.t;
  297. // Compare the opTime's
  298. if (bulkResult.lastOp == null) {
  299. bulkResult.lastOp = opTime;
  300. } else if (opTimeTS.greaterThan(lastOpTS)) {
  301. bulkResult.lastOp = opTime;
  302. } else if (opTimeTS.equals(lastOpTS)) {
  303. if (opTimeT.greaterThan(lastOpT)) {
  304. bulkResult.lastOp = opTime;
  305. }
  306. }
  307. }
  308. }
  309. // If we have an insert Batch type
  310. if (batch.batchType === INSERT && result.n) {
  311. bulkResult.nInserted = bulkResult.nInserted + result.n;
  312. }
  313. // If we have an insert Batch type
  314. if (batch.batchType === REMOVE && result.n) {
  315. bulkResult.nRemoved = bulkResult.nRemoved + result.n;
  316. }
  317. var nUpserted = 0;
  318. // We have an array of upserted values, we need to rewrite the indexes
  319. if (Array.isArray(result.upserted)) {
  320. nUpserted = result.upserted.length;
  321. for (var i = 0; i < result.upserted.length; i++) {
  322. bulkResult.upserted.push({
  323. index: result.upserted[i].index + batch.originalZeroIndex,
  324. _id: result.upserted[i]._id
  325. });
  326. }
  327. } else if (result.upserted) {
  328. nUpserted = 1;
  329. bulkResult.upserted.push({
  330. index: batch.originalZeroIndex,
  331. _id: result.upserted
  332. });
  333. }
  334. // If we have an update Batch type
  335. if (batch.batchType === UPDATE && result.n) {
  336. var nModified = result.nModified;
  337. bulkResult.nUpserted = bulkResult.nUpserted + nUpserted;
  338. bulkResult.nMatched = bulkResult.nMatched + (result.n - nUpserted);
  339. if (typeof nModified === 'number') {
  340. bulkResult.nModified = bulkResult.nModified + nModified;
  341. } else {
  342. bulkResult.nModified = null;
  343. }
  344. }
  345. if (Array.isArray(result.writeErrors)) {
  346. for (i = 0; i < result.writeErrors.length; i++) {
  347. writeError = {
  348. index: batch.originalZeroIndex + result.writeErrors[i].index,
  349. code: result.writeErrors[i].code,
  350. errmsg: result.writeErrors[i].errmsg,
  351. op: batch.operations[result.writeErrors[i].index]
  352. };
  353. bulkResult.writeErrors.push(new WriteError(writeError));
  354. }
  355. }
  356. if (result.writeConcernError) {
  357. bulkResult.writeConcernErrors.push(new WriteConcernError(result.writeConcernError));
  358. }
  359. };
  360. //
  361. // Clone the options
  362. var cloneOptions = function(options) {
  363. var clone = {};
  364. var keys = Object.keys(options);
  365. for (var i = 0; i < keys.length; i++) {
  366. clone[keys[i]] = options[keys[i]];
  367. }
  368. return clone;
  369. };
  370. /**
  371. * Creates a new BulkWriteError
  372. *
  373. * @class
  374. * @param {Error|string|object} message The error message
  375. * @param {BulkWriteResult} result The result of the bulk write operation
  376. * @return {BulkWriteError} A BulkWriteError instance
  377. * @extends {MongoError}
  378. */
  379. const BulkWriteError = function(error, result) {
  380. var message = error.err || error.errmsg || error.errMessage || error;
  381. MongoError.call(this, message);
  382. var keys = typeof error === 'object' ? Object.keys(error) : [];
  383. for (var i = 0; i < keys.length; i++) {
  384. this[keys[i]] = error[keys[i]];
  385. }
  386. this.name = 'BulkWriteError';
  387. this.result = result;
  388. };
  389. util.inherits(BulkWriteError, MongoError);
  390. // Exports symbols
  391. exports.BulkWriteError = BulkWriteError;
  392. exports.BulkWriteResult = BulkWriteResult;
  393. exports.WriteError = WriteError;
  394. exports.Batch = Batch;
  395. exports.LegacyOp = LegacyOp;
  396. exports.mergeBatchResults = mergeBatchResults;
  397. exports.cloneOptions = cloneOptions;
  398. exports.writeConcern = writeConcern;
  399. exports.INVALID_BSON_ERROR = INVALID_BSON_ERROR;
  400. exports.WRITE_CONCERN_ERROR = WRITE_CONCERN_ERROR;
  401. exports.MULTIPLE_ERROR = MULTIPLE_ERROR;
  402. exports.UNKNOWN_ERROR = UNKNOWN_ERROR;
  403. exports.INSERT = INSERT;
  404. exports.UPDATE = UPDATE;
  405. exports.REMOVE = REMOVE;