Click here to Skip to main content
15,881,882 members
Articles / Web Development / Node.js

Node.Js And Stuff

Rate me:
Please Sign up or sign in to vote.
4.97/5 (55 votes)
11 Feb 2013CPOL23 min read 356.6K   2.3K   172  
Small demo app using Node.Js/Socket.IO/MongoDB/D3.Js and jQuery.
var Long = require('bson').Long;

/**
  Reply message from mongo db
**/
var MongoReply = exports.MongoReply = function() {
  this.documents = [];
  this.index = 0;
};

MongoReply.prototype.parseHeader = function(binary_reply, bson) {
  // Unpack the standard header first
  this.messageLength = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
  this.index = this.index + 4;
  // Fetch the request id for this reply
  this.requestId = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
  this.index = this.index + 4;
  // Fetch the id of the request that triggered the response
  this.responseTo = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
  // Skip op-code field
  this.index = this.index + 4 + 4;
  // Unpack the reply message
  this.responseFlag = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
  this.index = this.index + 4;
  // Unpack the cursor id (a 64 bit long integer)
  var low_bits = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
  this.index = this.index + 4;
  var high_bits = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
  this.index = this.index + 4;
  this.cursorId = new Long(low_bits, high_bits);
  // Unpack the starting from
  this.startingFrom = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
  this.index = this.index + 4;
  // Unpack the number of objects returned
  this.numberReturned = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
  this.index = this.index + 4;
}

MongoReply.prototype.parseBody = function(binary_reply, bson, raw, callback) {
  raw = raw == null ? false : raw;
  // Just set a doc limit for deserializing
  var docLimitSize = 1024*20;

  // If our message length is very long, let's switch to process.nextTick for messages
  if(this.messageLength > docLimitSize) {
    var batchSize = this.numberReturned;
    this.documents = new Array(this.numberReturned);

    // Just walk down until we get a positive number >= 1
    for(var i = 50; i > 0; i--) {
      if((this.numberReturned/i) >= 1) {
        batchSize = i;
        break;
      }
    }

    // Actual main creator of the processFunction setting internal state to control the flow
    var parseFunction = function(_self, _binary_reply, _batchSize, _numberReturned) {
      var object_index = 0;
      // Internal loop process that will use nextTick to ensure we yield some time
      var processFunction = function() {
        // Adjust batchSize if we have less results left than batchsize
        if((_numberReturned - object_index) < _batchSize) {
          _batchSize = _numberReturned - object_index;
        }

        // If raw just process the entries
        if(raw) {
          // Iterate over the batch
          for(var i = 0; i < _batchSize; i++) {
            // Are we done ?
            if(object_index <= _numberReturned) {
              // Read the size of the bson object
              var bsonObjectSize = _binary_reply[_self.index] | _binary_reply[_self.index + 1] << 8 | _binary_reply[_self.index + 2] << 16 | _binary_reply[_self.index + 3] << 24;
              // If we are storing the raw responses to pipe straight through
              _self.documents[object_index] = binary_reply.slice(_self.index, _self.index + bsonObjectSize);
              // Adjust binary index to point to next block of binary bson data
              _self.index = _self.index + bsonObjectSize;
              // Update number of docs parsed
              object_index = object_index + 1;
            }
          }
        } else {
          try {
            // Parse documents
            _self.index = bson.deserializeStream(binary_reply, _self.index, _batchSize, _self.documents, object_index);
            // Adjust index
            object_index = object_index + _batchSize;
          } catch (err) {
            return callback(err);
          }
        }

        // If we hav more documents process NextTick
        if(object_index < _numberReturned) {
          process.nextTick(processFunction);
        } else {
          callback(null);
        }
      }

      // Return the process function
      return processFunction;
    }(this, binary_reply, batchSize, this.numberReturned)();
  } else {
    try {
      // Let's unpack all the bson documents, deserialize them and store them
      for(var object_index = 0; object_index < this.numberReturned; object_index++) {
        // Read the size of the bson object
        var bsonObjectSize = binary_reply[this.index] | binary_reply[this.index + 1] << 8 | binary_reply[this.index + 2] << 16 | binary_reply[this.index + 3] << 24;
        // If we are storing the raw responses to pipe straight through
        if(raw) {
          // Deserialize the object and add to the documents array
          this.documents.push(binary_reply.slice(this.index, this.index + bsonObjectSize));
        } else {
          // Deserialize the object and add to the documents array
          this.documents.push(bson.deserialize(binary_reply.slice(this.index, this.index + bsonObjectSize)));
        }
        // Adjust binary index to point to next block of binary bson data
        this.index = this.index + bsonObjectSize;
      }
    } catch(err) {
      return callback(err);
    }

    // No error return
    callback(null);
  }
}

MongoReply.prototype.is_error = function(){
  if(this.documents.length == 1) {
    return this.documents[0].ok == 1 ? false : true;
  }
  return false;
};

MongoReply.prototype.error_message = function() {
  return this.documents.length == 1 && this.documents[0].ok == 1 ? '' : this.documents[0].errmsg;
};

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)

- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence

Both of these at Sussex University UK.

Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2016
  • Codeproject MVP 2016
  • Microsoft C# MVP 2015
  • Codeproject MVP 2015
  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions