3 var utils = require('../utils'),
4 Long = require('mongodb-core').BSON.Long,
5 Timestamp = require('mongodb-core').BSON.Timestamp;
9 var INVALID_BSON_ERROR = 22;
10 var WRITE_CONCERN_ERROR = 64;
11 var MULTIPLE_ERROR = 65;
20 var writeConcern = function(target, col, options) {
21 var writeConcern = {};
23 // Collection level write concern
24 if(col.writeConcern && col.writeConcern.w != null) writeConcern.w = col.writeConcern.w;
25 if(col.writeConcern && col.writeConcern.j != null) writeConcern.j = col.writeConcern.j;
26 if(col.writeConcern && col.writeConcern.fsync != null) writeConcern.fsync = col.writeConcern.fsync;
27 if(col.writeConcern && col.writeConcern.wtimeout != null) writeConcern.wtimeout = col.writeConcern.wtimeout;
29 // Options level write concern
30 if(options && options.w != null) writeConcern.w = options.w;
31 if(options && options.wtimeout != null) writeConcern.wtimeout = options.wtimeout;
32 if(options && options.j != null) writeConcern.j = options.j;
33 if(options && options.fsync != null) writeConcern.fsync = options.fsync;
35 // Return write concern
40 * Helper function to define properties
43 var defineReadOnlyProperty = function(self, name, value) {
44 Object.defineProperty(self, name, {
53 * Keeps the state of a unordered batch so we can rewrite the results
54 * correctly after command execution
57 var Batch = function(batchType, originalZeroIndex) {
58 this.originalZeroIndex = originalZeroIndex;
59 this.currentIndex = 0;
60 this.originalIndexes = [];
61 this.batchType = batchType;
68 * Wraps a legacy operation so we can correctly rewrite it's error
71 var LegacyOp = function(batchType, operation, index) {
72 this.batchType = batchType;
74 this.operation = operation;
78 * Create a new BulkWriteResult instance (INTERNAL TYPE, do not instantiate directly)
81 * @property {boolean} ok Did bulk operation correctly execute
82 * @property {number} nInserted number of inserted documents
83 * @property {number} nUpdated number of documents updated logically
84 * @property {number} nUpserted Number of upserted documents
85 * @property {number} nModified Number of documents updated physically on disk
86 * @property {number} nRemoved Number of removed documents
87 * @return {BulkWriteResult} a BulkWriteResult instance
89 var BulkWriteResult = function(bulkResult) {
90 defineReadOnlyProperty(this, "ok", bulkResult.ok);
91 defineReadOnlyProperty(this, "nInserted", bulkResult.nInserted);
92 defineReadOnlyProperty(this, "nUpserted", bulkResult.nUpserted);
93 defineReadOnlyProperty(this, "nMatched", bulkResult.nMatched);
94 defineReadOnlyProperty(this, "nModified", bulkResult.nModified);
95 defineReadOnlyProperty(this, "nRemoved", bulkResult.nRemoved);
98 * Return an array of inserted ids
102 this.getInsertedIds = function() {
103 return bulkResult.insertedIds;
107 * Return an array of upserted ids
111 this.getUpsertedIds = function() {
112 return bulkResult.upserted;
116 * Return the upserted id at position x
118 * @param {number} index the number of the upserted id to return, returns undefined if no result for passed in index
121 this.getUpsertedIdAt = function(index) {
122 return bulkResult.upserted[index];
126 * Return raw internal result
130 this.getRawResponse = function() {
135 * Returns true if the bulk operation contains a write error
139 this.hasWriteErrors = function() {
140 return bulkResult.writeErrors.length > 0;
144 * Returns the number of write errors off the bulk operation
148 this.getWriteErrorCount = function() {
149 return bulkResult.writeErrors.length;
153 * Returns a specific write error object
155 * @return {WriteError}
157 this.getWriteErrorAt = function(index) {
158 if(index < bulkResult.writeErrors.length) {
159 return bulkResult.writeErrors[index];
165 * Retrieve all write errors
169 this.getWriteErrors = function() {
170 return bulkResult.writeErrors;
174 * Retrieve lastOp if available
178 this.getLastOp = function() {
179 return bulkResult.lastOp;
183 * Retrieve the write concern error if any
185 * @return {WriteConcernError}
187 this.getWriteConcernError = function() {
188 if(bulkResult.writeConcernErrors.length == 0) {
190 } else if(bulkResult.writeConcernErrors.length == 1) {
192 return bulkResult.writeConcernErrors[0];
195 // Combine the errors
197 for(var i = 0; i < bulkResult.writeConcernErrors.length; i++) {
198 var err = bulkResult.writeConcernErrors[i];
199 errmsg = errmsg + err.errmsg;
201 // TODO: Something better
202 if(i == 0) errmsg = errmsg + " and ";
205 return new WriteConcernError({ errmsg : errmsg, code : WRITE_CONCERN_ERROR });
209 this.toJSON = function() {
213 this.toString = function() {
214 return "BulkWriteResult(" + this.toJSON(bulkResult) + ")";
217 this.isOk = function() {
218 return bulkResult.ok == 1;
223 * Create a new WriteConcernError instance (INTERNAL TYPE, do not instantiate directly)
226 * @property {number} code Write concern error code.
227 * @property {string} errmsg Write concern error message.
228 * @return {WriteConcernError} a WriteConcernError instance
230 var WriteConcernError = function(err) {
231 if(!(this instanceof WriteConcernError)) return new WriteConcernError(err);
234 defineReadOnlyProperty(this, "code", err.code);
235 defineReadOnlyProperty(this, "errmsg", err.errmsg);
237 this.toJSON = function() {
238 return {code: err.code, errmsg: err.errmsg};
241 this.toString = function() {
242 return "WriteConcernError(" + err.errmsg + ")";
247 * Create a new WriteError instance (INTERNAL TYPE, do not instantiate directly)
250 * @property {number} code Write concern error code.
251 * @property {number} index Write concern error original bulk operation index.
252 * @property {string} errmsg Write concern error message.
253 * @return {WriteConcernError} a WriteConcernError instance
255 var WriteError = function(err) {
256 if(!(this instanceof WriteError)) return new WriteError(err);
259 defineReadOnlyProperty(this, "code", err.code);
260 defineReadOnlyProperty(this, "index", err.index);
261 defineReadOnlyProperty(this, "errmsg", err.errmsg);
264 // Define access methods
265 this.getOperation = function() {
269 this.toJSON = function() {
270 return {code: err.code, index: err.index, errmsg: err.errmsg, op: err.op};
273 this.toString = function() {
274 return "WriteError(" + JSON.stringify(this.toJSON()) + ")";
279 * Merges results into shared data structure
282 var mergeBatchResults = function(ordered, batch, bulkResult, err, result) {
283 // If we have an error set the result to be the err object
286 } else if(result && result.result) {
287 result = result.result;
288 } else if(result == null) {
292 // Do we have a top level error stop processing and return
293 if(result.ok == 0 && bulkResult.ok == 1) {
298 , code: result.code || 0
299 , errmsg: result.message
300 , op: batch.operations[0]
303 bulkResult.writeErrors.push(new WriteError(writeError));
305 } else if(result.ok == 0 && bulkResult.ok == 0) {
309 // Deal with opTime if available
310 if(result.opTime || result.lastOp) {
311 var opTime = result.lastOp || result.opTime;
315 // We have a time stamp
316 if(opTime instanceof Timestamp) {
317 if(bulkResult.lastOp == null) {
318 bulkResult.lastOp = opTime;
319 } else if(opTime.greaterThan(bulkResult.lastOp)) {
320 bulkResult.lastOp = opTime;
324 if(bulkResult.lastOp) {
325 lastOpTS = typeof bulkResult.lastOp.ts == 'number'
326 ? Long.fromNumber(bulkResult.lastOp.ts) : bulkResult.lastOp.ts;
327 lastOpT = typeof bulkResult.lastOp.t == 'number'
328 ? Long.fromNumber(bulkResult.lastOp.t) : bulkResult.lastOp.t;
332 var opTimeTS = typeof opTime.ts == 'number'
333 ? Long.fromNumber(opTime.ts) : opTime.ts;
334 var opTimeT = typeof opTime.t == 'number'
335 ? Long.fromNumber(opTime.t) : opTime.t;
337 // Compare the opTime's
338 if(bulkResult.lastOp == null) {
339 bulkResult.lastOp = opTime;
340 } else if(opTimeTS.greaterThan(lastOpTS)) {
341 bulkResult.lastOp = opTime;
342 } else if(opTimeTS.equals(lastOpTS)) {
343 if(opTimeT.greaterThan(lastOpT)) {
344 bulkResult.lastOp = opTime;
350 // If we have an insert Batch type
351 if(batch.batchType == INSERT && result.n) {
352 bulkResult.nInserted = bulkResult.nInserted + result.n;
355 // If we have an insert Batch type
356 if(batch.batchType == REMOVE && result.n) {
357 bulkResult.nRemoved = bulkResult.nRemoved + result.n;
362 // We have an array of upserted values, we need to rewrite the indexes
363 if(Array.isArray(result.upserted)) {
364 nUpserted = result.upserted.length;
366 for(var i = 0; i < result.upserted.length; i++) {
367 bulkResult.upserted.push({
368 index: result.upserted[i].index + batch.originalZeroIndex
369 , _id: result.upserted[i]._id
372 } else if(result.upserted) {
376 bulkResult.upserted.push({
377 index: batch.originalZeroIndex
378 , _id: result.upserted
382 // If we have an update Batch type
383 if(batch.batchType == UPDATE && result.n) {
384 var nModified = result.nModified;
385 bulkResult.nUpserted = bulkResult.nUpserted + nUpserted;
386 bulkResult.nMatched = bulkResult.nMatched + (result.n - nUpserted);
388 if(typeof nModified == 'number') {
389 bulkResult.nModified = bulkResult.nModified + nModified;
391 bulkResult.nModified = null;
395 if(Array.isArray(result.writeErrors)) {
396 for(var i = 0; i < result.writeErrors.length; i++) {
399 index: batch.originalZeroIndex + result.writeErrors[i].index
400 , code: result.writeErrors[i].code
401 , errmsg: result.writeErrors[i].errmsg
402 , op: batch.operations[result.writeErrors[i].index]
405 bulkResult.writeErrors.push(new WriteError(writeError));
409 if(result.writeConcernError) {
410 bulkResult.writeConcernErrors.push(new WriteConcernError(result.writeConcernError));
416 var cloneOptions = function(options) {
418 var keys = Object.keys(options);
419 for(var i = 0; i < keys.length; i++) {
420 clone[keys[i]] = options[keys[i]];
427 exports.BulkWriteResult = BulkWriteResult;
428 exports.WriteError = WriteError;
429 exports.Batch = Batch;
430 exports.LegacyOp = LegacyOp;
431 exports.mergeBatchResults = mergeBatchResults;
432 exports.cloneOptions = cloneOptions;
433 exports.writeConcern = writeConcern;
434 exports.INVALID_BSON_ERROR = INVALID_BSON_ERROR;
435 exports.WRITE_CONCERN_ERROR = WRITE_CONCERN_ERROR;
436 exports.MULTIPLE_ERROR = MULTIPLE_ERROR;
437 exports.UNKNOWN_ERROR = UNKNOWN_ERROR;
438 exports.INSERT = INSERT;
439 exports.UPDATE = UPDATE;
440 exports.REMOVE = REMOVE;