PageRenderTime 51ms CodeModel.GetById 12ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/node_modules/mongoose/node_modules/mongodb-core/lib/wireprotocol/3_2_support.js

https://bitbucket.org/coleman333/smartsite
JavaScript | 590 lines | 330 code | 97 blank | 163 comment | 100 complexity | 4da5b80939d2000669807c932332dd02 MD5 | raw file
  1'use strict';
  2
  3var Query = require('../connection/commands').Query,
  4  retrieveBSON = require('../connection/utils').retrieveBSON,
  5  f = require('util').format,
  6  MongoError = require('../error').MongoError,
  7  MongoNetworkError = require('../error').MongoNetworkError,
  8  getReadPreference = require('./shared').getReadPreference;
  9
 10var BSON = retrieveBSON(),
 11  Long = BSON.Long;
 12
 13var WireProtocol = function(legacyWireProtocol) {
 14  this.legacyWireProtocol = legacyWireProtocol;
 15};
 16
 17//
 18// Execute a write operation
 19var executeWrite = function(pool, bson, type, opsField, ns, ops, options, callback) {
 20  if (ops.length === 0) throw new MongoError('insert must contain at least one document');
 21  if (typeof options === 'function') {
 22    callback = options;
 23    options = {};
 24    options = options || {};
 25  }
 26
 27  // Split the ns up to get db and collection
 28  var p = ns.split('.');
 29  var d = p.shift();
 30  // Options
 31  var ordered = typeof options.ordered === 'boolean' ? options.ordered : true;
 32  var writeConcern = options.writeConcern;
 33
 34  // return skeleton
 35  var writeCommand = {};
 36  writeCommand[type] = p.join('.');
 37  writeCommand[opsField] = ops;
 38  writeCommand.ordered = ordered;
 39
 40  // Did we specify a write concern
 41  if (writeConcern && Object.keys(writeConcern).length > 0) {
 42    writeCommand.writeConcern = writeConcern;
 43  }
 44
 45  // If we have collation passed in
 46  if (options.collation) {
 47    for (var i = 0; i < writeCommand[opsField].length; i++) {
 48      if (!writeCommand[opsField][i].collation) {
 49        writeCommand[opsField][i].collation = options.collation;
 50      }
 51    }
 52  }
 53
 54  // Do we have bypassDocumentValidation set, then enable it on the write command
 55  if (typeof options.bypassDocumentValidation === 'boolean') {
 56    writeCommand.bypassDocumentValidation = options.bypassDocumentValidation;
 57  }
 58
 59  // optionally add a `txnNumber` if retryable writes are being attempted
 60  if (typeof options.txnNumber !== 'undefined') {
 61    writeCommand.txnNumber = options.txnNumber;
 62  }
 63
 64  // Options object
 65  var opts = { command: true };
 66  if (typeof options.session !== 'undefined') opts.session = options.session;
 67  var queryOptions = { checkKeys: false, numberToSkip: 0, numberToReturn: 1 };
 68  if (type === 'insert') queryOptions.checkKeys = false;
 69  if (typeof options.checkKeys === 'boolean') queryOptions.checkKeys = options.checkKeys;
 70
 71  // Ensure we support serialization of functions
 72  if (options.serializeFunctions) queryOptions.serializeFunctions = options.serializeFunctions;
 73  // Do not serialize the undefined fields
 74  if (options.ignoreUndefined) queryOptions.ignoreUndefined = options.ignoreUndefined;
 75
 76  try {
 77    // Create write command
 78    var cmd = new Query(bson, f('%s.$cmd', d), writeCommand, queryOptions);
 79    // Execute command
 80    pool.write(cmd, opts, callback);
 81  } catch (err) {
 82    callback(err);
 83  }
 84};
 85
 86//
 87// Needs to support legacy mass insert as well as ordered/unordered legacy
 88// emulation
 89//
 90WireProtocol.prototype.insert = function(pool, ismaster, ns, bson, ops, options, callback) {
 91  executeWrite(pool, bson, 'insert', 'documents', ns, ops, options, callback);
 92};
 93
 94WireProtocol.prototype.update = function(pool, ismaster, ns, bson, ops, options, callback) {
 95  executeWrite(pool, bson, 'update', 'updates', ns, ops, options, callback);
 96};
 97
 98WireProtocol.prototype.remove = function(pool, ismaster, ns, bson, ops, options, callback) {
 99  executeWrite(pool, bson, 'delete', 'deletes', ns, ops, options, callback);
100};
101
102WireProtocol.prototype.killCursor = function(bson, ns, cursorState, pool, callback) {
103  // Build command namespace
104  var parts = ns.split(/\./);
105  // Command namespace
106  var commandns = f('%s.$cmd', parts.shift());
107  const cursorId = cursorState.cursorId;
108  // Create killCursor command
109  var killcursorCmd = {
110    killCursors: parts.join('.'),
111    cursors: [cursorId]
112  };
113
114  // Build Query object
115  var query = new Query(bson, commandns, killcursorCmd, {
116    numberToSkip: 0,
117    numberToReturn: -1,
118    checkKeys: false,
119    returnFieldSelector: null
120  });
121
122  // Set query flags
123  query.slaveOk = true;
124
125  // Kill cursor callback
126  var killCursorCallback = function(err, result) {
127    if (err) {
128      if (typeof callback !== 'function') return;
129      return callback(err);
130    }
131
132    // Result
133    var r = result.message;
134    // If we have a timed out query or a cursor that was killed
135    if ((r.responseFlags & (1 << 0)) !== 0) {
136      if (typeof callback !== 'function') return;
137      return callback(new MongoNetworkError('cursor killed or timed out'), null);
138    }
139
140    if (!Array.isArray(r.documents) || r.documents.length === 0) {
141      if (typeof callback !== 'function') return;
142      return callback(
143        new MongoError(f('invalid killCursors result returned for cursor id %s', cursorId))
144      );
145    }
146
147    // Return the result
148    if (typeof callback === 'function') {
149      callback(null, r.documents[0]);
150    }
151  };
152
153  const options = { command: true };
154  if (typeof cursorState.session === 'object') {
155    options.session = cursorState.session;
156  }
157
158  // Execute the kill cursor command
159  if (pool && pool.isConnected()) {
160    try {
161      pool.write(query, options, killCursorCallback);
162    } catch (err) {
163      killCursorCallback(err, null);
164    }
165
166    return;
167  }
168
169  // Callback
170  if (typeof callback === 'function') callback(null, null);
171};
172
173WireProtocol.prototype.getMore = function(
174  bson,
175  ns,
176  cursorState,
177  batchSize,
178  raw,
179  connection,
180  options,
181  callback
182) {
183  options = options || {};
184  // Build command namespace
185  var parts = ns.split(/\./);
186  // Command namespace
187  var commandns = f('%s.$cmd', parts.shift());
188
189  // Create getMore command
190  var getMoreCmd = {
191    getMore: cursorState.cursorId,
192    collection: parts.join('.'),
193    batchSize: Math.abs(batchSize)
194  };
195
196  if (cursorState.cmd.tailable && typeof cursorState.cmd.maxAwaitTimeMS === 'number') {
197    getMoreCmd.maxTimeMS = cursorState.cmd.maxAwaitTimeMS;
198  }
199
200  // Build Query object
201  var query = new Query(bson, commandns, getMoreCmd, {
202    numberToSkip: 0,
203    numberToReturn: -1,
204    checkKeys: false,
205    returnFieldSelector: null
206  });
207
208  // Set query flags
209  query.slaveOk = true;
210
211  // Query callback
212  var queryCallback = function(err, result) {
213    if (err) return callback(err);
214    // Get the raw message
215    var r = result.message;
216
217    // If we have a timed out query or a cursor that was killed
218    if ((r.responseFlags & (1 << 0)) !== 0) {
219      return callback(new MongoNetworkError('cursor killed or timed out'), null);
220    }
221
222    // Raw, return all the extracted documents
223    if (raw) {
224      cursorState.documents = r.documents;
225      cursorState.cursorId = r.cursorId;
226      return callback(null, r.documents);
227    }
228
229    // We have an error detected
230    if (r.documents[0].ok === 0) {
231      return callback(new MongoError(r.documents[0]));
232    }
233
234    // Ensure we have a Long valid cursor id
235    var cursorId =
236      typeof r.documents[0].cursor.id === 'number'
237        ? Long.fromNumber(r.documents[0].cursor.id)
238        : r.documents[0].cursor.id;
239
240    // Set all the values
241    cursorState.documents = r.documents[0].cursor.nextBatch;
242    cursorState.cursorId = cursorId;
243
244    // Return the result
245    callback(null, r.documents[0], r.connection);
246  };
247
248  // Query options
249  var queryOptions = { command: true };
250
251  // If we have a raw query decorate the function
252  if (raw) {
253    queryOptions.raw = raw;
254  }
255
256  // Add the result field needed
257  queryOptions.documentsReturnedIn = 'nextBatch';
258
259  // Check if we need to promote longs
260  if (typeof cursorState.promoteLongs === 'boolean') {
261    queryOptions.promoteLongs = cursorState.promoteLongs;
262  }
263
264  if (typeof cursorState.promoteValues === 'boolean') {
265    queryOptions.promoteValues = cursorState.promoteValues;
266  }
267
268  if (typeof cursorState.promoteBuffers === 'boolean') {
269    queryOptions.promoteBuffers = cursorState.promoteBuffers;
270  }
271
272  if (typeof cursorState.session === 'object') {
273    queryOptions.session = cursorState.session;
274  }
275
276  // Write out the getMore command
277  connection.write(query, queryOptions, queryCallback);
278};
279
280WireProtocol.prototype.command = function(bson, ns, cmd, cursorState, topology, options) {
281  options = options || {};
282  // Check if this is a wire protocol command or not
283  var wireProtocolCommand =
284    typeof options.wireProtocolCommand === 'boolean' ? options.wireProtocolCommand : true;
285
286  // Establish type of command
287  if (cmd.find && wireProtocolCommand) {
288    // Create the find command
289    var query = executeFindCommand(bson, ns, cmd, cursorState, topology, options);
290    // Mark the cmd as virtual
291    cmd.virtual = false;
292    // Signal the documents are in the firstBatch value
293    query.documentsReturnedIn = 'firstBatch';
294    // Return the query
295    return query;
296  } else if (cursorState.cursorId != null) {
297    return;
298  } else if (cmd) {
299    return setupCommand(bson, ns, cmd, cursorState, topology, options);
300  } else {
301    throw new MongoError(f('command %s does not return a cursor', JSON.stringify(cmd)));
302  }
303};
304
305// // Command
306// {
307//     find: ns
308//   , query: <object>
309//   , limit: <n>
310//   , fields: <object>
311//   , skip: <n>
312//   , hint: <string>
313//   , explain: <boolean>
314//   , snapshot: <boolean>
315//   , batchSize: <n>
316//   , returnKey: <boolean>
317//   , maxScan: <n>
318//   , min: <n>
319//   , max: <n>
320//   , showDiskLoc: <boolean>
321//   , comment: <string>
322//   , maxTimeMS: <n>
323//   , raw: <boolean>
324//   , readPreference: <ReadPreference>
325//   , tailable: <boolean>
326//   , oplogReplay: <boolean>
327//   , noCursorTimeout: <boolean>
328//   , awaitdata: <boolean>
329//   , exhaust: <boolean>
330//   , partial: <boolean>
331// }
332
333// FIND/GETMORE SPEC
334// {
335//     “find”: <string>,
336//     “filter”: { ... },
337//     “sort”: { ... },
338//     “projection”: { ... },
339//     “hint”: { ... },
340//     “skip”: <int>,
341//     “limit”: <int>,
342//     “batchSize”: <int>,
343//     “singleBatch”: <bool>,
344//     “comment”: <string>,
345//     “maxScan”: <int>,
346//     “maxTimeMS”: <int>,
347//     “max”: { ... },
348//     “min”: { ... },
349//     “returnKey”: <bool>,
350//     “showRecordId”: <bool>,
351//     “snapshot”: <bool>,
352//     “tailable”: <bool>,
353//     “oplogReplay”: <bool>,
354//     “noCursorTimeout”: <bool>,
355//     “awaitData”: <bool>,
356//     “partial”: <bool>,
357//     “$readPreference”: { ... }
358// }
359
360//
361// Execute a find command
362var executeFindCommand = function(bson, ns, cmd, cursorState, topology, options) {
363  // Ensure we have at least some options
364  options = options || {};
365  // Get the readPreference
366  var readPreference = getReadPreference(cmd, options);
367  // Set the optional batchSize
368  cursorState.batchSize = cmd.batchSize || cursorState.batchSize;
369
370  // Build command namespace
371  var parts = ns.split(/\./);
372  // Command namespace
373  var commandns = f('%s.$cmd', parts.shift());
374
375  // Build actual find command
376  var findCmd = {
377    find: parts.join('.')
378  };
379
380  // I we provided a filter
381  if (cmd.query) {
382    // Check if the user is passing in the $query parameter
383    if (cmd.query['$query']) {
384      findCmd.filter = cmd.query['$query'];
385    } else {
386      findCmd.filter = cmd.query;
387    }
388  }
389
390  // Sort value
391  var sortValue = cmd.sort;
392
393  // Handle issue of sort being an Array
394  if (Array.isArray(sortValue)) {
395    var sortObject = {};
396
397    if (sortValue.length > 0 && !Array.isArray(sortValue[0])) {
398      var sortDirection = sortValue[1];
399      // Translate the sort order text
400      if (sortDirection === 'asc') {
401        sortDirection = 1;
402      } else if (sortDirection === 'desc') {
403        sortDirection = -1;
404      }
405
406      // Set the sort order
407      sortObject[sortValue[0]] = sortDirection;
408    } else {
409      for (var i = 0; i < sortValue.length; i++) {
410        sortDirection = sortValue[i][1];
411        // Translate the sort order text
412        if (sortDirection === 'asc') {
413          sortDirection = 1;
414        } else if (sortDirection === 'desc') {
415          sortDirection = -1;
416        }
417
418        // Set the sort order
419        sortObject[sortValue[i][0]] = sortDirection;
420      }
421    }
422
423    sortValue = sortObject;
424  }
425
426  // Add sort to command
427  if (cmd.sort) findCmd.sort = sortValue;
428  // Add a projection to the command
429  if (cmd.fields) findCmd.projection = cmd.fields;
430  // Add a hint to the command
431  if (cmd.hint) findCmd.hint = cmd.hint;
432  // Add a skip
433  if (cmd.skip) findCmd.skip = cmd.skip;
434  // Add a limit
435  if (cmd.limit) findCmd.limit = cmd.limit;
436
437  // Check if we wish to have a singleBatch
438  if (cmd.limit < 0) {
439    findCmd.limit = Math.abs(cmd.limit);
440    findCmd.singleBatch = true;
441  }
442
443  // Add a batchSize
444  if (typeof cmd.batchSize === 'number') {
445    if (cmd.batchSize < 0) {
446      if (cmd.limit !== 0 && Math.abs(cmd.batchSize) < Math.abs(cmd.limit)) {
447        findCmd.limit = Math.abs(cmd.batchSize);
448      }
449
450      findCmd.singleBatch = true;
451    }
452
453    findCmd.batchSize = Math.abs(cmd.batchSize);
454  }
455
456  // If we have comment set
457  if (cmd.comment) findCmd.comment = cmd.comment;
458
459  // If we have maxScan
460  if (cmd.maxScan) findCmd.maxScan = cmd.maxScan;
461
462  // If we have maxTimeMS set
463  if (cmd.maxTimeMS) findCmd.maxTimeMS = cmd.maxTimeMS;
464
465  // If we have min
466  if (cmd.min) findCmd.min = cmd.min;
467
468  // If we have max
469  if (cmd.max) findCmd.max = cmd.max;
470
471  // If we have returnKey set
472  if (cmd.returnKey) findCmd.returnKey = cmd.returnKey;
473
474  // If we have showDiskLoc set
475  if (cmd.showDiskLoc) findCmd.showRecordId = cmd.showDiskLoc;
476
477  // If we have snapshot set
478  if (cmd.snapshot) findCmd.snapshot = cmd.snapshot;
479
480  // If we have tailable set
481  if (cmd.tailable) findCmd.tailable = cmd.tailable;
482
483  // If we have oplogReplay set
484  if (cmd.oplogReplay) findCmd.oplogReplay = cmd.oplogReplay;
485
486  // If we have noCursorTimeout set
487  if (cmd.noCursorTimeout) findCmd.noCursorTimeout = cmd.noCursorTimeout;
488
489  // If we have awaitData set
490  if (cmd.awaitData) findCmd.awaitData = cmd.awaitData;
491  if (cmd.awaitdata) findCmd.awaitData = cmd.awaitdata;
492
493  // If we have partial set
494  if (cmd.partial) findCmd.partial = cmd.partial;
495
496  // If we have collation passed in
497  if (cmd.collation) findCmd.collation = cmd.collation;
498
499  // If we have explain, we need to rewrite the find command
500  // to wrap it in the explain command
501  if (cmd.explain) {
502    findCmd = {
503      explain: findCmd
504    };
505  }
506
507  // Did we provide a readConcern
508  if (cmd.readConcern) findCmd.readConcern = cmd.readConcern;
509
510  // Set up the serialize and ignoreUndefined fields
511  var serializeFunctions =
512    typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
513  var ignoreUndefined =
514    typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
515
516  // We have a Mongos topology, check if we need to add a readPreference
517  if (topology.type === 'mongos' && readPreference && readPreference.preference !== 'primary') {
518    findCmd = {
519      $query: findCmd,
520      $readPreference: readPreference.toJSON()
521    };
522  }
523
524  // Build Query object
525  var query = new Query(bson, commandns, findCmd, {
526    numberToSkip: 0,
527    numberToReturn: 1,
528    checkKeys: false,
529    returnFieldSelector: null,
530    serializeFunctions: serializeFunctions,
531    ignoreUndefined: ignoreUndefined
532  });
533
534  // Set query flags
535  query.slaveOk = readPreference.slaveOk();
536
537  // Return the query
538  return query;
539};
540
541//
542// Set up a command cursor
543var setupCommand = function(bson, ns, cmd, cursorState, topology, options) {
544  // Set empty options object
545  options = options || {};
546  // Get the readPreference
547  var readPreference = getReadPreference(cmd, options);
548
549  // Final query
550  var finalCmd = {};
551  for (var name in cmd) {
552    finalCmd[name] = cmd[name];
553  }
554
555  // Build command namespace
556  var parts = ns.split(/\./);
557
558  // Serialize functions
559  var serializeFunctions =
560    typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
561
562  // Set up the serialize and ignoreUndefined fields
563  var ignoreUndefined =
564    typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : false;
565
566  // We have a Mongos topology, check if we need to add a readPreference
567  if (topology.type === 'mongos' && readPreference && readPreference.preference !== 'primary') {
568    finalCmd = {
569      $query: finalCmd,
570      $readPreference: readPreference.toJSON()
571    };
572  }
573
574  // Build Query object
575  var query = new Query(bson, f('%s.$cmd', parts.shift()), finalCmd, {
576    numberToSkip: 0,
577    numberToReturn: -1,
578    checkKeys: false,
579    serializeFunctions: serializeFunctions,
580    ignoreUndefined: ignoreUndefined
581  });
582
583  // Set query flags
584  query.slaveOk = readPreference.slaveOk();
585
586  // Return the query
587  return query;
588};
589
590module.exports = WireProtocol;