Example #1
0
var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
  var self = this;
  var state = Cursor.INIT;
  var streamOptions = {};

  // Tailable cursor options
  var numberOfRetries = options.numberOfRetries || 5;
  var tailableRetryInterval = options.tailableRetryInterval || 500;
  var currentNumberOfRetries = numberOfRetries;

  // Get the promiseLibrary
  var promiseLibrary = options.promiseLibrary;

  // No promise library selected fall back
  if(!promiseLibrary) {
    promiseLibrary = typeof global.Promise == 'function' ?
      global.Promise : require('es6-promise').Promise;
  }

  // Set up
  Readable.call(this, {objectMode: true});

  // Internal cursor state
  this.s = {
    // Tailable cursor options
      numberOfRetries: numberOfRetries
    , tailableRetryInterval: tailableRetryInterval
    , currentNumberOfRetries: currentNumberOfRetries
    // State
    , state: state
    // Stream options
    , streamOptions: streamOptions
    // BSON
    , bson: bson
    // Namespace
    , ns: ns
    // Command
    , cmd: cmd
    // Options
    , options: options
    // Topology
    , topology: topology
    // Topology options
    , topologyOptions: topologyOptions
    // Promise library
    , promiseLibrary: promiseLibrary
    // Current doc
    , currentDoc: null
  }

  // Translate correctly
  if(self.s.options.noCursorTimeout == true) {
    self.addCursorFlag('noCursorTimeout', true);
  }

  // Set the sort value
  this.sortValue = self.s.cmd.sort;
}
Example #2
0
var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
  var self = this;
  var state = Cursor.INIT;
  var streamOptions = {};

  // Tailable cursor options
  var numberOfRetries = options.numberOfRetries || 5;
  var tailableRetryInterval = options.tailableRetryInterval || 500;
  var currentNumberOfRetries = numberOfRetries;
  // MaxTimeMS
  var maxTimeMS = null;

  // Set up
  Readable.call(this, {objectMode: true});

  // Internal cursor state
  this.s = {
    // MaxTimeMS
      maxTimeMS: null
    // Tailable cursor options
    , numberOfRetries: numberOfRetries
    , tailableRetryInterval: tailableRetryInterval
    , currentNumberOfRetries: currentNumberOfRetries
    // State
    , state: state
    // Stream options
    , streamOptions: streamOptions
    // BSON
    , bson: bson
    // Namespace
    , ns: ns
    // Command
    , cmd: cmd
    // Options
    , options: options
    // Topology
    , topology: topology
    // Topology options
    , topologyOptions: topologyOptions
  }

  // Legacy fields
  this.timeout = self.s.options.noCursorTimeout == true;
  this.sortValue = self.s.cmd.sort;
  this.readPreference = self.s.options.readPreference;
}
/**
 * Creates a new Cursor instance (INTERNAL TYPE, do not instantiate directly)
 * @class Cursor
 * @extends external:CoreCursor
 * @extends external:Readable
 * @property {string} sortValue Cursor query sort setting.
 * @property {boolean} timeout Is Cursor able to time out.
 * @property {ReadPreference} readPreference Get cursor ReadPreference.
 * @fires Cursor#data
 * @fires Cursor#end
 * @fires Cursor#close
 * @fires Cursor#readable
 * @return {Cursor} a Cursor instance.
 * @example
 * Cursor cursor options.
 *
 * collection.find({}).project({a:1})                             // Create a projection of field a
 * collection.find({}).skip(1).limit(10)                          // Skip 1 and limit 10
 * collection.find({}).batchSize(5)                               // Set batchSize on cursor to 5
 * collection.find({}).filter({a:1})                              // Set query on the cursor
 * collection.find({}).comment('add a comment')                   // Add a comment to the query, allowing to correlate queries
 * collection.find({}).addCursorFlag('tailable', true)            // Set cursor as tailable
 * collection.find({}).addCursorFlag('oplogReplay', true)         // Set cursor as oplogReplay
 * collection.find({}).addCursorFlag('noCursorTimeout', true)     // Set cursor as noCursorTimeout
 * collection.find({}).addCursorFlag('awaitData', true)           // Set cursor as awaitData
 * collection.find({}).addCursorFlag('partial', true)             // Set cursor as partial
 * collection.find({}).addQueryModifier('$orderby', {a:1})        // Set $orderby {a:1}
 * collection.find({}).max(10)                                    // Set the cursor max
 * collection.find({}).maxTimeMS(1000)                            // Set the cursor maxTimeMS
 * collection.find({}).min(100)                                   // Set the cursor min
 * collection.find({}).returnKey(true)                            // Set the cursor returnKey
 * collection.find({}).setReadPreference(ReadPreference.PRIMARY)  // Set the cursor readPreference
 * collection.find({}).showRecordId(true)                         // Set the cursor showRecordId
 * collection.find({}).sort([['a', 1]])                           // Sets the sort order of the cursor query
 * collection.find({}).hint('a_1')                                // Set the cursor hint
 *
 * All options are chainable, so one can do the following.
 *
 * collection.find({}).maxTimeMS(1000).maxScan(100).skip(1).toArray(..)
 */
function Cursor(bson, ns, cmd, options, topology, topologyOptions) {
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
  const state = Cursor.INIT;
  const streamOptions = {};

  // Tailable cursor options
  const numberOfRetries = options.numberOfRetries || 5;
  const tailableRetryInterval = options.tailableRetryInterval || 500;
  const currentNumberOfRetries = numberOfRetries;

  // Get the promiseLibrary
  const promiseLibrary = options.promiseLibrary || Promise;

  // Set up
  Readable.call(this, { objectMode: true });

  // Internal cursor state
  this.s = {
    // Tailable cursor options
    numberOfRetries: numberOfRetries,
    tailableRetryInterval: tailableRetryInterval,
    currentNumberOfRetries: currentNumberOfRetries,
    // State
    state: state,
    // Stream options
    streamOptions: streamOptions,
    // BSON
    bson: bson,
    // Namespace
    ns: ns,
    // Command
    cmd: cmd,
    // Options
    options: options,
    // Topology
    topology: topology,
    // Topology options
    topologyOptions: topologyOptions,
    // Promise library
    promiseLibrary: promiseLibrary,
    // Current doc
    currentDoc: null,
    // explicitlyIgnoreSession
    explicitlyIgnoreSession: options.explicitlyIgnoreSession
  };

  // Optional ClientSession
  if (!options.explicitlyIgnoreSession && options.session) {
    this.s.session = options.session;
  }

  // Translate correctly
  if (this.s.options.noCursorTimeout === true) {
    this.addCursorFlag('noCursorTimeout', true);
  }

  // Set the sort value
  this.sortValue = this.s.cmd.sort;

  // Get the batchSize
  const batchSize =
    cmd.cursor && cmd.cursor.batchSize
      ? cmd.cursor && cmd.cursor.batchSize
      : options.cursor && options.cursor.batchSize
        ? options.cursor.batchSize
        : 1000;

  // Set the batchSize
  this.setCursorBatchSize(batchSize);
}
Example #4
0
var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
  CoreCursor.apply(this, Array.prototype.slice.call(arguments, 0));
  var self = this;
  var state = Cursor.INIT;
  var streamOptions = {};

  // Tailable cursor options
  var numberOfRetries = options.numberOfRetries || 5;
  var tailableRetryInterval = options.tailableRetryInterval || 500;
  var currentNumberOfRetries = numberOfRetries;
  // MaxTimeMS
  var maxTimeMS = null;

  // Set up
  Readable.call(this, {objectMode: true});

  // Add a read Only property
  Object.defineProperty(this, 'sortValue', {
    enumerable:true,
    get: function() { return cmd.sort; }
  });

  // Add a read Only property
  Object.defineProperty(this, 'timeout', {
    enumerable:true,
    get: function() { return options.noCursorTimeout == true; }
  });

  // Get the read preferences
  Object.defineProperty(this, 'readPreference', {
    enumerable:true,
    get: function() { return options.readPreference; }
  });

  /**
   * Set the cursor query
   * @method
   * @param {object} filter The filter object used for the cursor.
   * @return {Cursor}
   */
  this.filter = function(filter) {
    if(state == Cursor.CLOSED || state == Cursor.OPEN || self.isDead()) throw new MongoError("Cursor is closed");
    cmd.query = filter;
    return this;
  }

  // Flags allowed for cursor
  var flags = ['tailable', 'oplogReplay', 'noCursorTimeout', 'awaitData', 'exhaust', 'partial'];

  /**
   * Add a cursor flag to the cursor
   * @method
   * @param {string} flag The flag to set, must be one of following ['tailable', 'oplogReplay', 'noCursorTimeout', 'awaitData', 'exhaust', 'partial'].
   * @param {boolean} value The flag boolean value.
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.addCursorFlag = function(flag, value) {
    if(state == Cursor.CLOSED || state == Cursor.OPEN || self.isDead()) throw new MongoError("Cursor is closed");
    if(flags.indexOf(flag) == -1) throw new MongoError(f("flag % not a supported flag %s", flag, flags));
    if(typeof value != 'boolean') throw new MongoError(f("flag % must be a boolean value", flag));
    options[flag] = value;
    return this;
  }

  /**
   * Add a query modifier to the cursor query
   * @method
   * @param {string} name The query modifier (must start with $, such as $orderby etc)
   * @param {boolean} value The flag boolean value.
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.addQueryModifier = function(name, value) {
    if(state == Cursor.CLOSED || state == Cursor.OPEN || self.isDead()) throw new MongoError("Cursor is closed");
    if(name[0] != '$') throw new MongoError(f("%s is not a valid query modifier"));
    // Strip of the $
    var field = name.substr(1);
    // Set on the command
    cmd[field] = value;
    // Deal with the special case for sort
    if(field == 'orderby') cmd.sort = cmd[field];
    return this;
  }

  /**
   * Add a comment to the cursor query allowing for tracking the comment in the log.
   * @method
   * @param {string} value The comment attached to this query.
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.comment = function(value) {
    if(state == Cursor.CLOSED || state == Cursor.OPEN || self.isDead()) throw new MongoError("Cursor is closed");
    cmd.comment = value;
    return this;
  }

  /**
   * Set a maxTimeMS on the cursor query, allowing for hard timeout limits on queries (Only supported on MongoDB 2.6 or higher)
   * @method
   * @param {number} value Number of milliseconds to wait before aborting the query.
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.maxTimeMS = function(value) {
    if(typeof value != 'number') throw new MongoError("maxTimeMS must be a number");
    if(state == Cursor.CLOSED || state == Cursor.OPEN || self.isDead()) throw new MongoError("Cursor is closed");
    maxTimeMS = value;
    cmd.maxTimeMS = value;
    return self;
  }

  this.maxTimeMs = this.maxTimeMS;

  /**
   * Sets a field projection for the query.
   * @method
   * @param {object} value The field projection object.
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.project = function(value) {
    if(state == Cursor.CLOSED || state == Cursor.OPEN || self.isDead()) throw new MongoError("Cursor is closed");
    cmd.fields = value;
    return this;
  }

  /**
   * Sets the sort order of the cursor query.
   * @method
   * @param {(string|array|object)} keyOrList The key or keys set for the sort.
   * @param {number} [direction] The direction of the sorting (1 or -1).
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.sort = function(keyOrList, direction) {
    if(options.tailable) throw new MongoError("Tailable cursor doesn't support sorting");
    if(state == Cursor.CLOSED || state == Cursor.OPEN || self.isDead()) throw new MongoError("Cursor is closed");
    var order = keyOrList;

    if(direction != null) {
      order = [[keyOrList, direction]];
    }

    cmd.sort = order;
    return this;
  }

  /**
   * Set the batch size for the cursor.
   * @method
   * @param {number} value The batchSize for the cursor.
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.batchSize = function(value) {
    if(options.tailable) throw new MongoError("Tailable cursor doesn't support limit");
    if(state == Cursor.CLOSED || self.isDead()) throw new MongoError("Cursor is closed");
    if(typeof value != 'number') throw new MongoError("batchSize requires an integer");
    cmd.batchSize = value;
    this.cursorBatchSize = value;
    return self;
  }

  /**
   * Set the limit for the cursor.
   * @method
   * @param {number} value The limit for the cursor query.
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.limit = function(value) {
    if(options.tailable) throw new MongoError("Tailable cursor doesn't support limit");
    if(state == Cursor.OPEN || state == Cursor.CLOSED || self.isDead()) throw new MongoError("Cursor is closed");
    if(typeof value != 'number') throw new MongoError("limit requires an integer");
    cmd.limit = value;
    this.cursorLimit = value;
    return self;
  }

  /**
   * Set the skip for the cursor.
   * @method
   * @param {number} value The skip for the cursor query.
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.skip = function(value) {
    if(options.tailable) throw new MongoError("Tailable cursor doesn't support skip");
    if(state == Cursor.OPEN || state == Cursor.CLOSED || self.isDead()) throw new MongoError("Cursor is closed");
    if(typeof value != 'number') throw new MongoError("skip requires an integer");
    cmd.skip = value;
    this.cursorSkip = value;
    return self;
  }

  /**
   * The callback format for results
   * @callback Cursor~resultCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   * @param {(object|null)} result The result object if the command was executed successfully.
   */

  /**
   * Get the next available document from the cursor, returns null if no more documents are available.
   * @function external:CoreCursor#next
   * @param {Cursor~resultCallback} callback The result callback.
   * @throws {MongoError}
   * @return {null}
   */

  /**
   * Clone the cursor
   * @function external:CoreCursor#clone
   * @return {Cursor}
   */  

  /**
   * Resets the cursor
   * @function external:CoreCursor#rewind
   * @return {null}
   */  

  /**
   * Get the next available document from the cursor, returns null if no more documents are available.
   * @method
   * @param {Cursor~resultCallback} callback The result callback.
   * @throws {MongoError}
   * @deprecated
   * @return {null}
   */
  this.nextObject = function(callback) {
    if(state == Cursor.CLOSED || self.isDead()) return handleCallback(callback, new MongoError("Cursor is closed"));
    if(state == Cursor.INIT && cmd.sort) {
      try {
        cmd.sort = formattedOrderClause(cmd.sort);
      } catch(err) {
        return handleCallback(callback, err);
      }
    }
    
    // Get the next object
    self.next(function(err, doc) {
      if(err && err.tailable && currentNumberOfRetries == 0) return callback(err);
      if(err && err.tailable && currentNumberOfRetries > 0) {
        currentNumberOfRetries = currentNumberOfRetries - 1;
        return setTimeout(function() {
          self.nextObject(callback);
        }, tailableRetryInterval);
      }

      state = Cursor.OPEN;
      if(err) return handleCallback(callback, err);
      handleCallback(callback, null, doc);
    });
  }

  // Trampoline emptying the number of retrieved items
  // without incurring a nextTick operation
  var loop = function(self, callback) {
    // No more items we are done
    if(self.bufferedCount() == 0) return;
    // Get the next document
    self.next(callback);
    // Loop
    return loop;
  }

  /**
   * Iterates over all the documents for this cursor. As with **{cursor.toArray}**,
   * not all of the elements will be iterated if this cursor had been previouly accessed.
   * In that case, **{cursor.rewind}** can be used to reset the cursor. However, unlike
   * **{cursor.toArray}**, the cursor will only hold a maximum of batch size elements
   * at any given time if batch size is specified. Otherwise, the caller is responsible
   * for making sure that the entire result can fit the memory.
   * @method
   * @deprecated
   * @param {Cursor~resultCallback} callback The result callback.
   * @throws {MongoError}
   * @return {null}
   */
  this.each = function(callback) {
    if(!callback) throw new MongoError('callback is mandatory');
    if(state == Cursor.CLOSED || self.isDead()) return handleCallback(callback, new MongoError("Cursor is closed"), null);
    if(state == Cursor.INIT) state = Cursor.OPEN;
    // Trampoline all the entries
    if(self.bufferedCount() > 0) {
      while(fn = loop(self, callback)) fn(self, callback);
      self.each(callback);
    } else {
      self.next(function(err, item) {
        if(err) return handleCallback(callback, err);
        if(item == null) return handleCallback(callback, null, null);
        if(handleCallback(callback, null, item) == false) return;
        self.each(callback);
      })
    }
  };

  /**
   * The callback format for the forEach iterator method
   * @callback Cursor~iteratorCallback
   * @param {Object} doc An emitted document for the iterator
   */

  /**
   * The callback error format for the forEach iterator method
   * @callback Cursor~endCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   */

  /**
   * Iterates over all the documents for this cursor using the iterator, callback pattern.
   * @method
   * @param {Cursor~iteratorCallback} iterator The iteration callback.
   * @param {Cursor~endCallback} callback The end callback.
   * @throws {MongoError}
   * @return {null}
   */
  this.forEach = function(iterator, callback) {
    this.each(function(err, doc){
      if(err) callback(err);
      else if(doc) iterator(doc);
      else callback(null);
    });
  }

  /**
   * Set the ReadPreference for the cursor.
   * @method
   * @param {(string|ReadPreference)} readPreference The new read preference for the cursor.
   * @throws {MongoError}
   * @return {Cursor}
   */
  this.setReadPreference = function(r) {
    if(state != Cursor.INIT) throw new MongoError('cannot change cursor readPreference after cursor has been accessed');
    if(r instanceof ReadPreference) {
      options.readPreference = new CoreReadPreference(r.mode, r.tags);
    } else {
      options.readPreference = new CoreReadPreference(r);
    }

    return this;
  }  

  /**
   * The callback format for results
   * @callback Cursor~toArrayResultCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   * @param {object[]} documents All the documents the satisfy the cursor.
   */

  /**
   * Returns an array of documents. The caller is responsible for making sure that there
   * is enough memory to store the results. Note that the array only contain partial
   * results when this cursor had been previouly accessed. In that case,
   * cursor.rewind() can be used to reset the cursor.
   * @method
   * @param {Cursor~toArrayResultCallback} callback The result callback.
   * @throws {MongoError}
   * @return {null}
   */
  this.toArray = function(callback) {
    if(!callback) throw new MongoError('callback is mandatory');
    if(options.tailable) return handleCallback(callback, new MongoError("Tailable cursor cannot be converted to array"), null);
    var items = [];
    
    // Reset cursor
    this.rewind();

    // Fetch all the documents
    var fetchDocs = function() {
      self.next(function(err, doc) {
        if(err) return handleCallback(callback, err);
        if(doc == null) {
          state = Cursor.CLOSED;
          return handleCallback(callback, null, items);
        }

        // Add doc to items
        items.push(doc)
        // Get all buffered objects
        if(self.bufferedCount() > 0) {
          items = items.concat(self.readBufferedDocuments(self.bufferedCount()));
        }

        // Attempt a fetch
        fetchDocs();
      })      
    }

    fetchDocs();
  }

  /**
   * The callback format for results
   * @callback Cursor~countResultCallback
   * @param {MongoError} error An error instance representing the error during the execution.
   * @param {number} count The count of documents.
   */

  /**
   * Get the count of documents for this cursor
   * @method
   * @param {boolean} applySkipLimit Should the count command apply limit and skip settings on the cursor or in the passed in options.
   * @param {(string|ReadPreference)} readPreference The new read preference for the cursor.
   * @param {object} [options=null] Optional settings.
   * @param {number} [options.skip=null] The number of documents to skip.
   * @param {number} [options.limit=null] The maximum amounts to count before aborting.
   * @param {number} [options.maxTimeMS=null] Number of miliseconds to wait before aborting the query.
   * @param {string} [options.hint=null] An index name hint for the query.
   * @param {Cursor~countResultCallback} callback The result callback.
   * @return {null}
   */
  this.count = function(applySkipLimit, opts, callback) {
    if(typeof opts == 'function') callback = opts, opts = {};
    opts = opts || {};
    if(cmd.query == null) callback(new MongoError("count can only be used with find command"));
    if(typeof applySkipLimit == 'function') {
      callback = applySkipLimit;
      applySkipLimit = true;
    }

    var opts = {};
    if(applySkipLimit) {
      if(typeof this.cursorSkip == 'number') opts.skip = this.cursorSkip;
      if(typeof this.cursorLimit == 'number') opts.limit = this.cursorLimit;    
    }

    // Command
    var command = {
      'count': ns.split('.').pop(), 'query': cmd.query
    }

    // If maxTimeMS set
    if(typeof maxTimeMS == 'number') {
      command.maxTimeMS = maxTimeMS;
    }

    // Get a server
    var server = topology.getServer(opts);
    // Get a connection
    var connection = topology.getConnection(opts);
    // Get the callbacks
    var callbacks = server.getCallbacks();

    // Merge in any options
    if(opts.skip) command.skip = opts.skip;
    if(opts.limit) command.limit = opts.limit;
    if(options.hint) command.hint = options.hint;

    // Build Query object
    var query = new Query(bson, f("%s.$cmd", ns.split('.').shift()), command, {
        numberToSkip: 0, numberToReturn: -1
      , checkKeys: false
    });

    // Set up callback
    callbacks.once(query.requestId, function(err, result) {
      if(err) return handleCallback(callback, err);
      if(result.documents.length == 1 
        && (result.documents[0].errmsg
        || result.documents[0].err
        || result.documents[0]['$err'])) return callback(MongoError.create(result.documents[0]));
      handleCallback(callback, null, result.documents[0].n);
    });

    // Write the initial command out
    connection.write(query);
  };

  /**
   * Close the console, sending a KillCursor command and emitting close.
   * @method
   * @param {Cursor~resultCallback} [callback] The result callback.
   * @return {null}
   */
  this.close = function(callback) {
    state = Cursor.CLOSED;
    // Kill the cursor
    this.kill();
    // Emit the close event for the cursor
    this.emit('close');      
    // Callback if provided
    if(callback) return handleCallback(callback, null, self);  
  }

  /**
   * Is the cursor closed
   * @method
   * @return {boolean}
   */
  this.isClosed = function() {
    return this.isDead();
  }

  this.destroy = function(err) {
    this.pause();
    this.close();
    if(err) this.emit('error', err);
  }

  /**
   * Return a modified Readable stream including a possible transform method.
   * @method
   * @param {object} [options=null] Optional settings.
   * @param {function} [options.transform=null] A transformation method applied to each document emitted by the stream.
   * @return {Cursor}
   */
  this.stream = function(options) {
    streamOptions = options || {};
    return this;
  }

  /**
   * Execute the explain for the cursor
   * @method
   * @param {Cursor~resultCallback} [callback] The result callback.
   * @return {null}
   */
  this.explain = function(callback) {
    cmd.explain = true;
    self.next(callback);
  }

  this._read = function(n) {
    if(state == Cursor.CLOSED || self.isDead()) {
      // options.db.removeListener('close', closeListener);
      return self.push(null);
    }

    // Get the next item
    self.nextObject(function(err, result) {
      if(err) {
        if(!self.isDead()) self.destroy();
        return self.push(null);
      }

      // If we provided a transformation method
      if(typeof streamOptions.transform == 'function' && result != null) {
        return self.push(streamOptions.transform(result));
      }

      // Return the result
      self.push(result);
    });
  }  

  /**
   * The read() method pulls some data out of the internal buffer and returns it. If there is no data available, then it will return null.
   * @function external:Readable#read 
   * @param {number} size Optional argument to specify how much data to read.
   * @return {(String | Buffer | null)}
   */

  /**
   * Call this function to cause the stream to return strings of the specified encoding instead of Buffer objects.
   * @function external:Readable#setEncoding 
   * @param {string} encoding The encoding to use.
   * @return {null}
   */

  /**
   * This method will cause the readable stream to resume emitting data events.
   * @function external:Readable#resume 
   * @return {null}
   */

  /**
   * This method will cause a stream in flowing-mode to stop emitting data events. Any data that becomes available will remain in the internal buffer.
   * @function external:Readable#pause 
   * @return {null}
   */

  /**
   * This method pulls all the data out of a readable stream, and writes it to the supplied destination, automatically managing the flow so that the destination is not overwhelmed by a fast readable stream.
   * @function external:Readable#pipe 
   * @param {Writable} destination The destination for writing data
   * @param {object} [options] Pipe options
   * @return {null}
   */

  /**
   * This method will remove the hooks set up for a previous pipe() call.
   * @function external:Readable#unpipe 
   * @param {Writable} [destination] The destination for writing data
   * @return {null}
   */

  /**
   * This is useful in certain cases where a stream is being consumed by a parser, which needs to "un-consume" some data that it has optimistically pulled out of the source, so that the stream can be passed on to some other party.
   * @function external:Readable#unshift 
   * @param {(Buffer|string)} chunk Chunk of data to unshift onto the read queue.
   * @return {null}
   */

  /**
   * Versions of Node prior to v0.10 had streams that did not implement the entire Streams API as it is today. (See "Compatibility" below for more information.)
   * @function external:Readable#wrap 
   * @param {Stream} stream An "old style" readable stream.
   * @return {null}
   */
}