示例#1
0
Cursor.prototype.rewind = function() {
  var self = this;

  if (self.state != Cursor.INIT) {
    if (self.state != Cursor.CLOSED) {
      self.close(function() {});
    }

    self.numberOfReturned = 0;
    self.totalNumberOfRecords = 0;
    self.items = [];
    self.cursorId = Long.fromInt(0);
    self.state = Cursor.INIT;
    self.queryRun = false;
  }

  return self;
};
      db.command(command, _options, function(err, result) {
        if(err) {
          state = 'closed';
          return callback(err, null);
        }

        // Retrieve the cursor id
        cursorId = result.cursor.id;
        if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);
        // Get the first batch results
        items = result.cursor.firstBatch;
        // We have items return the first one
        if(items.length > 0) {
          callback(null, items.shift());
        } else {
          state = 'closed';
          callback(null, null);
        }
      });
    db.command(command, _options, function(err, result) {
      if(err) {
        state = 'closed';
        return callback(err, null);
      }

      // Retrieve the cursor id
      cursorId = result.cursor.id;
      if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);

      // Validate cursorId
      if(cursorId.equals(zeroCursor)) {
        return callback(null, result.cursor.firstBatch);
      };

      // Add to the items
      items = result.cursor.firstBatch;
      // Execute the getMore
      exhaustGetMore(callback);
    });
示例#4
0
    var queryCallback = function (err, r) {
        if (err) return callback(err);

        // If we have a timed out query or a cursor that was killed
        if ((r.responseFlags & (1 << 0)) != 0) {
            return callback(new MongoError("cursor killed or timed out"), null);
        }

        // Ensure we have a Long valie cursor id
        var cursorId = typeof r.cursorId == 'number'
            ? Long.fromNumber(r.cursorId)
            : r.cursorId;

        // Set all the values
        cursorState.documents = r.documents;
        cursorState.cursorId = cursorId;

        // Return
        callback(null, null, r.connection);
    }
      getMore(function(err, result) {
        if(err) {
          state = 'closed';
          return callback(err, null);
        }

        // Set the cursor id
        cursorId = result.cursorId;
        if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);

        // Add the items
        items = items.concat(result.documents);
        // If no more items
        if(items.length == 0) {
          state = 'closed';
          return callback(null, null);
        }

        // Return the item
        return callback(null, items.shift());
      })
示例#6
0
 test.server.setMessageHandler(request => {
   const doc = request.document;
   if (doc.ismaster) {
     request.reply(
       Object.assign({}, mock.DEFAULT_ISMASTER, {
         maxWireVersion: 6
       })
     );
   } else if (doc.find) {
     request.reply({
       cursor: {
         id: Long.fromNumber(1),
         ns: 'test.test',
         firstBatch: []
       },
       ok: 1
     });
   } else if (doc.getMore) {
     request.reply(errdoc);
   }
 });
Cursor.prototype.nextObject = function(callback) {
  var self = this;

  if(self.state == Cursor.INIT) {
    var cmd;
    try {
      cmd = generateQueryCommand(self);
    } catch (err) {
      return callback(err, null);
    }

    var commandHandler = function(err, result) {
      if(err != null && result == null) return callback(err, null);

      if(!err && result.documents[0] && result.documents[0]['$err']) {
        return self.close(function() {callback(result.documents[0]['$err'], null);});
      }

      self.queryRun = true;
      self.state = Cursor.OPEN; // Adjust the state of the cursor
      self.cursorId = result.cursorId;
      self.totalNumberOfRecords = result.numberReturned;

      // Add the new documents to the list of items
      self.items = self.items.concat(result.documents);
      self.nextObject(callback);
      result = null;
    };

    self.db._executeQueryCommand(cmd, {read:self.read, raw:self.raw}, commandHandler);
    commandHandler = null;
  } else if(self.items.length) {
    callback(null, self.items.shift());
  } else if(self.cursorId.greaterThan(Long.fromInt(0))) {
    getMore(self, callback);
  } else {
    // Force cursor to stay open
    return self.close(function() {callback(null, null);});
  }
}
    getMore(function(err, result) {
      if(err) {
        items = [];
        state = 'closed';
        return callback(err, null);
      }

      // Add the items
      items = items.concat(result.documents);

      // Set the cursor id
      cursorId = result.cursorId;
      if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);

      // If the cursor is done
      if(result.cursorId.equals(zeroCursor)) {
        return callback(null, items);
      }

      // Check the cursor id
      exhaustGetMore(callback);
    });
  var queryCallback = function(err, r) {
    if(err) return callback(err);

    // If we have a timed out query or a cursor that was killed
    if((r.responseFlags & (1 << 0)) != 0) {
      return callback(new MongoError("cursor killed or timed out"), null);
    }

    // Raw, return all the extracted documents
    if(raw) {
      cursorState.documents = r.documents;
      cursorState.cursorId = r.cursorId;
      return callback(null, r.documents);
    }

    // // Check if we have any errors
    // if((Array.isArray(r.documents) || r.documents.length == 0)) {
    //   console.log("@@@@@@@@@@@@@@@@@@@ YO")
    //   console.dir(r.cursorId)
    //   // return callback(new MongoError(f('invalid getMore result returned for cursor id %s', cursorState.cursorId)));
    // }

    // We have an error detected
    if(r.documents[0].ok == 0) {
      return callback(MongoError.create(r.documents[0]));
    }

    // Ensure we have a Long valid cursor id
    var cursorId = typeof r.documents[0].cursor.id == 'number'
      ? Long.fromNumber(r.documents[0].cursor.id)
      : r.documents[0].cursor.id;

    // Set all the values
    cursorState.documents = r.documents[0].cursor.nextBatch;
    cursorState.cursorId = cursorId;

    // Return the result
    callback(null, r.documents[0]);
  }
示例#10
0
    this.db.command(commandObject, options, function (err, result) {
        if (err) return callback(err, null);
        if (result == null) return callback(new Error("no result returned for parallelCollectionScan"), null);

        var cursors = [];
        // Create command cursors for each item
        for (var i = 0; i < result.cursors.length; i++) {
            var rawId = result.cursors[i].cursor.id
            // Convert cursorId to Long if needed
            var cursorId = typeof rawId == 'number' ? Long.fromNumber(rawId) : rawId;

            // Command cursor options
            var commandOptions = {
                batchSize: options.batchSize, cursorId: cursorId, items: result.cursors[i].cursor.firstBatch
            }

            // Add a command cursor
            cursors.push(new CommandCursor(self.db, self, {}, commandOptions));
        }

        callback(null, cursors);
    });
/**
 * Constructor for a cursor object that handles all the operations on query result
 * using find. This cursor object is unidirectional and cannot traverse backwards. Clients should not be creating a cursor directly, 
 * but use find to acquire a cursor.
 *
 * @class Represents a Cursor.
 * @param {Db} db the database object to work with.
 * @param {Collection} collection the collection to query.
 * @param {Object} selector the query selector.
 * @param {Object} fields an object containing what fields to include or exclude from objects returned.
 * @param {Number} skip number of documents to skip.
 * @param {Number} limit the number of results to return. -1 has a special meaning and is used by Db.eval. A value of 1 will also be treated as if it were -1.
 * @param {String|Array|Object} sort the required sorting for the query.
 * @param {Object} hint force the query to use a specific index.
 * @param {Boolean} explain return the explaination of the query.
 * @param {Boolean} snapshot Snapshot mode assures no duplicates are returned.
 * @param {Boolean} timeout allow the query to timeout.
 * @param {Boolean} tailable allow the cursor to be tailable.
 * @param {Number} batchSize the number of the subset of results to request the database to return for every request. This should initially be greater than 1 otherwise the database will automatically close the cursor. The batch size can be set to 1 with cursorInstance.batchSize after performing the initial query to the database.
 * @param {Boolean} raw return all query documents as raw buffers (default false).
 * @param {Boolean} read specify override of read from source (primary/secondary).
 * @param {Boolean} returnKey only return the index key.
 * @param {Number} maxScan limit the number of items to scan.
 * @param {Number} min set index bounds.
 * @param {Number} max set index bounds.
 * @param {Boolean} showDiskLoc show disk location of results.
 * @param {String} comment you can put a $comment field on a query to make looking in the profiler logs simpler.
 */
function Cursor(db, collection, selector, fields, skip, limit
	, sort, hint, explain, snapshot, timeout, tailable, batchSize, slaveOk, raw, read
	, returnKey, maxScan, min, max, showDiskLoc, comment) {
  this.db = db;
  this.collection = collection;
  this.selector = selector;
  this.fields = fields;
  this.skipValue = skip == null ? 0 : skip;
  this.limitValue = limit == null ? 0 : limit;
  this.sortValue = sort;
  this.hint = hint;
  this.explainValue = explain;
  this.snapshot = snapshot;
  this.timeout = timeout == null ? true : timeout;
  this.tailable = tailable;
  this.batchSizeValue = batchSize == null ? 0 : batchSize;
  this.slaveOk = slaveOk == null ? collection.slaveOk : slaveOk;
  this.raw = raw == null ? false : raw;
  this.read = read == null ? true : read;
  this.returnKey = returnKey;
  this.maxScan = maxScan;
  this.min = min;
  this.max = max;
  this.showDiskLoc = showDiskLoc;
  this.comment = comment;
  
  this.totalNumberOfRecords = 0;
  this.items = [];
  this.cursorId = Long.fromInt(0);

  // State variables for the cursor
  this.state = Cursor.INIT;
  // Keep track of the current query run
  this.queryRun = false;
  this.getMoreTimer = false;
  this.collectionName = (this.db.databaseName ? this.db.databaseName + "." : '') + this.collection.collectionName;
};
示例#12
0
    var queryCallback = function(err, result) {
      if(err) return callback(err);

      // Check if we have a command cursor
      if(Array.isArray(result.documents) && result.documents.length == 1) {
        if(result.documents[0]['$err'] 
          || result.documents[0]['errmsg']) {
          return callback(new MongoError(result.documents[0]), null);          
        }

        if(result.documents[0].cursor != null 
          && typeof result.documents[0].cursor != 'string') {
            var id = result.documents[0].cursor.id;
            // Promote id to long if needed
            cursorState.cursorId = typeof id == 'number' ? Long.fromNumber(id) : id;
            // If we have a firstBatch set it
            if(Array.isArray(result.documents[0].cursor.firstBatch)) {
              cursorState.documents = result.documents[0].cursor.firstBatch;
            }

            // Return after processing command cursor
            return callback(null, null);
        }

        if(Array.isArray(result.documents[0].result)) {
          cursorState.documents = result.documents[0].result;
          cursorState.cursorId = Long.ZERO;
          return callback(null, null);
        }
      }

      // Otherwise fall back to regular find path
      cursorState.cursorId = result.cursorId;
      cursorState.documents = result.documents;
      callback(null, null);
    }
示例#13
0
var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
  options = options || {};
  // Cursor reference
  var self = this;
  // Initial query
  var query = null;

  // Cursor pool
  this.pool = null;
  // Cursor server
  this.server = null;

  // Do we have a not connected handler
  this.disconnectHandler = options.disconnectHandler;

  // Set local values
  this.bson = bson;
  this.ns = ns;
  this.cmd = cmd;
  this.options = options;
  this.topology = topology;

  // All internal state
  this.cursorState = {
      cursorId: null
    , cmd: cmd
    , documents: options.documents || []
    , cursorIndex: 0
    , dead: false
    , killed: false
    , init: false
    , notified: false
    , limit: options.limit || cmd.limit || 0
    , skip: options.skip || cmd.skip || 0
    , batchSize: options.batchSize || cmd.batchSize || 1000
    , currentLimit: 0
    // Result field name if not a cursor (contains the array of results)
    , transforms: options.transforms
  }

  // Add promoteLong to cursor state
  if(typeof topologyOptions.promoteLongs == 'boolean') {
    this.cursorState.promoteLongs = topologyOptions.promoteLongs;
  }

  // Callback controller
  this.callbacks = null;

  // Logger
  this.logger = Logger('Cursor', options);

  //
  // Did we pass in a cursor id
  if(typeof cmd == 'number') {
    this.cursorState.cursorId = Long.fromNumber(cmd);
    this.cursorState.lastCursorId = this.cursorState.cursorId;
  } else if(cmd instanceof Long) {
    this.cursorState.cursorId = cmd;
    this.cursorState.lastCursorId = cmd;
  }
}
示例#14
0
Cursor.prototype.nextObject = function(options, callback) {
  var self = this;

  if(typeof options == 'function') {
    callback = options;
    options = {};
  }

  if(self.state == Cursor.INIT) {
    var cmd;
    try {
      cmd = generateQueryCommand(self);
    } catch (err) {
      return callback(err, null);
    }

    // Execute command
    var commandHandler = function(err, result) {
      self.state = Cursor.OPEN;
      if(err != null && result == null) return callback(err, null);

      if(!err && result.documents[0] && result.documents[0]['$err']) {
        return self.close(function() {callback(result.documents[0]['$err'], null);});
      }

      self.queryRun = true;
      self.state = Cursor.OPEN; // Adjust the state of the cursor
      self.cursorId = result.cursorId;
      self.totalNumberOfRecords = result.numberReturned;

      // Add the new documents to the list of items, using forloop to avoid
      // new array allocations and copying
      for(var i = 0; i < result.documents.length; i++) {
        self.items.push(result.documents[i]);
      }

      // If we have noReturn set just return (not modifying the internal item list)
      // used for toArray
      if(options.noReturn) {
        return callback(null, true);
      }

      // Ignore callbacks until the cursor is dead for exhausted
      if(self.exhaust && result.cursorId.toString() == "0") {
        self.nextObject(callback);
      } else if(self.exhaust == false || self.exhaust == null) {
        self.nextObject(callback);
      }
    };

    // If we have no connection set on this cursor check one out
    if(self.connection == null) {
      try {
        self.connection = this.read == null ? self.db.serverConfig.checkoutWriter() : self.db.serverConfig.checkoutReader(this.read);
      } catch(err) {
        return callback(err, null);
      }
    }

    // Execute the command
    self.db._executeQueryCommand(cmd, {exhaust: self.exhaust, raw:self.raw, read:self.read, connection:self.connection}, commandHandler);
    // Set the command handler to null
    commandHandler = null;
  } else if(self.items.length) {
    callback(null, self.items.shift());
  } else if(self.cursorId.greaterThan(Long.fromInt(0))) {
    getMore(self, callback);
  } else {
    // Force cursor to stay open
    return self.close(function() {callback(null, null);});
  }
}
示例#15
0
 it('converts `bson.Long` to `{$numberLong: <str>}`', function() {
   assert.deepEqual(inflate(bson.Long.fromString("10")), {
     $numberLong: "10"
   });
 });
示例#16
0
var Cursor = function(bson, ns, cmd, options, topology, topologyOptions) {
  options = options || {};
  // Initial query
  var query = null;

  // Current limit processed, used to cut the number of returned docs correctly
  var currentLimit = 0;

  // Cursor connection
  var connection = null;
  // Cursor server
  var server = null;

  // Do we have a not connected handler
  var disconnectHandler = options.disconnectHandler;

  // Cursor reference
  var self = this;

  // All internal state
  var cursorState = {
      cursorId: null
    , documents: options.documents || []
    , dead: false
    , killed: false
    , init: false
    , limit: options.limit || cmd.limit || 0
    , skip: options.skip || cmd.skip || 0
    , batchSize: options.batchSize || cmd.batchSize || 0
  }

  // Callback controller
  var callbacks = null;

  // Logger
  var logger = Logger('Cursor', options);

  // 
  // Did we pass in a cursor id
  if(typeof cmd == 'number') {
    cursorState.cursorId = Long.fromNumber(cmd);
  } else if(cmd instanceof Long) {
    cursorState.cursorId = cmd;
  }

  // Allow the manipulation of the batch size of the cursor
  // after creation has happened
  Object.defineProperty(this, 'cursorBatchSize', {
      enumerable:true,
      set: function(value) { cursorState.batchSize = value; }
    , get: function() { return cursorState.batchSize; }
  });

  // Allow the manipulation of the cursor limit
  Object.defineProperty(this, 'cursorLimit', {
      enumerable:true,
      set: function(value) { cursorState.limit = value; }
    , get: function() { return cursorState.limit; }
  });

  // Allow the manipulation of the cursor skip
  Object.defineProperty(this, 'cursorSkip', {
      enumerable:true,
      set: function(value) { cursorState.skip = value; }
    , get: function() { return cursorState.skip; }
  });

  //
  // Handle callback (including any exceptions thrown)
  var handleCallback = function(callback, err, result) {
    try {
      callback(err, result);
    } catch(err) {
      process.nextTick(function() {
        throw err;
      });
    }
  }

  /**
   * Clone the cursor
   * @method
   * @return {Cursor}
   */  
  this.clone = function() {
    return topology.cursor(ns, cmd, options);
  }

  /**
   * Retrieve the next document from the cursor
   * @method
   * @param {resultCallback} callback A callback function
   */
  this.next = function(callback) {
    if(cursorState.killed) return handleCallback(callback, null, null);
    if(cursorState.dead) return handleCallback(callback, new MongoError("cursor is dead"));    
    // We have just started the cursor
    if(!cursorState.init) {
      // Topology is not connected, save the call in the provided store to be
      // Executed at some point when the handler deems it's reconnected
      if(!topology.isConnected(options) && disconnectHandler != null) {
        return disconnectHandler.addObjectAndMethod('cursor', self, 'next', [callback], callback);
      }

      try {
        // Get a server
        server = topology.getServer(options);
        // Get a connection
        connection = server.getConnection();
        // Get the callbacks
        callbacks = server.getCallbacks();
      } catch(err) {
        return callback(err);
      }

      // Set as init
      cursorState.init = true;

      // Get the right wire protocol command
      query = server.wireProtocolHandler.command(bson, ns, cmd, cursorState, topology, options);
    }

    // Process exhaust messages
    var processExhaustMessages = function(err, result) {
      if(err) {
        cursorState.dead = true;
        callbacks.unregister(query.requestId);
        return callback(err);
      }

      // Concatenate all the documents
      cursorState.documents = cursorState.documents.concat(result.documents);

      // If we have no documents left
      if(Long.ZERO.equals(result.cursorId)) {
        cursorState.cursorId = Long.ZERO;
        callbacks.unregister(query.requestId);
        return self.next(callback);
      }

      // Set up next listener
      callbacks.register(result.requestId, processExhaustMessages)

      // Initial result
      if(cursorState.cursorId == null) {
        cursorState.cursorId = result.cursorId;
        self.next(callback);
      }
    }    

    // If we have exhaust
    if(options.exhaust && cursorState.cursorId == null) {
      // Handle all the exhaust responses
      callbacks.register(query.requestId, processExhaustMessages);
      // Write the initial command out
      return connection.write(query);
    } else if(options.exhaust && cursorState.documents.length > 0) {
      return handleCallback(callback, null, cursorState.documents.shift());
    } else if(options.exhaust && Long.ZERO.equals(cursorState.cursorId)) {
      callbacks.unregister(query.requestId);
      return handleCallback(callback, null, null);
    } else if(options.exhaust) {
      return setTimeout(function() {
        if(Long.ZERO.equals(cursorState.cursorId)) return;
        self.next(callback);
      }, 1);
    }

    // If we don't have a cursorId execute the first query
    if(cursorState.cursorId == null) {
      execInitialQuery(query, function(err, r) {
        if(err) return handleCallback(callback, err, null);
        if(cursorState.documents.length == 0) return handleCallback(callback, null, null);
        self.next(callback);
      });
    } else if(cursorState.documents.length == 0 && !Long.ZERO.equals(cursorState.cursorId)) {
      execGetMore(function(err, doc) {
        if(err) return handleCallback(callback, err);
        if(cursorState.documents.length == 0 && Long.ZERO.equals(cursorState.cursorId)) cursorState.dead = true;
        // Tailable cursor getMore result, notify owner about it
        // No attempt is made here to retry, this is left to the user of the
        // core module to handle to keep core simple
        if(cursorState.documents.length == 0 && options.tailable) {
          return handleCallback(callback, MongoError.create({
              message: "No more documents in tailed cursor"
            , tailable: options.tailable
            , awaitData: options.awaitData
          }));
        }

        if(cursorState.limit > 0 && currentLimit >= cursorState.limit) {
          cursorState.dead = true;
          cursorState.documents = [];
          return handleCallback(callback, null, null);
        }

        self.next(callback);
      });
    } else if(cursorState.documents.length == 0 && options.tailable) { 
      return handleCallback(callback, MongoError.create({
          message: "No more documents in tailed cursor"
        , tailable: options.tailable
        , awaitData: options.awaitData
      }));
    } else if(cursorState.documents.length == 0 && Long.ZERO.equals(cursorState.cursorId)) {
      cursorState.dead = true;
      handleCallback(callback, null, null);
    } else {
      if(cursorState.limit > 0 && currentLimit >= cursorState.limit) {
        cursorState.dead = true;
        return handleCallback(callback, null, null);
      }

      currentLimit += 1;
      handleCallback(callback, null, cursorState.documents.shift());
    }
  }

  /**
   * Checks if the cursor is dead
   * @method
   * @return {boolean} A boolean signifying if the cursor is dead or not
   */
  this.isDead = function() {
    return cursorState.dead == true;
  }

  /**
   * Returns current buffered documents length
   * @method
   * @return {number} The number of items in the buffered documents
   */
  this.bufferedCount = function() {
    return cursorState.documents.length;
  }

  /**
   * Returns current buffered documents
   * @method
   * @return {Array} An array of buffered documents
   */
  this.readBufferedDocuments = function(number) {
    var length = number < cursorState.documents.length ? number : cursorState.documents.length;
    var elements = cursorState.documents.splice(0, length);
    currentLimit = currentLimit + length;
    return elements;
  }

  /**
   * Resets the cursor
   * @method
   * @return {null}
   */  
  this.rewind = function() {
    if(cursorState.init) {
      if(!cursorState.dead) {
        this.kill();
      }

      currentLimit = 0;
      cursorState.init = false;
      cursorState.dead = false;
      cursorState.killed = false;
      cursorState.documents = [];
      cursorState.cursorId = null;
    }  
  }

  /**
   * Kill the cursor
   * @method
   * @param {resultCallback} callback A callback function
   */
  this.kill = function(callback) {
    // Set cursor to dead
    cursorState.dead = true;
    cursorState.killed = true;
    // Remove documents
    cursorState.documents = [];

    // If no cursor id just return
    if(cursorState.cursorId == null || cursorState.cursorId.isZero() || cursorState.init == false) {
      if(callback) callback(null, null);
      return;
    }

    // Execute command
    server.wireProtocolHandler.killCursor(bson, cursorState.cursorId, connection, callback);
  }

  //
  // Execute getMore command
  var execGetMore = function(callback) {
    if(logger.isDebug()) logger.debug(f("schedule getMore call for query [%s]", JSON.stringify(query)))
    // Determine if it's a raw query
    var raw = options.raw || cmd.raw;
    // We have a wire protocol handler
    server.wireProtocolHandler.getMore(bson, ns, cursorState, cursorState.batchSize, raw, connection, callbacks, options, callback);
  }

  // 
  // Execute the first query
  var execInitialQuery = function(query, callback) {
    if(logger.isDebug()) {
      logger.debug(f("issue initial query [%s] with flags [%s]"
        , JSON.stringify(cmd)
        , JSON.stringify(query)));
    }

    var queryCallback = function(err, result) {
      if(err) return callback(err);

      // Check if we have a command cursor
      if(Array.isArray(result.documents) && result.documents.length == 1) {
        if(result.documents[0]['$err'] 
          || result.documents[0]['errmsg']) {
          return callback(new MongoError(result.documents[0]), null);          
        }

        if(result.documents[0].cursor != null 
          && typeof result.documents[0].cursor != 'string') {
            var id = result.documents[0].cursor.id;
            // Promote id to long if needed
            cursorState.cursorId = typeof id == 'number' ? Long.fromNumber(id) : id;
            // If we have a firstBatch set it
            if(Array.isArray(result.documents[0].cursor.firstBatch)) {
              cursorState.documents = result.documents[0].cursor.firstBatch;
            }

            // Return after processing command cursor
            return callback(null, null);
        }

        if(Array.isArray(result.documents[0].result)) {
          cursorState.documents = result.documents[0].result;
          cursorState.cursorId = Long.ZERO;
          return callback(null, null);
        }
      }

      // Otherwise fall back to regular find path
      cursorState.cursorId = result.cursorId;
      cursorState.documents = result.documents;
      callback(null, null);
    }

    // If we have a raw query decorate the function
    if(options.raw || cmd.raw) {
      queryCallback.raw = options.raw || cmd.raw;
    }

    // Set up callback
    callbacks.register(query.requestId, queryCallback);

    // Write the initial command out
    connection.write(query);
  }
}
示例#17
0
 it('converts `bson.Long` to `{$numberLong: <str>}` (between 2^32 and 2^53)', function() {
   assert.deepEqual(inflate(bson.Long.fromString("4294967297")), {
     $numberLong: "4294967297"
   });
 });
示例#18
0
Response.prototype.parse = function(options) {
  // Don't parse again if not needed
  if(this.parsed) return;
  options = options || {};

  // Allow the return of raw documents instead of parsing
  var raw = options.raw || false;
  var documentsReturnedIn = options.documentsReturnedIn || null;

  //
  // Single document and documentsReturnedIn set
  //
  if(this.numberReturned == 1 && documentsReturnedIn != null && raw) {
    // Calculate the bson size
    var bsonSize = this.data[this.index] | this.data[this.index + 1] << 8 | this.data[this.index + 2] << 16 | this.data[this.index + 3] << 24;
    // Slice out the buffer containing the command result document
    var document = this.data.slice(this.index, this.index + bsonSize);
    // Set up field we wish to keep as raw
    var fieldsAsRaw = {}
    fieldsAsRaw[documentsReturnedIn] = true;
    // Set up the options
    var _options = {promoteLongs: this.opts.promoteLongs, fieldsAsRaw: fieldsAsRaw};

    // Deserialize but keep the array of documents in non-parsed form
    var doc = this.bson.deserialize(document, _options);

    // Get the documents
    this.documents = doc.cursor[documentsReturnedIn];
    this.numberReturned = this.documents.length;
    // Ensure we have a Long valie cursor id
    this.cursorId = typeof doc.cursor.id == 'number'
      ? Long.fromNumber(doc.cursor.id)
      : doc.cursor.id;

    // Adjust the index
    this.index = this.index + bsonSize;

    // Set as parsed
    this.parsed = true
    return;
  }

  //
  // Parse Body
  //
  for(var i = 0; i < this.numberReturned; i++) {
    var bsonSize = this.data[this.index] | this.data[this.index + 1] << 8 | this.data[this.index + 2] << 16 | this.data[this.index + 3] << 24;
    // Parse options
    var _options = {promoteLongs: this.opts.promoteLongs};

    // If we have raw results specified slice the return document
    if(raw) {
      this.documents[i] = this.data.slice(this.index, this.index + bsonSize);
    } else {
      this.documents[i] = this.bson.deserialize(this.data.slice(this.index, this.index + bsonSize), _options);
    }

    // Adjust the index
    this.index = this.index + bsonSize;
  }

  // Set parsed
  this.parsed = true;
}
示例#19
0
var CommandCursor = function(db, collection, command, options) {
  // Ensure empty options if no options passed
  options = options || {};

  // Default cursor id is 0
  var cursorId = Long.fromInt(0);
  var zeroCursor = Long.fromInt(0);
  var state = 'init';

  // Hardcode batch size
  command.cursor.batchSize = 1;

  // BatchSize
  var batchSize = command.cursor.batchSize || 0;
  var raw = options.raw || false;
  var readPreference = options.readPreference || 'primary';

  // Checkout a connection
  var connection = db.serverConfig.checkoutReader(readPreference);
  // MaxTimeMS
  var maxTimeMS = options.maxTimeMS;

  // Contains all the items
  var items = null;

  // Execute getmore
  var getMore = function(callback) {
    // Resolve more of the cursor using the getMore command
    var getMoreCommand = new GetMoreCommand(db
      , db.databaseName + "." + collection.collectionName
      , batchSize
      , cursorId
    );

    // Set up options
    var command_options = { connection:connection };

    // Execute the getMore Command
    db._executeQueryCommand(getMoreCommand, command_options, function(err, result) {
      if(err) {
        items = [];
        state = 'closed';
        return callback(err);
      }

      // Return all the documents
      callback(null, result);
    });
  }

  var exhaustGetMore = function(callback) {
    getMore(function(err, result) {
      if(err) {
        items = [];
        state = 'closed';
        return callback(err, null);
      }

      // Add the items
      items = items.concat(result.documents);

      // Set the cursor id
      cursorId = result.cursorId;
      if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);

      // If the cursor is done
      if(result.cursorId.equals(zeroCursor)) {
        return callback(null, items);
      }

      // Check the cursor id
      exhaustGetMore(callback);
    });
  }

  var exhaustGetMoreEach = function(callback) {
    getMore(function(err, result) {
      if(err) {
        items = [];
        state = 'closed';
        return callback(err, null);
      }

      // Add the items
      items = result.documents;

      // Emit all the items in the first batch
      while(items.length > 0) {
        callback(null, items.shift());
      }

      // Set the cursor id
      cursorId = result.cursorId;
      if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);

      // If the cursor is done
      if(result.cursorId.equals(zeroCursor)) {
        state = "closed";
        return callback(null, null);
      }

      // Check the cursor id
      exhaustGetMoreEach(callback);
    });
  }

  //
  // Get all the elements
  //
  this.get = function(options, callback) {
    if(typeof options == 'function') {
      callback = options;
      options = {};
    }

    // Set the connection to the passed in one if it's provided
    connection = options.connection ? options.connection : connection;

    // Command options
    var _options = {connection:connection};
    if(typeof maxTimeMS == 'number') _options.maxTimeMS = maxTimeMS;

    // Execute the internal command first
    db.command(command, _options, function(err, result) {
      if(err) {
        state = 'closed';
        return callback(err, null);
      }

      // Retrieve the cursor id
      cursorId = result.cursor.id;
      if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);

      // Validate cursorId
      if(cursorId.equals(zeroCursor)) {
        return callback(null, result.cursor.firstBatch);
      };

      // Add to the items
      items = result.cursor.firstBatch;
      // Execute the getMore
      exhaustGetMore(callback);
    });
  }

  //
  // Iterate over all the items
  //
  this.each = function(options, callback) {
    if(typeof options == 'function') {
      callback = options;
      options = {};
    }

    // If it's a closed cursor return error
    if(this.isClosed()) return callback(new Error("cursor is closed"));
    // Set the connection to the passed in one if it's provided
    connection = options.connection ? options.connection : connection;

    // Command options
    var _options = {connection:connection};
    if(typeof maxTimeMS == 'number') _options.maxTimeMS = maxTimeMS;

    // Execute the internal command first
    db.command(command, _options, function(err, result) {
      if(err) {
        state = 'closed';
        return callback(err, null);
      }

      // Get all the items
      items = result.cursor.firstBatch;

      // Emit all the items in the first batch
      while(items.length > 0) {
        callback(null, items.shift());
      }

      // Retrieve the cursor id
      cursorId = result.cursor.id;
      if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);

      // If no cursor we just finish up the current batch of items
      if(cursorId.equals(zeroCursor)) {
        state = 'closed';
        return callback(null, null);
      }

      // Emit each until no more getMore's
      exhaustGetMoreEach(callback);
    });
  }

  //
  // Get the next object
  //
  this.next = function(options, callback) {
    if(typeof options == 'function') {
      callback = options;
      options = {};
    }

    // If it's a closed cursor return error
    if(this.isClosed()) return callback(new Error("cursor is closed"));

    // Set the connection to the passed in one if it's provided
    connection = options.connection ? options.connection : connection;

    // Command options
    var _options = {connection:connection};
    if(typeof maxTimeMS == 'number') _options.maxTimeMS = maxTimeMS;

    // Execute the internal command first
    if(!items) {
      db.command(command, _options, function(err, result) {
        if(err) {
          state = 'closed';
          return callback(err, null);
        }

        // Retrieve the cursor id
        cursorId = result.cursor.id;
        if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);
        // Get the first batch results
        items = result.cursor.firstBatch;
        // We have items return the first one
        if(items.length > 0) {
          callback(null, items.shift());
        } else {
          state = 'closed';
          callback(null, null);
        }
      });
    } else if(items.length > 0) {
      callback(null, items.shift());
    } else if(items.length == 0 && cursorId.equals(zeroCursor)) {
      state = 'closed';
      callback(null, null);
    } else {
      // Execute a getMore
      getMore(function(err, result) {
        if(err) {
          state = 'closed';
          return callback(err, null);
        }

        // Set the cursor id
        cursorId = result.cursorId;
        if(typeof cursorId == 'number') cursorId = Long.fromNumber(cursorId);

        // Add the items
        items = items.concat(result.documents);
        // If no more items
        if(items.length == 0) {
          state = 'closed';
          return callback(null, null);
        }

        // Return the item
        return callback(null, items.shift());
      })
    }
  }

  // Validate if the cursor is closed
  this.isClosed = function() {
    return state == 'closed';
  }

  // Allow us to set the MaxTimeMS
  this.maxTimeMS = function(_maxTimeMS) {
    maxTimeMS = _maxTimeMS;
  }

  // Close the cursor sending a kill cursor command if needed
  this.close = function(options, callback) {
    if(typeof options == 'function') {
      callback = options;
      options = {};
    }

    // Close the cursor if not needed
    if(cursorId instanceof Long && cursorId.greaterThan(Long.fromInt(0))) {
      try {
        var command = new KillCursorCommand(this.db, [cursorId]);
        // Added an empty callback to ensure we don't throw any null exceptions
        db._executeQueryCommand(command, {connection:connection});
      } catch(err) {}
    }

    // Null out the connection
    connection = null;
    // Reset cursor id
    cursorId = Long.fromInt(0);
    // Set to closed status
    state = 'closed';
    // Clear out all the items
    items = null;

    if(callback) {
      callback(null, null);
    }
  }
}
示例#20
0
 it('converts `bson.Long` to `{$numberLong: <str>}` (greater than 2^53)', function() {
   assert.deepEqual(inflate(bson.Long.fromString("18014398509481984")), {
     $numberLong: "18014398509481984"
   });
 });
示例#21
0
 $numberLong: function(val) {
   return bson.Long.fromString(val.$numberLong);
 },
示例#22
0
Cursor.prototype.nextObject = function(options, callback) {
  var self = this;

  if(typeof options == 'function') {
    callback = options;
    options = {};
  }

  if(self.state == Cursor.INIT) {
    var cmd;
    try {
      cmd = generateQueryCommand(self);
    } catch (err) {
      return callback(err, null);
    }

    // No need to check the keys
    var queryOptions = {exhaust: self.exhaust
      , raw:self.raw
      , readPreference:self.readPreference
      , connection:self.connection
      , checkKeys: false};

    // Execute command
    var commandHandler = function(err, result) {
      // If on reconnect, the command got given a different connection, switch
      // the whole cursor to it.
      self.connection = queryOptions.connection;
      self.state = Cursor.OPEN; // Adjust the state of the cursor
      if(err != null && result == null) return callback(utils.toError(err), null);

      if(err == null && (result == null || result.documents == null || !Array.isArray(result.documents))) {
        return self.close(function() {callback(new Error("command failed to return results"), null);});
      }

      if(err == null && result && result.documents[0] && result.documents[0]['$err']) {
        return self.close(function() {callback(utils.toError(result.documents[0]), null);});
      }

      if(err == null && result && result.documents[0] && result.documents[0]['errmsg']) {
        return self.close(function() {callback(utils.toError(result.documents[0]), null);});
      }

      self.queryRun = true;
      self.cursorId = result.cursorId;
      self.totalNumberOfRecords = result.numberReturned;

      // Add the new documents to the list of items, using forloop to avoid
      // new array allocations and copying
      for(var i = 0; i < result.documents.length; i++) {
        self.items.push(result.documents[i]);
      }

      // If we have noReturn set just return (not modifying the internal item list)
      // used for toArray
      if(options.noReturn) {
        return callback(null, true);
      }

      // Ignore callbacks until the cursor is dead for exhausted
      if(self.exhaust && result.cursorId.toString() == "0") {
        self.nextObject(callback);
      } else if(self.exhaust == false || self.exhaust == null) {
        self.nextObject(callback);
      }
    };

    // If we have no connection set on this cursor check one out
    if(self.connection == null) {
      try {
        self.connection = self.db.serverConfig.checkoutReader(this.readPreference);

        // Check if we have an error from the checkout Reader function
        if(self.connection instanceof Error) {
          return callback(utils.toError(self.connection), null);
        }

        // Add to the query options
        queryOptions.connection = self.connection;
      } catch(err) {
        return callback(utils.toError(err), null);
      }
    }

    // Execute the command
    self.db._executeQueryCommand(cmd, queryOptions, commandHandler);
    // Set the command handler to null
    commandHandler = null;
  } else if(self.items.length) {
    var doc = self.items.shift();
    // If we have a transform
    if(typeof self.transforms == 'function') {
      doc = self.transforms(doc);
    }

    callback(null, doc);
  } else if(self.cursorId.greaterThan(Long.fromInt(0))) {
    getMore(self, callback);
  } else {
    // Force cursor to stay open
    return self.close(function() {callback(null, null);});
  }
}
示例#23
0
  var queryCallback = function(err, result) {
    if(err) return callback(err);

    // Query failure bit set
    if(result.queryFailure) {
      return callback(MongoError.create(result.documents[0]), null);
    }

    // Store the connection for usage with getMore command
    self.connection = result.connection;

    // Check if we have a command cursor
    if(Array.isArray(result.documents) && result.documents.length == 1
      && (!self.cmd.find || (self.cmd.find && self.cmd.virtual == false))
      && (result.documents[0].cursor != 'string'
        || result.documents[0]['$err']
        || result.documents[0]['errmsg']
        || Array.isArray(result.documents[0].result))
      ) {

      // We have a an error document return the error
      if(result.documents[0]['$err']
        || result.documents[0]['errmsg']) {
        return callback(MongoError.create(result.documents[0]), null);
      }

      // We have a cursor document
      if(result.documents[0].cursor != null
        && typeof result.documents[0].cursor != 'string') {
          var id = result.documents[0].cursor.id;
          // If we have a namespace change set the new namespace for getmores
          if(result.documents[0].cursor.ns) {
            self.ns = result.documents[0].cursor.ns;
          }
          // Promote id to long if needed
          self.cursorState.cursorId = typeof id == 'number' ? Long.fromNumber(id) : id;
          self.cursorState.lastCursorId = self.cursorState.cursorId;
          // If we have a firstBatch set it
          if(Array.isArray(result.documents[0].cursor.firstBatch)) {
            self.cursorState.documents = result.documents[0].cursor.firstBatch;//.reverse();
          }

          // Return after processing command cursor
          return callback(null, null);
      }

      if(Array.isArray(result.documents[0].result)) {
        self.cursorState.documents = result.documents[0].result;
        self.cursorState.cursorId = Long.ZERO;
        return callback(null, null);
      }
    }

    // Otherwise fall back to regular find path
    self.cursorState.cursorId = result.cursorId;
    self.cursorState.documents = result.documents;
    self.cursorState.lastCursorId = result.cursorId;

    // Transform the results with passed in transformation method if provided
    if(self.cursorState.transforms && typeof self.cursorState.transforms.query == 'function') {
      self.cursorState.documents = self.cursorState.transforms.query(result);
    }

    // Return callback
    callback(null, null);
  }