common.js 12 KB

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