Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

mongodb

Package Overview
Dependencies
Maintainers
0
Versions
575
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mongodb - npm Package Compare versions

Comparing version 0.9.4 to 0.9.6-7

.npmignore

2

benchmark/grid_fs_write_benchmark.js

@@ -10,3 +10,3 @@ require.paths.unshift('../lib');

GridStore = require('mongodb').GridStore,
sys = require('sys');
sys = require('util');

@@ -13,0 +13,0 @@ var simulated_buffer = new Buffer(1024*1000*10).toString();

@@ -8,10 +8,18 @@ require.paths.unshift('../lib');

Collection = require('mongodb').Collection,
sys = require('sys');
var BSON = require('bson');
sys = require('util'),
debug = require('util').debug,
inspect = require('util').inspect;
var db = new Db('streaming_benchmark', new Server("127.0.0.1", 27017, {auto_reconnect: true}), {})
// var BSON = require('bson');
var parser = require('mongodb').BSONPure;
var objectID = require('mongodb').ObjectID;
// var parser = BSON;
// var objectID = BSON.ObjectID;
var db = new Db('streaming_benchmark', new Server("127.0.0.1", 27017, {auto_reconnect: true, poolSize:4}), {})
// Set native deserializer
db.bson_deserializer = BSON;
db.bson_serializer = BSON;
db.pkFactory = BSON.ObjectID;
db.bson_deserializer = parser;
db.bson_serializer = parser;
db.pkFactory = objectID;

@@ -26,3 +34,3 @@ // Open the db

for(var i = 0; i < 100000; i++) {
// for(var i = 0; i < 1000; i++) {
// for(var i = 0; i < 10000; i++) {
collection.save({'i':i, 'a':i, 'c':i, 'd':{'i':i}}, function(err, result){});

@@ -44,3 +52,3 @@ }

if ((count%10000)==0) sys.puts("recs:" + count + " :: " +
((new Date().getTime() - started_at)/1000) + "seconds");
((new Date().getTime() - started_at)/10000) + "seconds");
});

@@ -47,0 +55,0 @@ });

GLOBAL.DEBUG = true;
sys = require("sys");
debug = require('util').debug,
inspect = require('util').inspect,
test = require("assert");

@@ -23,3 +25,3 @@

// Erase all records in collection
collection.remove(function(err, collection) {
collection.remove({}, function(err, r) {
db.admin(function(err, admin) {

@@ -39,3 +41,2 @@

cursor.toArray(function(err, items) {
// Stop profiling

@@ -47,5 +48,5 @@ admin.setProfilingLevel('off', function(err, level) {

// Validate returns a hash if all is well or return an error has if there is a
// Validate returns a hash if all is well or return an error hash if there is a
// problem.
admin.validatCollection(collection.collectionName, function(err, result) {
admin.validateCollection(collection.collectionName, function(err, result) {
sys.puts(result.result);

@@ -52,0 +53,0 @@ db.close();

@@ -11,3 +11,2 @@ GLOBAL.DEBUG = true;

Server = require('../lib/mongodb').Server,
ServerCluster = require('../lib/mongodb').ServerCluster,
// BSON = require('../lib/mongodb').BSONPure;

@@ -14,0 +13,0 @@ ReplSetServers = require('../lib/mongodb').ReplSetServers,

GLOBAL.DEBUG = true;
sys = require("sys");
sys = require("sys"),
debug = require('util').debug,
inspect = require('util').inspect,
test = require("assert");

@@ -9,3 +11,2 @@

Server = require('../lib/mongodb').Server,
// BSON = require('../lib/mongodb').BSONPure;
BSON = require('../lib/mongodb').BSONNative;

@@ -20,5 +21,5 @@

db.dropDatabase(function(err, result) {
db.collection('test', function(err, collection) {
db.collection('test', function(err, collection) {
// Erase all records from the collection, if any
collection.remove(function(err, collection) {
collection.remove({}, function(err, result) {
// Insert 3 records

@@ -25,0 +26,0 @@ for(var i = 0; i < 3; i++) {

require.paths.unshift("../../lib");
var sys = require('sys'),
var sys = require('util'),
debug = require('util').debug,
inspect = require('util').inspect,
Buffer = require('buffer').Buffer,

@@ -22,4 +24,8 @@ BSON = require('./bson').BSON,

sys.puts("=== EXCEUTING TEST_BSON ===");
sys.puts("=== EXECUTING TEST_BSON ===");
// Should fail due to illegal key
assert.throws(function() { new ObjectID('foo'); })
assert.throws(function() { new ObjectID2('foo'); })
// Long data type tests

@@ -65,67 +71,91 @@ var l2_string = Long2.fromNumber(100);

// Simple serialization and deserialization test for a Single String value
var simple_string_serialized = BSON.serialize({doc:'Serialize'});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:'Serialize'};
var simple_string_serialized = BSON.serialize(doc, true, false);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
// Nested doc
var doc = {a:{b:{c:1}}};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
// Simple integer serialization/deserialization test, including testing boundary conditions
var simple_string_serialized = BSON.serialize({doc:-1});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:-1};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
var simple_string_serialized = BSON.serialize({doc:2147483648});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:2147483648};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
var simple_string_serialized = BSON.serialize({doc:-2147483648});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:-2147483648};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
// Simple serialization and deserialization test for a Long value
var simple_string_serialized = BSON.serialize({doc:Long2.fromNumber(9223372036854775807)});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:Long2.fromNumber(9223372036854775807)};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize({doc:Long.fromNumber(9223372036854775807)}, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
var simple_string_serialized = BSON.serialize({doc:Long2.fromNumber(-9223372036854775807)});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:Long2.fromNumber(-9223372036854775807)};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize({doc:Long.fromNumber(-9223372036854775807)}, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
// Simple serialization and deserialization for a Float value
var simple_string_serialized = BSON.serialize({doc:2222.3333});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:2222.3333};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
var simple_string_serialized = BSON.serialize({doc:-2222.3333});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:-2222.3333};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
// Simple serialization and deserialization for a null value
var simple_string_serialized = BSON.serialize({doc:null});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:null};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
// Simple serialization and deserialization for a boolean value
var simple_string_serialized = BSON.serialize({doc:true});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:true};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
// Simple serialization and deserialization for a date value
var date = new Date();
var simple_string_serialized = BSON.serialize({doc:date});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')));
assert.deepEqual(BSONJS.deserialize(simple_string_serialized), BSON.deserialize(simple_string_serialized));
var doc = {doc:date};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')), BSON.deserialize(simple_string_serialized));
// Simple serialization and deserialization for a boolean value
var simple_string_serialized = BSON.serialize({doc:/abcd/mi});
assert.equal(BSONJS.deserialize(simple_string_serialized).doc.toString(), BSON.deserialize(simple_string_serialized).doc.toString());
assert.equal(BSONJS.deserialize(simple_string_serialized).doc.toString(), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString());
var doc = {doc:/abcd/mi};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.equal(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), BSON.deserialize(simple_string_serialized).doc.toString());
var simple_string_serialized = BSON.serialize({doc:/abcd/});
assert.equal(BSONJS.deserialize(simple_string_serialized).doc.toString(), BSON.deserialize(simple_string_serialized).doc.toString());
assert.equal(BSONJS.deserialize(simple_string_serialized).doc.toString(), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString());
var doc = {doc:/abcd/};
var simple_string_serialized = BSON.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc, false, true));
assert.equal(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), BSON.deserialize(simple_string_serialized).doc.toString());
// Simple serialization and deserialization for a objectId value
var simple_string_serialized = BSON.serialize({doc:new ObjectID2()});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized).doc.toString(), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString());
assert.deepEqual(BSONJS.deserialize(simple_string_serialized).doc.toString(), BSON.deserialize(simple_string_serialized).doc.toString());
var doc = {doc:new ObjectID2()};
var simple_string_serialized = BSON.serialize(doc, false, true);
var doc2 = {doc:ObjectID.createFromHexString(doc.doc.toHexString())};
assert.deepEqual(simple_string_serialized, BSONJS.serialize(doc2, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.toString(), BSON.deserialize(simple_string_serialized).doc.toString());
// Simple serialization and deserialization for a Binary value

@@ -135,34 +165,44 @@ var binary = new Binary2();

for(var index = 0; index < string.length; index++) { binary.put(string.charAt(index)); }
var simple_string_serialized = BSON.serialize({doc:binary});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized).doc.value(), BSON.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.value());
assert.deepEqual(BSONJS.deserialize(simple_string_serialized).doc.value(), BSON.deserialize(simple_string_serialized).doc.value());
var binary2 = new Binary();
var string = 'binstring'
for(var index = 0; index < string.length; index++) { binary2.put(string.charAt(index)); }
var simple_string_serialized = BSON.serialize({doc:binary}, false, true);
assert.deepEqual(simple_string_serialized, BSONJS.serialize({doc:binary2}, false, true));
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.value(), BSON.deserialize(simple_string_serialized).doc.value());
// Simple serialization and deserialization for a Code value
var code = new Code2('this.a > i', {'i': 1});
var code2 = new Code('this.a > i', {'i': 1});
var simple_string_serialized_2 = BSONJS.serialize({doc:code2});
var simple_string_serialized = BSON.serialize({doc:code});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized_2).doc.scope, BSON.deserialize(new Buffer(simple_string_serialized, 'binary')).doc.scope);
assert.deepEqual(BSONJS.deserialize(simple_string_serialized_2).doc.code, BSON.deserialize(simple_string_serialized).doc.code);
var simple_string_serialized_2 = BSONJS.serialize({doc:code2}, false, true);
var simple_string_serialized = BSON.serialize({doc:code}, false, true);
assert.deepEqual(simple_string_serialized, simple_string_serialized_2);
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc.scope, BSON.deserialize(simple_string_serialized).doc.scope);
// Simple serialization and deserialization for an Object
var simple_string_serialized = BSON.serialize({doc:{a:1, b:{c:2}}});
var simple_string_serialized_2 = BSONJS.serialize({doc:{a:1, b:{c:2}}});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized_2).doc, BSON.deserialize(new Buffer(simple_string_serialized, 'binary')).doc);
assert.deepEqual(BSONJS.deserialize(simple_string_serialized_2).doc, BSON.deserialize(simple_string_serialized).doc);
var simple_string_serialized = BSON.serialize({doc:{a:1, b:{c:2}}}, false, true);
var simple_string_serialized_2 = BSONJS.serialize({doc:{a:1, b:{c:2}}}, false, true);
assert.deepEqual(simple_string_serialized, simple_string_serialized_2)
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc, BSON.deserialize(simple_string_serialized).doc);
// Simple serialization and deserialization for an Array
var simple_string_serialized = BSON.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]});
var simple_string_serialized_2 = BSONJS.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]});
assert.deepEqual(BSONJS.deserialize(simple_string_serialized_2).doc, BSON.deserialize(new Buffer(simple_string_serialized, 'binary')).doc);
assert.deepEqual(BSONJS.deserialize(simple_string_serialized_2).doc, BSON.deserialize(simple_string_serialized).doc);
var simple_string_serialized = BSON.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]}, false, true);
var simple_string_serialized_2 = BSONJS.serialize({doc:[9, 9, 1, 2, 3, 1, 1, 1, 1, 1, 1, 1]}, false, true);
assert.deepEqual(simple_string_serialized, simple_string_serialized_2)
assert.deepEqual(BSONJS.deserialize(new Buffer(simple_string_serialized_2, 'binary')).doc, BSON.deserialize(simple_string_serialized).doc);
// Simple serialization and deserialization for a DBRef
var oid = new ObjectID2()
var simple_string_serialized = BSONJS.serialize({doc:new DBRef('namespace', oid, 'integration_tests_')});
var simple_string_serialized_2 = BSON.serialize({doc:new DBRef2('namespace', oid, 'integration_tests_')});
var oid2 = new ObjectID.createFromHexString(oid.toHexString())
var simple_string_serialized = BSONJS.serialize({doc:new DBRef('namespace', oid2, 'integration_tests_')}, false, true);
var simple_string_serialized_2 = BSON.serialize({doc:new DBRef2('namespace', oid, 'integration_tests_')}, false, true);
assert.deepEqual(simple_string_serialized, simple_string_serialized_2)
// Ensure we have the same values for the dbref
var object_js = BSONJS.deserialize(simple_string_serialized_2);
var object_js = BSONJS.deserialize(new Buffer(simple_string_serialized_2, 'binary'));
var object_c = BSON.deserialize(simple_string_serialized);
assert.equal(object_js.doc.namespace, object_c.doc.namespace);

@@ -179,3 +219,3 @@ assert.equal(object_js.doc.oid.toHexString(), object_c.doc.oid.toHexString());

}
var object = BSON.deserialize(serialized_data);
var object = BSON.deserialize(new Buffer(serialized_data, 'binary'));
assert.equal('Patty', object.name)

@@ -187,3 +227,6 @@ assert.equal(34, object.age)

var doc = { "name" : "本荘由利地域に洪水警報", "name1" : "öüóőúéáűíÖÜÓŐÚÉÁŰÍ", "name2" : "abcdedede"};
var simple_string_serialized = BSON.serialize(doc);
var simple_string_serialized = BSON.serialize(doc, false, true);
var simple_string_serialized2 = BSONJS.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, simple_string_serialized2)
var object = BSON.deserialize(simple_string_serialized);

@@ -196,4 +239,6 @@ assert.equal(doc.name, object.name)

var doc = {b:[1, 2, 3]};
var simple_string_serialized = BSON.serialize(doc);
var simple_string_serialized_2 = BSONJS.serialize(doc);
var simple_string_serialized = BSON.serialize(doc, false, true);
var simple_string_serialized_2 = BSONJS.serialize(doc, false, true);
assert.deepEqual(simple_string_serialized, simple_string_serialized_2)
var object = BSON.deserialize(simple_string_serialized);

@@ -208,16 +253,83 @@ assert.deepEqual(doc, object)

// Test same serialization for Object ID
var object_id = new ObjectID();
var object_id2 = ObjectID2.createFromHexString(object_id.toString())
var simple_string_serialized = BSONJS.serialize({doc:object_id}, false, true);
var simple_string_serialized_2 = BSON.serialize({doc:object_id2}, false, true);
assert.equal(simple_string_serialized_2.length, simple_string_serialized.length);
assert.deepEqual(simple_string_serialized, simple_string_serialized_2)
var object = BSONJS.deserialize(new Buffer(simple_string_serialized_2, 'binary'));
var object2 = BSON.deserialize(simple_string_serialized);
assert.deepEqual(object, object2);
// JS Object
var c1 = { _id: new ObjectID, comments: [], title: 'number 1' };
var c2 = { _id: new ObjectID, comments: [], title: 'number 2' };
var doc = {
numbers: []
, owners: []
, comments: [c1, c2]
, _id: new ObjectID
};
var simple_string_serialized = BSONJS.serialize(doc, false, true);
// C++ Object
var c1 = { _id: ObjectID2.createFromHexString(c1._id.toHexString()), comments: [], title: 'number 1' };
var c2 = { _id: ObjectID2.createFromHexString(c2._id.toHexString()), comments: [], title: 'number 2' };
var doc = {
numbers: []
, owners: []
, comments: [c1, c2]
, _id: ObjectID2.createFromHexString(doc._id.toHexString())
};
var simple_string_serialized_2 = BSON.serialize(doc, false, true);
for(var i = 0; i < simple_string_serialized_2.length; i++) {
// debug(i + "[" + simple_string_serialized_2[i] + "] = [" + simple_string_serialized[i] + "]")
assert.equal(simple_string_serialized_2[i], simple_string_serialized[i]);
}
// Deserialize the string
var doc1 = BSONJS.deserialize(new Buffer(simple_string_serialized_2));
var doc2 = BSON.deserialize(new Buffer(simple_string_serialized_2));
assert.deepEqual(doc2, doc1)
var doc = {
_id: 'testid',
key1: { code: 'test1', time: {start:1309323402727,end:1309323402727}, x:10, y:5 },
key2: { code: 'test1', time: {start:1309323402727,end:1309323402727}, x:10, y:5 }
};
var simple_string_serialized = BSONJS.serialize(doc, false, true);
var simple_string_serialized_2 = BSON.serialize(doc, false, true);
for(var i = 0; i < simple_string_serialized_2.length; i++) {
// debug(i + "[" + simple_string_serialized_2[i] + "] = [" + simple_string_serialized[i] + "]")
assert.equal(simple_string_serialized_2[i], simple_string_serialized[i]);
}
// Deserialize the string
var doc1 = BSONJS.deserialize(new Buffer(simple_string_serialized_2));
var doc2 = BSON.deserialize(new Buffer(simple_string_serialized_2));
assert.deepEqual(doc2, doc1)
// Force garbage collect
global.gc();
require.paths.unshift("../../lib");
var sys = require('sys'),
var sys = require('util'),
fs = require('fs'),

@@ -17,3 +17,3 @@ Buffer = require('buffer').Buffer,

sys.puts("=== EXCEUTING TEST_FULL_BSON ===");
sys.puts("=== EXECUTING TEST_FULL_BSON ===");

@@ -39,3 +39,4 @@ // Should Correctly Deserialize object

}
var object = BSONJS.deserialize(serialized_data);
var object = BSONJS.deserialize(new Buffer(serialized_data, 'binary'));
assert.equal("hello", object.string);

@@ -75,3 +76,3 @@ assert.deepEqual([1, 2, 3], object.array);

var serialized_data = BSON.serialize(test_undefined)
var object = BSONJS.deserialize(serialized_data);
var object = BSONJS.deserialize(new Buffer(serialized_data, 'binary'));
assert.equal(null, object.doc)

@@ -171,3 +172,3 @@

// Should Correctly Serialize and Deserialize a big Binary object
var data = fs.readFileSync("../../integration/test_gs_weird_bug.png", 'binary');
var data = fs.readFileSync("../../test/gridstore/test_gs_weird_bug.png", 'binary');
var bin = new Binary()

@@ -174,0 +175,0 @@ bin.write(data)

var Collection = require('./collection').Collection,
Cursor = require('./cursor').Cursor,
DbCommand = require('./commands/db_command').DbCommand;
DbCommand = require('./commands/db_command').DbCommand,
debug = require('util').debug,
inspect = require('util').inspect;

@@ -12,16 +14,15 @@ var Admin = exports.Admin = function(db) {

var command = {buildinfo:1};
var databaseName = self.db.databaseName;
self.db.databaseName = 'admin';
this.db.executeDbCommand(command, function(err, doc) {
this.command(command, function(err, doc) {
if(err != null) return callback(err, null);
return callback(null, doc.documents[0]);
});
// Ensure change before event loop executes
self.db.databaseName = databaseName;
}
Admin.prototype.profilingLevel = function(callback) {
var self = this;
var command = {profile:-1};
this.db.executeDbCommand(command, function(err, doc) {
this.command(command, function(err, doc) {
doc = doc.documents[0];
if(err == null && (doc.ok == 1 || doc.was.constructor == Numeric)) {

@@ -44,5 +45,38 @@ var was = doc.was;

Admin.prototype.authenticate = function(username, password, callback) {
var self = this;
var databaseName = this.db.databaseName;
this.db.databaseName = 'admin';
this.db.authenticate(username, password, function(err, result) {
self.db.databaseName = databaseName;
return callback(err, result);
})
}
Admin.prototype.logout = function(options, callback) {
var self = this;
var databaseName = this.db.databaseName;
this.db.databaseName = 'admin';
this.db.logout(options, function(err, result) {
return callback(err, result);
})
self.db.databaseName = databaseName;
}
Admin.prototype.addUser = function(username, password, callback) {
var self = this;
var databaseName = this.db.databaseName;
this.db.databaseName = 'admin';
this.db.addUser(username, password, function(err, result) {
self.db.databaseName = databaseName;
return callback(err, result);
})
}
Admin.prototype.setProfilingLevel = function(level, callback) {
var self = this;
var command = {};
var profile = 0;
if(level == "off") {

@@ -55,13 +89,13 @@ profile = 0;

} else {
callback(new Error("Error: illegal profiling level value " + level));
return;
return callback(new Error("Error: illegal profiling level value " + level));
}
command['profile'] = profile;
this.db.executeDbCommand(command, function(err, doc) {
this.command(command, function(err, doc) {
doc = doc.documents[0];
if(err == null && (doc.ok == 1 || doc.was.constructor == Numeric)) {
callback(null, level);
return callback(null, level);
} else {
err != null ? callback(err, null) : callback(new Error("Error with profile command"), null);
return err != null ? callback(err, null) : callback(new Error("Error with profile command"), null);
}

@@ -72,24 +106,41 @@ });

Admin.prototype.profilingInfo = function(callback) {
var self = this;
var databaseName = this.db.databaseName;
this.db.databaseName = 'admin';
new Cursor(this.db, new Collection(this.db, DbCommand.SYSTEM_PROFILE_COLLECTION), {}).toArray(function(err, items) {
callback(err, items);
});
return callback(err, items);
});
self.db.databaseName = databaseName;
};
Admin.prototype.validatCollection = function(collectionName, callback) {
Admin.prototype.command = function(command, callback) {
var self = this;
// Execute a command
this.db.executeDbAdminCommand(command, function(err, result) {
// Ensure change before event loop executes
return callback(err, result);
});
}
Admin.prototype.validateCollection = function(collectionName, callback) {
var self = this;
var command = {validate: collectionName};
this.db.executeDbCommand(command, function(err, doc) {
if(err != null) return callback(err, null);
doc = doc.documents[0];
if(err != null) {
callback(err, null);
} else if(doc.ok == 0) {
callback(new Error("Error with validate command"), null);
if(doc.ok == 0) {
return callback(new Error("Error with validate command"), null);
} else if(doc.result.constructor != String) {
callback(new Error("Error with validation data"), null);
return callback(new Error("Error with validation data"), null);
} else if(doc.result.match(/exception|corrupt/) != null) {
callback(new Error("Error: invalid collection " + collectionName), null);
return callback(new Error("Error: invalid collection " + collectionName), null);
} else {
callback(null, doc);
return callback(null, doc);
}
});
};

@@ -1,11 +0,22 @@

var sys = require('sys');
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/classes/binary-parser [v1.0]
/**
* Module dependencies.
*/
var sys = require('util');
/**
* Binary Parser.
* Jonas Raoni Soares Silva
* http://jsfromhell.com/classes/binary-parser [v1.0]
*/
var chr = String.fromCharCode;
var maxBits = [];
for(var i = 0; i<64; i++)
for (var i = 0; i < 64; i++) {
maxBits[i] = Math.pow(2, i);
}
var p = exports.BinaryParser = function( bigEndian, allowExceptions ){
function BinaryParser (bigEndian, allowExceptions) {
this.bigEndian = bigEndian;

@@ -15,138 +26,154 @@ this.allowExceptions = allowExceptions;

var Buffer = exports.BinaryParser.Buffer = function( bigEndian, buffer ){
this.bigEndian = bigEndian || 0;
this.buffer = [];
this.setBuffer( buffer );
};
BinaryParser.warn = function warn (msg) {
if (this.allowExceptions) {
throw new Error(msg);
}
Buffer.prototype.setBuffer = function( data ){
if( data ){
for( var l, i = l = data.length, b = this.buffer = new Array( l ); i; b[l - i] = data.charCodeAt( --i ) );
this.bigEndian && b.reverse();
}
return 1;
};
Buffer.prototype.hasNeededBits = function( neededBits ){
return this.buffer.length >= -( -neededBits >> 3 );
};
BinaryParser.decodeFloat = function decodeFloat (data, precisionBits, exponentBits) {
var b = new this.Buffer(this.bigEndian, data);
Buffer.prototype.checkBuffer = function( neededBits ){
if( !this.hasNeededBits( neededBits ) )
throw new Error( "checkBuffer::missing bytes" );
};
b.checkBuffer(precisionBits + exponentBits + 1);
Buffer.prototype.readBits = function( start, length ){
//shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
function shl( a, b ){
for( ; b--; a = ( ( a %= 0x7fffffff + 1 ) & 0x40000000 ) == 0x40000000 ? a * 2 : ( a - 0x40000000 ) * 2 + 0x7fffffff + 1 );
return a;
}
if( start < 0 || length <= 0 )
return 0;
this.checkBuffer( start + length );
for( var offsetLeft, offsetRight = start % 8, curByte = this.buffer.length - ( start >> 3 ) - 1, lastByte = this.buffer.length + ( -( start + length ) >> 3 ), diff = curByte - lastByte, sum = ( ( this.buffer[ curByte ] >> offsetRight ) & ( ( 1 << ( diff ? 8 - offsetRight : length ) ) - 1 ) ) + ( diff && ( offsetLeft = ( start + length ) % 8 ) ? ( this.buffer[ lastByte++ ] & ( ( 1 << offsetLeft ) - 1 ) ) << ( diff-- << 3 ) - offsetRight : 0 ); diff; sum += shl( this.buffer[ lastByte++ ], ( diff-- << 3 ) - offsetRight ) );
return sum;
};
p.warn = function( msg ){
if( this.allowExceptions )
throw new Error( msg );
return 1;
};
p.decodeFloat = function( data, precisionBits, exponentBits ){
var b = new this.Buffer( this.bigEndian, data );
b.checkBuffer( precisionBits + exponentBits + 1 );
//var bias = Math.pow( 2, exponentBits - 1 ) - 1,
var bias = maxBits[exponentBits - 1] - 1,
signal = b.readBits( precisionBits + exponentBits, 1 ), exponent = b.readBits( precisionBits, exponentBits ), significand = 0,
divisor = 2, curByte = b.buffer.length + ( -precisionBits >> 3 ) - 1;
do{
for( var byteValue = b.buffer[ ++curByte ], startBit = precisionBits % 8 || 8, mask = 1 << startBit; mask >>= 1; ( byteValue & mask ) && ( significand += 1 / divisor ), divisor *= 2 );
}while( precisionBits -= startBit );
var bias = maxBits[exponentBits - 1] - 1
, signal = b.readBits(precisionBits + exponentBits, 1)
, exponent = b.readBits(precisionBits, exponentBits)
, significand = 0
, divisor = 2
, curByte = b.buffer.length + (-precisionBits >> 3) - 1;
do {
for (var byteValue = b.buffer[ ++curByte ], startBit = precisionBits % 8 || 8, mask = 1 << startBit; mask >>= 1; ( byteValue & mask ) && ( significand += 1 / divisor ), divisor *= 2 );
} while (precisionBits -= startBit);
return exponent == ( bias << 1 ) + 1 ? significand ? NaN : signal ? -Infinity : +Infinity : ( 1 + signal * -2 ) * ( exponent || significand ? !exponent ? Math.pow( 2, -bias + 1 ) * significand : Math.pow( 2, exponent - bias ) * ( 1 + significand ) : 0 );
};
p.decodeInt = function( data, bits, signed, forceBigEndian ){
var b = new this.Buffer( this.bigEndian||forceBigEndian, data ), x = b.readBits( 0, bits ), max = maxBits[bits]; //max = Math.pow( 2, bits );
return signed && x >= max / 2 ? x - max : x;
BinaryParser.decodeInt = function decodeInt (data, bits, signed, forceBigEndian) {
var b = new this.Buffer(this.bigEndian || forceBigEndian, data)
, x = b.readBits(0, bits)
, max = maxBits[bits]; //max = Math.pow( 2, bits );
return signed && x >= max / 2
? x - max
: x;
};
p.encodeFloat = function( data, precisionBits, exponentBits ){
//var bias = Math.pow( 2, exponentBits - 1 ) - 1,
var bias = maxBits[exponentBits - 1] - 1,
minExp = -bias + 1, maxExp = bias, minUnnormExp = minExp - precisionBits,
status = isNaN( n = parseFloat( data ) ) || n == -Infinity || n == +Infinity ? n : 0,
exp = 0, len = 2 * bias + 1 + precisionBits + 3, bin = new Array( len ),
signal = ( n = status !== 0 ? 0 : n ) < 0, n = Math.abs( n ), intPart = Math.floor( n ), floatPart = n - intPart,
i, lastBit, rounded, j, result;
for( i = len; i; bin[--i] = 0 );
for( i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor( intPart / 2 ) );
for( i = bias + 1; floatPart > 0 && i; ( bin[++i] = ( ( floatPart *= 2 ) >= 1 ) - 0 ) && --floatPart );
for( i = -1; ++i < len && !bin[i]; );
if( bin[( lastBit = precisionBits - 1 + ( i = ( exp = bias + 1 - i ) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - ( exp = minExp - 1 ) ) ) + 1] ){
if( !( rounded = bin[lastBit] ) ){
for( j = lastBit + 2; !rounded && j < len; rounded = bin[j++] );
BinaryParser.encodeFloat = function encodeFloat (data, precisionBits, exponentBits) {
var bias = maxBits[exponentBits - 1] - 1
, minExp = -bias + 1
, maxExp = bias
, minUnnormExp = minExp - precisionBits
, n = parseFloat(data)
, status = isNaN(n) || n == -Infinity || n == +Infinity ? n : 0
, exp = 0
, len = 2 * bias + 1 + precisionBits + 3
, bin = new Array(len)
, signal = (n = status !== 0 ? 0 : n) < 0
, intPart = Math.floor(n = Math.abs(n))
, floatPart = n - intPart
, lastBit
, rounded
, result
, i
, j;
for (i = len; i; bin[--i] = 0);
for (i = bias + 2; intPart && i; bin[--i] = intPart % 2, intPart = Math.floor(intPart / 2));
for (i = bias + 1; floatPart > 0 && i; (bin[++i] = ((floatPart *= 2) >= 1) - 0 ) && --floatPart);
for (i = -1; ++i < len && !bin[i];);
if (bin[(lastBit = precisionBits - 1 + (i = (exp = bias + 1 - i) >= minExp && exp <= maxExp ? i + 1 : bias + 1 - (exp = minExp - 1))) + 1]) {
if (!(rounded = bin[lastBit])) {
for (j = lastBit + 2; !rounded && j < len; rounded = bin[j++]);
}
for( j = lastBit + 1; rounded && --j >= 0; ( bin[j] = !bin[j] - 0 ) && ( rounded = 0 ) );
for (j = lastBit + 1; rounded && --j >= 0; (bin[j] = !bin[j] - 0) && (rounded = 0));
}
for( i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i]; );
if( ( exp = bias + 1 - i ) >= minExp && exp <= maxExp )
for (i = i - 2 < 0 ? -1 : i - 3; ++i < len && !bin[i];);
if ((exp = bias + 1 - i) >= minExp && exp <= maxExp) {
++i;
else if( exp < minExp ){
exp != bias + 1 - len && exp < minUnnormExp && this.warn( "encodeFloat::float underflow" );
i = bias + 1 - ( exp = minExp - 1 );
} else if (exp < minExp) {
exp != bias + 1 - len && exp < minUnnormExp && this.warn("encodeFloat::float underflow");
i = bias + 1 - (exp = minExp - 1);
}
if( intPart || status !== 0 ){
this.warn( intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status );
if (intPart || status !== 0) {
this.warn(intPart ? "encodeFloat::float overflow" : "encodeFloat::" + status);
exp = maxExp + 1;
i = bias + 2;
if( status == -Infinity )
if (status == -Infinity) {
signal = 1;
else if( isNaN( status ) )
} else if (isNaN(status)) {
bin[i] = 1;
}
}
for( n = Math.abs( exp + bias ), j = exponentBits + 1, result = ""; --j; result = ( n % 2 ) + result, n = n >>= 1 );
for( n = 0, j = 0, i = ( result = ( signal ? "1" : "0" ) + result + bin.slice( i, i + precisionBits ).join( "" ) ).length, r = []; i; j = ( j + 1 ) % 8 ){
n += ( 1 << j ) * result.charAt( --i );
if( j == 7 ){
r[r.length] = String.fromCharCode( n );
for (n = Math.abs(exp + bias), j = exponentBits + 1, result = ""; --j; result = (n % 2) + result, n = n >>= 1);
for (n = 0, j = 0, i = (result = (signal ? "1" : "0") + result + bin.slice(i, i + precisionBits).join("")).length, r = []; i; j = (j + 1) % 8) {
n += (1 << j) * result.charAt(--i);
if (j == 7) {
r[r.length] = String.fromCharCode(n);
n = 0;
}
}
r[r.length] = n ? String.fromCharCode( n ) : "";
return ( this.bigEndian ? r.reverse() : r ).join( "" );
r[r.length] = n
? String.fromCharCode(n)
: "";
return (this.bigEndian ? r.reverse() : r).join("");
};
p.encodeInt = function( data, bits, signed, forceBigEndian ){
//var max = Math.pow( 2, bits );
BinaryParser.encodeInt = function encodeInt (data, bits, signed, forceBigEndian) {
var max = maxBits[bits];
( data >= max || data < -( max / 2 ) ) && this.warn( "encodeInt::overflow" ) && ( data = 0 );
data < 0 && ( data += max );
for( var r = []; data; r[r.length] = String.fromCharCode( data % 256 ), data = Math.floor( data / 256 ) );
for( bits = -( -bits >> 3 ) - r.length; bits--; r[r.length] = "\0" );
return ( (this.bigEndian||forceBigEndian) ? r.reverse() : r ).join( "" );
if (data >= max || data < -(max / 2)) {
this.warn("encodeInt::overflow");
data = 0;
}
if (data < 0) {
data += max;
}
for (var r = []; data; r[r.length] = String.fromCharCode(data % 256), data = Math.floor(data / 256));
for (bits = -(-bits >> 3) - r.length; bits--; r[r.length] = "\0");
return ((this.bigEndian || forceBigEndian) ? r.reverse() : r).join("");
};
p.toSmall = function( data ){ return this.decodeInt( data, 8, true ); };
p.fromSmall = function( data ){ return this.encodeInt( data, 8, true ); };
p.toByte = function( data ){ return this.decodeInt( data, 8, false ); };
p.fromByte = function( data ){ return this.encodeInt( data, 8, false ); };
p.toShort = function( data ){ return this.decodeInt( data, 16, true ); };
p.fromShort = function( data ){ return this.encodeInt( data, 16, true ); };
p.toWord = function( data ){ return this.decodeInt( data, 16, false ); };
p.fromWord = function( data ){ return this.encodeInt( data, 16, false ); };
p.toInt = function( data ){ return this.decodeInt( data, 32, true ); };
p.fromInt = function( data ){ return this.encodeInt( data, 32, true ); };
p.toLong = function( data ){ return this.decodeInt( data, 64, true ); };
p.fromLong = function( data ){ return this.encodeInt( data, 64, true ); };
p.toDWord = function( data ){ return this.decodeInt( data, 32, false ); };
p.fromDWord = function( data ){ return this.encodeInt( data, 32, false ); };
p.toQWord = function( data ){ return this.decodeInt( data, 64, true ); };
p.fromQWord = function( data ){ return this.encodeInt( data, 64, true ); };
p.toFloat = function( data ){ return this.decodeFloat( data, 23, 8 ); };
p.fromFloat = function( data ){ return this.encodeFloat( data, 23, 8 ); };
p.toDouble = function( data ){ return this.decodeFloat( data, 52, 11 ); };
p.fromDouble = function( data ){ return this.encodeFloat( data, 52, 11 ); };
BinaryParser.toSmall = function( data ){ return this.decodeInt( data, 8, true ); };
BinaryParser.fromSmall = function( data ){ return this.encodeInt( data, 8, true ); };
BinaryParser.toByte = function( data ){ return this.decodeInt( data, 8, false ); };
BinaryParser.fromByte = function( data ){ return this.encodeInt( data, 8, false ); };
BinaryParser.toShort = function( data ){ return this.decodeInt( data, 16, true ); };
BinaryParser.fromShort = function( data ){ return this.encodeInt( data, 16, true ); };
BinaryParser.toWord = function( data ){ return this.decodeInt( data, 16, false ); };
BinaryParser.fromWord = function( data ){ return this.encodeInt( data, 16, false ); };
BinaryParser.toInt = function( data ){ return this.decodeInt( data, 32, true ); };
BinaryParser.fromInt = function( data ){ return this.encodeInt( data, 32, true ); };
BinaryParser.toLong = function( data ){ return this.decodeInt( data, 64, true ); };
BinaryParser.fromLong = function( data ){ return this.encodeInt( data, 64, true ); };
BinaryParser.toDWord = function( data ){ return this.decodeInt( data, 32, false ); };
BinaryParser.fromDWord = function( data ){ return this.encodeInt( data, 32, false ); };
BinaryParser.toQWord = function( data ){ return this.decodeInt( data, 64, true ); };
BinaryParser.fromQWord = function( data ){ return this.encodeInt( data, 64, true ); };
BinaryParser.toFloat = function( data ){ return this.decodeFloat( data, 23, 8 ); };
BinaryParser.fromFloat = function( data ){ return this.encodeFloat( data, 23, 8 ); };
BinaryParser.toDouble = function( data ){ return this.decodeFloat( data, 52, 11 ); };
BinaryParser.fromDouble = function( data ){ return this.encodeFloat( data, 52, 11 ); };
// Factor out the encode so it can be shared by add_header and push_int32
p.encode_int32 = function(number) {
BinaryParser.encode_int32 = function encode_int32 (number) {
var a, b, c, d, unsigned;

@@ -164,3 +191,3 @@ unsigned = (number < 0) ? (number + 0x100000000) : number;

p.encode_int64 = function(number) {
BinaryParser.encode_int64 = function encode_int64 (number) {
var a, b, c, d, e, f, g, h, unsigned;

@@ -187,82 +214,182 @@ unsigned = (number < 0) ? (number + 0x10000000000000000) : number;

/**
UTF8 methods
**/
* UTF8 methods
*/
// Take a raw binary string and return a utf8 string
p.decode_utf8 = function(a) {
var string = "";
var i = 0;
var c, c1, c2, c3;
c = c1 = c2 = 0;
BinaryParser.decode_utf8 = function decode_utf8 (binaryStr) {
var len = binaryStr.length
, decoded = ''
, i = 0
, c = 0
, c1 = 0
, c2 = 0
, c3;
while ( i < a.length ) {
c = a.charCodeAt(i);
while (i < len) {
c = binaryStr.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
decoded += String.fromCharCode(c);
i++;
} else if((c > 191) && (c < 224)) {
c2 = a.charCodeAt(i+1);
string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
} else if ((c > 191) && (c < 224)) {
c2 = binaryStr.charCodeAt(i+1);
decoded += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
c2 = a.charCodeAt(i+1);
c3 = a.charCodeAt(i+2);
string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
c2 = binaryStr.charCodeAt(i+1);
c3 = binaryStr.charCodeAt(i+2);
decoded += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return string;
return decoded;
};
// Encode a cstring correctly
p.encode_cstring = function(s) {
return unescape(encodeURIComponent(s)) + p.fromByte(0);
// Encode a cstring
BinaryParser.encode_cstring = function encode_cstring (s) {
return unescape(encodeURIComponent(s)) + BinaryParser.fromByte(0);
};
// Take a utf8 string and return a binary string
p.encode_utf8 = function(s) {
var a="";
for (var n=0; n< s.length; n++) {
var c=s.charCodeAt(n);
if (c<128) {
BinaryParser.encode_utf8 = function encode_utf8 (s) {
var a = ""
, c;
for (var n = 0, len = s.length; n < len; n++) {
c = s.charCodeAt(n);
if (c < 128) {
a += String.fromCharCode(c);
} else if ((c>127)&&(c<2048)) {
a += String.fromCharCode( (c>>6) | 192) ;
a += String.fromCharCode( (c&63) | 128);
} else if ((c > 127) && (c < 2048)) {
a += String.fromCharCode((c>>6) | 192) ;
a += String.fromCharCode((c&63) | 128);
} else {
a += String.fromCharCode( (c>>12) | 224);
a += String.fromCharCode( ((c>>6) & 63) | 128);
a += String.fromCharCode( (c&63) | 128);
a += String.fromCharCode((c>>12) | 224);
a += String.fromCharCode(((c>>6) & 63) | 128);
a += String.fromCharCode((c&63) | 128);
}
}
return a;
};
p.hprint = function(s) {
for (var i=0; i<s.length; i++) {
if (s.charCodeAt(i)<32) {
var number = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(16) : s.charCodeAt(i).toString(16);
sys.debug(number+' : ');}
else {
var number = s.charCodeAt(i) <= 15 ? "0" + s.charCodeAt(i).toString(16) : s.charCodeAt(i).toString(16);
sys.debug(number+' : '+ s.charAt(i));}
BinaryParser.hprint = function hprint (s) {
var number;
for (var i = 0, len = s.length; i < len; i++) {
if (s.charCodeAt(i) < 32) {
number = s.charCodeAt(i) <= 15
? "0" + s.charCodeAt(i).toString(16)
: s.charCodeAt(i).toString(16);
process.stdout.write(number + " ")
} else {
number = s.charCodeAt(i) <= 15
? "0" + s.charCodeAt(i).toString(16)
: s.charCodeAt(i).toString(16);
process.stdout.write(number + " ")
}
}
process.stdout.write("\n\n");
};
p.to_byte_array = function(s) {
var array = [];
for (var i=0; i<s.length; i++) {
if (s.charCodeAt(i)<32) {array.push(s.charCodeAt(i));}
else {array.push(s.charCodeAt(i))}
}
sys.puts(array);
}
BinaryParser.ilprint = function hprint (s) {
var number;
p.pprint = function(s) {
for (var i=0; i<s.length; i++) {
if (s.charCodeAt(i)<32) {sys.puts(s.charCodeAt(i)+' : ');}
else {sys.puts(s.charCodeAt(i)+' : '+ s.charAt(i));}
for (var i = 0, len = s.length; i < len; i++) {
if (s.charCodeAt(i) < 32) {
number = s.charCodeAt(i) <= 15
? "0" + s.charCodeAt(i).toString(10)
: s.charCodeAt(i).toString(10);
sys.debug(number+' : ');
} else {
number = s.charCodeAt(i) <= 15
? "0" + s.charCodeAt(i).toString(10)
: s.charCodeAt(i).toString(10);
sys.debug(number+' : '+ s.charAt(i));
}
}
};
BinaryParser.hlprint = function hprint (s) {
var number;
for (var i = 0, len = s.length; i < len; i++) {
if (s.charCodeAt(i) < 32) {
number = s.charCodeAt(i) <= 15
? "0" + s.charCodeAt(i).toString(16)
: s.charCodeAt(i).toString(16);
sys.debug(number+' : ');
} else {
number = s.charCodeAt(i) <= 15
? "0" + s.charCodeAt(i).toString(16)
: s.charCodeAt(i).toString(16);
sys.debug(number+' : '+ s.charAt(i));
}
}
};
/**
* BinaryParser buffer constructor.
*/
function BinaryParserBuffer (bigEndian, buffer) {
this.bigEndian = bigEndian || 0;
this.buffer = [];
this.setBuffer(buffer);
};
BinaryParserBuffer.prototype.setBuffer = function setBuffer (data) {
var l, i, b;
if (data) {
i = l = data.length;
b = this.buffer = new Array(l);
for (; i; b[l - i] = data.charCodeAt(--i));
this.bigEndian && b.reverse();
}
};
BinaryParserBuffer.prototype.hasNeededBits = function hasNeededBits (neededBits) {
return this.buffer.length >= -(-neededBits >> 3);
};
BinaryParserBuffer.prototype.checkBuffer = function checkBuffer (neededBits) {
if (!this.hasNeededBits(neededBits)) {
throw new Error("checkBuffer::missing bytes");
}
};
BinaryParserBuffer.prototype.readBits = function readBits (start, length) {
//shl fix: Henri Torgemane ~1996 (compressed by Jonas Raoni)
function shl (a, b) {
for (; b--; a = ((a %= 0x7fffffff + 1) & 0x40000000) == 0x40000000 ? a * 2 : (a - 0x40000000) * 2 + 0x7fffffff + 1);
return a;
}
if (start < 0 || length <= 0) {
return 0;
}
this.checkBuffer(start + length);
var offsetLeft
, offsetRight = start % 8
, curByte = this.buffer.length - ( start >> 3 ) - 1
, lastByte = this.buffer.length + ( -( start + length ) >> 3 )
, diff = curByte - lastByte
, sum = ((this.buffer[ curByte ] >> offsetRight) & ((1 << (diff ? 8 - offsetRight : length)) - 1)) + (diff && (offsetLeft = (start + length) % 8) ? (this.buffer[lastByte++] & ((1 << offsetLeft) - 1)) << (diff-- << 3) - offsetRight : 0);
for(; diff; sum += shl(this.buffer[lastByte++], (diff-- << 3) - offsetRight));
return sum;
};
/**
* Expose.
*/
exports.BinaryParser = BinaryParser;
BinaryParser.Buffer = BinaryParserBuffer;

@@ -1,28 +0,43 @@

var InsertCommand = require('./commands/insert_command').InsertCommand,
QueryCommand = require('./commands/query_command').QueryCommand,
DeleteCommand = require('./commands/delete_command').DeleteCommand,
UpdateCommand = require('./commands/update_command').UpdateCommand,
DbCommand = require('./commands/db_command').DbCommand,
BinaryParser = require('./bson/binary_parser').BinaryParser,
Cursor = require('./cursor').Cursor;
/**
* Module dependencies.
*/
var InsertCommand = require('./commands/insert_command').InsertCommand
, QueryCommand = require('./commands/query_command').QueryCommand
, DeleteCommand = require('./commands/delete_command').DeleteCommand
, UpdateCommand = require('./commands/update_command').UpdateCommand
, DbCommand = require('./commands/db_command').DbCommand
, BinaryParser = require('./bson/binary_parser').BinaryParser
, Cursor = require('./cursor').Cursor
, debug = require('util').debug
, inspect = require('util').inspect;
/**
* Sort functions, Normalize and prepare sort parameters
*/
/**
Sort functions, Normalize and prepare sort parameters
**/
var formatSortValue = function(sortDirection) {
function formatSortValue (sortDirection) {
var value = ("" + sortDirection).toLowerCase();
if(value == 'ascending' || value == 'asc' || value == 1) return 1;
if(value == 'descending' || value == 'desc' || value == -1 ) return -1;
throw new Error("Illegal sort clause, must be of the form " +
"[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]");
switch (value) {
case 'ascending':
case 'asc':
case '1':
return 1;
case 'descending':
case 'desc':
case '-1':
return -1;
default:
throw new Error("Illegal sort clause, must be of the form "
+ "[['field1', '(ascending|descending)'], "
+ "['field2', '(ascending|descending)']]");
}
};
var formattedOrderClause = function(sortValue) {
function formattedOrderClause (sortValue) {
var orderBy = {};
var self = this;
if(Array.isArray(sortValue)) {
sortValue.forEach(function(sortElement) {
if(sortElement.constructor == String) {
if (Array.isArray(sortValue)) {
sortValue.forEach(function (sortElement) {
if (sortElement.constructor == String) {
orderBy[sortElement] = 1;

@@ -33,3 +48,3 @@ } else {

});
} else if(sortValue.constructor == String) {
} else if (sortValue.constructor == String) {
orderBy[sortValue] = 1;

@@ -40,2 +55,3 @@ } else {

}
return orderBy;

@@ -45,158 +61,287 @@ };

/**
Handles all the operations on objects in collections
**/
var Collection = exports.Collection = function(db, collectionName, pkFactory) {
* toString helper.
*/
var toString = Object.prototype.toString;
/**
* Collection constructor.
*
* @param {Database} db
* @param {String} collectionName
* @param {Function} pkFactory
*/
function Collection (db, collectionName, pkFactory, options) {
this.checkCollectionName(collectionName);
this.db = db;
this.collectionName = collectionName;
this.internalHint;
this.pkFactory = pkFactory == null ? db.bson_serializer.ObjectID : pkFactory;
// Add getter and setters
this.__defineGetter__("hint", function() { return this.internalHint; });
this.__defineSetter__("hint", function(value) { this.internalHint = this.normalizeHintField(value); });
// Ensure the collection name is not illegal
this.checkCollectionName(collectionName);
this.opts = options != null && ('object' === typeof options) ? options : {};
this.slaveOk = options == null || options.slaveOk == null ? db.slaveOk : options.slaveOk;
this.pkFactory = pkFactory == null
? db.bson_serializer.ObjectID
: pkFactory;
Object.defineProperty(this, "hint", {
enumerable: true
, get: function () {
return this.internalHint;
}
, set: function (v) {
this.internalHint = this.normalizeHintField(v);
}
});
};
Collection.prototype.insert = function(docs, options, callback) {
var args = Array.prototype.slice.call(arguments, 1);
callback = args.pop();
if(Object.prototype.toString.call(callback) != '[object Function]') {
args.push(callback)
callback = null
}
options = args.length ? args.shift() : {};
/**
* Inserts `docs` into the db.
*
* @param {Array|Object} docs
* @param {Object} options (optional)
* @param {Function} callback (optional)
* @return {Collection}
*/
Collection.prototype.insert = function insert (docs, options, callback) {
if ('function' === typeof options) callback = options, options = {};
if(options == null) options = {};
if(!('function' === typeof callback)) callback = null;
this.insertAll(Array.isArray(docs) ? docs : [docs], options, callback);
return this;
};
Collection.prototype.checkCollectionName = function(collectionName) {
if (typeof collectionName != 'string')
/**
* Checks if `collectionName` is valid.
*
* @param {String} collectionName
*/
Collection.prototype.checkCollectionName = function checkCollectionName (collectionName) {
if ('string' !== typeof collectionName) {
throw Error("collection name must be a String");
}
if (!collectionName || collectionName.indexOf('..') != -1)
if (!collectionName || collectionName.indexOf('..') != -1) {
throw Error("collection names cannot be empty");
}
if (collectionName.indexOf('$') != -1 && collectionName.match(/((^\$cmd)|(oplog\.\$main))/) == null)
if (collectionName.indexOf('$') != -1 &&
collectionName.match(/((^\$cmd)|(oplog\.\$main))/) == null) {
throw Error("collection names must not contain '$'");
}
if (collectionName.match(/^\.|\.$/) != null)
if (collectionName.match(/^\.|\.$/) != null) {
throw Error("collection names must not start or end with '.'");
}
};
Collection.prototype.remove = function(selector, options, callback) {
var args = Array.prototype.slice.call(arguments, 0);
callback = args.pop();
var removeSelector = args.length ? args.shift() : {};
options = args.length ? args.shift() : {};
/**
* Removes documents specified by `selector` from the db.
* @param {Object} selector (optional)
* @param {Object} options (optional)
* @param {Function} callback (optional)
*/
// Generate selector for remove all if not available
var deleteCommand = new DeleteCommand(this.db, this.db.databaseName + "." + this.collectionName, removeSelector);
// Execute the command, do not add a callback as it's async
var result = this.db.executeCommand(deleteCommand);
if(result instanceof Error) {
if(callback != null) return callback(result, null);
Collection.prototype.remove = function remove (selector, options, callback) {
if ('function' === typeof selector) {
callback = selector;
selector = options = {};
} else if ('function' === typeof options) {
callback = options;
options = {};
}
// If safe mode check last error
if(callback != null) {
if(options.safe || this.db.strict) {
this.db.error(function(err, error) {
if(error[0].err) {
callback(new Error(error[0].err), null);
} else {
callback(null, null);
}
});
} else {
// Callback with no error check
callback(null, this);
// Ensure options
if(options == null) options = {};
if(!('function' === typeof callback)) callback = null;
var deleteCommand = new DeleteCommand(
this.db
, this.db.databaseName + "." + this.collectionName
, selector);
var errorOptions = options.safe != null ? options.safe : null;
errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
errorOptions = errorOptions == null && this.db.strict != null ? this.db.strict : errorOptions;
// Execute the command, do not add a callback as it's async
if (options && options.safe || this.opts.safe != null || this.db.strict) {
// Insert options
var commandOptions = {read:false};
// If we have safe set set async to false
if(errorOptions == null) commandOptions['async'] = true;
// Set safe option
commandOptions['safe'] = true;
// If we have an error option
if(typeof errorOptions == 'object') {
var keys = Object.keys(errorOptions);
for(var i = 0; i < keys.length; i++) {
commandOptions[keys[i]] = errorOptions[keys[i]];
}
}
// Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection)
this.db.executeCommand(deleteCommand, commandOptions, function (err, error) {
error = error && error.documents;
if(!callback) return;
if (err) {
callback(err);
} else if (error[0].err) {
callback(new Error(error[0].err));
} else {
callback(null, error[0].n);
}
});
} else {
var result = this.db.executeCommand(deleteCommand);
// If no callback just return
if (!callback) return;
// If error return error
if (result instanceof Error) {
return callback(result);
}
// Otherwise just return
return callback();
}
};
Collection.prototype.rename = function(collectionName, callback) {
/**
* Renames the collection.
*
* @param {String} newName
* @param {Function} callback
*/
Collection.prototype.rename = function rename (newName, callback) {
var self = this;
try {
this.checkCollectionName(collectionName);
this.db.renameCollection(this.collectionName, collectionName, function(err, result) {
if(err != null || result.documents[0].ok == 0) {
err != null ? callback(err, null) : callback(new Error(result.documents[0].errmsg), null);
this.checkCollectionName(newName);
this.db.renameCollection(this.collectionName, newName, function (err, result) {
if (err) {
callback(err);
} else if (result.documents[0].ok == 0) {
callback(new Error(result.documents[0].errmsg));
} else {
// Set collectionname to new one and return the collection
self.db.collection(collectionName, callback);
self.db.collection(newName, callback);
}
});
} catch(err) {
callback(err, null);
} catch (err) {
callback(err);
}
};
Collection.prototype.insertAll = function(docs, options, callback) {
var self = this;
var error= null;
var args = Array.prototype.slice.call(arguments, 1);
callback = args.pop();
/**
* Insert many docs into the db.
*
* @param {Array} docs
* @param {Object} options (optional)
* @param {Function} callback (optional)
*/
if(Object.prototype.toString.call(callback) != '[object Function]') {
args.push(callback)
callback = null
}
Collection.prototype.insertAll = function insertAll (docs, options, callback) {
if('function' === typeof options) callback = options, options = {};
if(options == null) options = {};
if(!('function' === typeof callback)) callback = null;
options = args.length ? args.shift() : {};
var insertCommand = new InsertCommand(
this.db
, this.db.databaseName + "." + this.collectionName);
// List of all id's inserted
var objects = [];
// Create an insert command
var insertCommand = new InsertCommand(this.db, this.db.databaseName + "." + this.collectionName);
// Add id to each document if it's not already defined
for(var index = 0; index < docs.length; index++) {
// Add the documents and decorate them with id's if they have none
for (var index = 0, len = docs.length; index < len; ++index) {
var doc = docs[index];
// Add the id to the document
var id = doc["_id"] == null ? this.pkFactory.createPk() : doc["_id"];
doc['_id'] = id;
// Insert the document
// Add id to each document if it's not already defined
if (!doc['_id'] && this.db.forceServerObjectId != true) {
doc['_id'] = this.pkFactory.createPk();
}
insertCommand.add(doc);
objects.push(doc);
}
// Execute the command
var result = this.db.executeCommand(insertCommand);
if(result instanceof Error) {
if(callback != null) return callback(result, null);
}
// Collect errorOptions
var errorOptions = options.safe != null ? options.safe : null;
errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
errorOptions = errorOptions == null && this.db.strict != null ? this.db.strict : errorOptions;
// If safe is defined check for error message
if(callback != null) {
if(options.safe || this.db.strict) {
this.db.error(function(err, error) {
if(error[0].err) {
if(callback != null) callback(new Error(error[0].err), null);
} else {
if(callback != null) callback(null, objects);
}
});
} else {
if(callback != null) callback(null, objects);
// if(options != null && (options.safe == true || this.db.strict == true || this.opts.safe == true)) {
if(errorOptions) {
// Insert options
var commandOptions = {read:false};
// If we have safe set set async to false
if(errorOptions == null) commandOptions['async'] = true;
// Set safe option
commandOptions['safe'] = true;
// If we have an error option
if(typeof errorOptions == 'object') {
var keys = Object.keys(errorOptions);
for(var i = 0; i < keys.length; i++) {
commandOptions[keys[i]] = errorOptions[keys[i]];
}
}
// Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection)
this.db.executeCommand(insertCommand, commandOptions, function (err, error) {
error = error && error.documents;
if(!callback) return;
if (err) {
callback(err);
} else if (error[0].err) {
callback(new Error(error[0].err));
} else {
callback(null, docs);
}
});
} else {
var result = this.db.executeCommand(insertCommand);
// If no callback just return
if(!callback) return;
// If error return error
if(result instanceof Error) {
return callback(result);
}
// Otherwise just return
return callback(null, docs);
}
};
Collection.prototype.save = function(doc, options, callback) {
/**
* Save a document.
*
* @param {Object} doc
* @param {Object} options (optional)
* @param {Function} callback (optional)
*/
Collection.prototype.save = function save (doc, options, callback) {
var args = Array.prototype.slice.call(arguments, 1);
callback = args.pop();
options = args.length ? args.shift() : {};
if(Object.prototype.toString.call(callback) != '[object Function]') {
args.push(callback)
callback = null
}
options = args.length ? args.shift() : null;
var errorOptions = options.safe != null ? options.safe : false;
errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
var id = doc['_id'];
if(id != null) {
this.update({'_id':id}, doc, {upsert: true, safe: options != null ? options.safe : false}, callback);
if (id) {
this.update({ _id: id }, doc, { upsert: true, safe: errorOptions }, callback);
} else {
this.insert(doc, {safe: options != null ? options.safe : false}, function(err, docs) { Array.isArray(docs) ? callback(err, docs[0]) : callback(err, docs); });
this.insert(doc, { safe: errorOptions }, callback && function (err, docs) {
if (err) return callback(err, null);
if (Array.isArray(docs)) {
callback(err, docs[0]);
} else {
callback(err, docs);
}
});
}

@@ -206,59 +351,73 @@ };

/**
Update a single document in this collection.
spec - a associcated array containing the fields that need to be present in
the document for the update to succeed
* Updates documents.
*
* By default updates only the first found doc. To update all matching
* docs in the db, set options.multi to true.
*
* @param {Object} selector
* @param {Object} document - the fields/vals to be updated, or in the case of
* an upsert operation, inserted.
* @param {Object} options (optional)
* upsert - {bool} perform an upsert operation
* multi - {bool} update all documents matching the selector
* safe - {bool} check if the update failed (requires extra call to db)
* @param {Function} callback (optional)
*/
document - an associated array with the fields to be updated or in the case of
a upsert operation the fields to be inserted.
Collection.prototype.update = function update (selector, document, options, callback) {
if('function' === typeof options) callback = options, options = null;
if(options == null) options = {};
if(!('function' === typeof callback)) callback = null;
Options:
upsert - true/false (perform upsert operation)
multi - true/false (update all documents matching spec)
safe - true/false (perform check if the operation failed, required extra call to db)
**/
Collection.prototype.update = function(spec, document, options, callback) {
var args = Array.prototype.slice.call(arguments, 2);
callback = args.pop();
var updateCommand = new UpdateCommand(
this.db
, this.db.databaseName + "." + this.collectionName
, selector
, document
, options);
if(Object.prototype.toString.call(callback) != '[object Function]') {
args.push(callback)
callback = null
}
// Unpack the error options if any
var errorOptions = (options && options.safe != null) ? options.safe : null;
errorOptions = errorOptions == null && this.opts.safe != null ? this.opts.safe : errorOptions;
errorOptions = errorOptions == null && this.db.strict != null ? this.db.strict : errorOptions;
options = args.length ? args.shift() : null;
var safe = options == null || options.safe == null || options.safe == false ? false : true;
var upsert = options == null || options.upsert == null || options.upsert == false ? false : true;
// Create update command
var updateCommand = new UpdateCommand(this.db, this.db.databaseName + "." + this.collectionName, spec, document, options);
// Execute command
var result = this.db.executeCommand(updateCommand);;
if(result instanceof Error) {
if(callback != null) return callback(result, null);
}
// If we are executing in strict mode or safe both the update and the safe command must happen on the same line
if(errorOptions) {
// Insert options
var commandOptions = {read:false};
// If we have safe set set async to false
if(errorOptions == null) commandOptions['async'] = true;
// Set safe option
commandOptions['safe'] = true;
// If we have an error option
if(typeof errorOptions == 'object') {
var keys = Object.keys(errorOptions);
for(var i = 0; i < keys.length; i++) {
commandOptions[keys[i]] = errorOptions[keys[i]];
}
}
// If safe, we need to check for successful execution
if(safe || this.db.strict) {
this.db.error(function(err, error) {
if(callback != null) {
if (upsert) {
// Document was either inserted or updated, simply pass on the error if one occurred.
if (error[0].err) {
if(callback != null) callback(new Error(error[0].err), null);
} else {
// Add the new id for the document and return
if(callback != null) callback(null, error[0].n);
}
} else {
// If another kind of error occurred then report it to the callback function.
if (error[0].err) {
if(callback != null) callback(new Error(error[0].err), null);
} else {
if(callback != null) callback(null, error[0].n);
}
}
}
});
// Execute command with safe options (rolls up both command and safe command into one and executes them on the same connection)
this.db.executeCommand(updateCommand, commandOptions, function (err, error) {
error = error && error.documents;
if(!callback) return;
if (err) {
callback(err);
} else if (error[0].err) {
callback(new Error(error[0].err));
} else {
callback(null, error[0].n);
}
});
} else {
// Call back with ok if no error found
if(callback != null) callback(null, 1);
var result = this.db.executeCommand(updateCommand);
// If no callback just return
if (!callback) return;
// If error return error
if (result instanceof Error) {
return callback(result);
}
// Otherwise just return
return callback();
}

@@ -268,30 +427,65 @@ };

/**
Fetch a distinct collection
**/
Collection.prototype.distinct = function(key, query, callback) {
var args = Array.prototype.slice.call(arguments, 1);
callback = args.pop();
query = args.length ? args.shift() : {};
* Fetch a distinct collection
* @param {String} key
* @param {Object} query (optional)
* @param {Function} callback (optional)
*/
var mapCommandHash = {distinct:this.collectionName, key:key, query:query};
this.db.executeCommand(DbCommand.createDbCommand(this.db, mapCommandHash), function(err, result) {
if(err == null && result.documents[0].ok == 1) {
callback(null, result.documents[0].values);
} else {
err != null ? callback(err, null) : callback(new Error(result.documents[0].errmsg), null);
Collection.prototype.distinct = function distinct (key, query, callback) {
if ('function' === typeof query) callback = query, query = {};
var mapCommandHash = {
distinct: this.collectionName
, query: query
, key: key
};
var cmd = DbCommand.createDbCommand(this.db, mapCommandHash);
this.db.executeCommand(cmd, {read:true}, function (err, result) {
if (err) {
return callback(err);
}
if (result.documents[0].ok != 1) {
return callback(new Error(result.documents[0].errmsg));
}
callback(null, result.documents[0].values);
});
};
Collection.prototype.count = function(query, callback) {
if(typeof query === "function") { callback = query; query = null; }
var query_object = query == null ? {} : query;
var final_query = {count: this.collectionName, query: query_object, fields: null};
var queryCommand = new QueryCommand(this.db, this.db.databaseName + ".$cmd", QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, final_query, null);
// Execute the command
this.db.executeCommand(queryCommand, function(err, result) {
if(err == null && result.documents[0].ok == 1) {
/**
* Count number of matching documents in the db.
*
* @param {Object} query
* @param {Function} callback
*/
Collection.prototype.count = function count (query, callback) {
if ('function' === typeof query) callback = query, query = {};
var final_query = {
count: this.collectionName
, query: query
, fields: null
};
var queryCommand = new QueryCommand(
this.db
, this.db.databaseName + ".$cmd"
, QueryCommand.OPTS_NO_CURSOR_TIMEOUT
, 0
, -1
, final_query
, null
);
this.db.executeCommand(queryCommand, {read:true}, function (err, result) {
if (err) {
callback(err);
} else if (result.documents[0].ok != 1) {
callback(new Error(result.documents[0].errmsg));
} else {
callback(null, result.documents[0].n);
} else {
err != null ? callback(err, null) : callback(new Error(result.documents[0].errmsg), null);
}

@@ -301,3 +495,9 @@ });

Collection.prototype.drop = function(callback) {
/**
* Drop this collection.
*
* @param {Function} callback
*/
Collection.prototype.drop = function drop (callback) {
this.db.dropCollection(this.collectionName, callback);

@@ -307,45 +507,49 @@ };

/**
Fetch and update a collection
query: a filter for the query
sort: if multiple docs match, choose the first one in the specified sort order as the object to manipulate
update: an object describing the modifications to the documents selected by the query
options:
remove: set to a true to remove the object before returning
new: set to true if you want to return the modified object rather than the original. Ignored for remove.
upsert: true/false (perform upsert operation)
**/
Collection.prototype.findAndModify = function(query, sort, update, options, callback) {
* Find and update a document.
*
* @param {Object} query
* @param {Array} sort - if multiple docs match, choose the first one
* in the specified sort order as the object to manipulate
* @param {Object} doc - the fields/vals to be updated
* @param {Object} options -
* remove: {Bool} set to true to remove the object before returning
* upsert: {Bool} perform an upsert operation
* new: {Bool} set to true if you want to return the modified object
* rather than the original. Ignored for remove.
* @param {Function} callback
*/
Collection.prototype.findAndModify = function findAndModify (query, sort, doc, options, callback) {
var args = Array.prototype.slice.call(arguments, 1);
callback = args.pop();
sort = args.length ? args.shift() : [];
update = args.length ? args.shift() : null;
doc = args.length ? args.shift() : null;
options = args.length ? args.shift() : {};
// Format the sort object
var sort_object = formattedOrderClause(sort);
// Unpack the options
var queryOptions = QueryCommand.OPTS_NONE;
// Build the query object for the findAndModify
var queryObject = {
'findandmodify': this.collectionName,
'query':query,
'sort':sort_object
}
'findandmodify': this.collectionName
, 'query': query
, 'sort': formattedOrderClause(sort)
};
queryObject['new'] = options['new'] ? 1 : 0;
queryObject['remove'] = options.remove ? 1 : 0;
queryObject['upsert'] = options.upsert ? 1 : 0;
if (options.fields) queryObject['fields'] = options.fields;
queryObject.new = options.new ? 1 : 0;
queryObject.remove = options.remove ? 1 : 0;
queryObject.upsert = options.upsert ? 1 : 0;
// Set up the update if it exists
if(update) queryObject['update'] = update;
if (options.fields) {
queryObject.fields = options.fields;
}
// Set up the sort
if(!Array.isArray(sort) && sort.length == 0) queryObject['sort'] = sort_object;
if (doc && !options.remove) {
queryObject.update = doc;
}
if(options.remove) delete queryObject['update'];
// Execute command
this.db.executeDbCommand(queryObject, function(err, doc) {
err ? callback(err, doc) : callback(err, doc.documents[0].value);
this.db.executeDbCommand(queryObject, function (err, result) {
if (err) {
callback(err);
} else if (result.documents[0].ok != 1) {
callback(new Error(result.documents[0].errmsg));
} else {
callback(err, result.documents[0].value)
}
});

@@ -356,2 +560,3 @@ }

* Various argument possibilities
* TODO : combine/reduce # of possibilities
* 1 callback?

@@ -369,13 +574,15 @@ * 2 selector, callback?,

*/
Collection.prototype.find = function() {
var options,
args = Array.prototype.slice.call(arguments, 0),
has_callback = typeof args[args.length - 1] === 'function',
has_weird_callback = typeof args[0] === 'function',
callback = has_callback ? args.pop() : (has_weird_callback ? args.shift() : null),
len = args.length,
selector = (len >= 1) ? args[0] : {},
fields = (len >= 2) ? args[1] : undefined;
if(len == 1 && has_weird_callback) { // backwards compat for callback?, options case
Collection.prototype.find = function find () {
var options
, args = Array.prototype.slice.call(arguments, 0)
, has_callback = typeof args[args.length - 1] === 'function'
, has_weird_callback = typeof args[0] === 'function'
, callback = has_callback ? args.pop() : (has_weird_callback ? args.shift() : null)
, len = args.length
, selector = len >= 1 ? args[0] : {}
, fields = len >= 2 ? args[1] : undefined;
if (len === 1 && has_weird_callback) {
// backwards compat for callback?, options case
selector = {};

@@ -385,21 +592,43 @@ options = args[0];

if(len == 2){ // backwards compat for options object
var test = ['limit','sort','fields','skip','hint','explain','snapshot','timeout','tailable', 'batchSize'],
idx = 0, l = test.length, is_option = false;
while(!is_option && idx < l) if(test[idx] in fields ) is_option = true; else idx++;
options = is_option ? fields : {};
if(is_option) fields = undefined;
if (len === 2) {
// backwards compat for options object
var test = ['limit','sort','fields','skip','hint','explain','snapshot','timeout','tailable', 'batchSize']
, is_option = false;
for (var idx = 0, l = test.length; idx < l; ++idx) {
if (test[idx] in fields) {
is_option = true;
break;
}
}
if (is_option) {
options = fields;
fields = undefined;
} else {
options = {};
}
}
if(len == 3) options = args[2];
if(options && options.fields){
if (3 === len) {
options = args[2];
}
if (options && options.fields) {
fields = {};
if(options.fields.constructor == Array){
if(options.fields.length == 0) fields['_id'] = 1;
else for(var i = 0, l = options.fields.length; i < l; i++) fields[options.fields[i]] = 1;
if (Array.isArray(options.fields)) {
if (!options.fields.length) {
fields['_id'] = 1;
} else {
for (var i = 0, l = options.fields.length; i < l; i++) {
fields[options.fields[i]] = 1;
}
}
} else {
fields = options.fields;
}
else fields = options.fields;
}
if(!options) options = {};
if (!options) options = {};
options.skip = len > 3 ? args[2] : options.skip ? options.skip : 0;

@@ -409,3 +638,4 @@ options.limit = len > 3 ? args[3] : options.limit ? options.limit : 0;

options.timeout = len == 5 ? args[4] : options.timeout ? options.timeout : undefined;
options.slaveOk = options.slaveOk != null ? options.slaveOk : false;
// If we have overridden slaveOk otherwise use the default db setting
options.slaveOk = options.slaveOk != null ? options.slaveOk : this.db.slaveOk;

@@ -416,2 +646,3 @@ var o = options;

if (callback) {
// TODO refactor Cursor args
callback(null, new Cursor(this.db, this, selector, fields, o.skip, o.limit, o.sort, o.hint, o.explain, o.snapshot, o.timeout, o.tailable, o.batchSize, o.slaveOk));

@@ -423,75 +654,172 @@ } else {

Collection.prototype.normalizeHintField = function(hint) {
/**
* Normalizes a `hint` argument.
*
* @param {String|Object|Array} hint
* @return {Object}
*/
Collection.prototype.normalizeHintField = function normalizeHintField (hint) {
var finalHint = null;
// Normalize the hint parameter
if(hint != null && hint.constructor == String) {
finalHint = {};
finalHint[hint] = 1;
} else if(hint != null && hint.constructor == Object) {
finalHint = {};
for(var name in hint) { finalHint[name] = hint[name]; }
} else if(hint != null && hint.constructor == Array) {
finalHint = {};
hint.forEach(function(param) { finalHint[param] = 1; });
if (null != hint) {
switch (hint.constructor) {
case String:
finalHint = {};
finalHint[hint] = 1;
break;
case Object:
finalHint = {};
for (var name in hint) {
finalHint[name] = hint[name];
}
break;
case Array:
finalHint = {};
hint.forEach(function(param) {
finalHint[param] = 1;
});
break;
}
}
return finalHint;
};
Collection.prototype.findOne = function(queryObject, options, callback) {
var args = Array.prototype.slice.call(arguments, 0);
callback = args.pop();
queryObject = args.length ? args.shift() : {};
options = args.length ? args.shift() : {};
/**
* Finds one document.
*
* @param {Object} queryObject
* @param {Object} options
* @param {Function} callback
*/
var fields = options.fields;
// Normalize fields filtering
if(options && options.fields){
Collection.prototype.findOne = function findOne (queryObject, options, callback) {
if ('function' === typeof queryObject) {
callback = queryObject;
queryObject = {};
options = {};
} else if ('function' === typeof options) {
callback = options;
options = {};
}
var fields;
if (options.fields && Array.isArray(options.fields)) {
fields = {};
if(options.fields.constructor == Array){
if(options.fields.length == 0) fields['_id'] = 1;
else for(var i = 0, l = options.fields.length; i < l; i++) fields[options.fields[i]] = 1;
if (0 === options.fields.length) {
fields['_id'] = 1;
} else {
for (var i = 0, len = options.fields.length; i < len; ++i) {
fields[options.fields[i]] = 1;
}
}
else fields = options.fields;
} else {
fields = options.fields;
}
// Unpack the options
var timeout = options.timeout != null ? options.timeout : QueryCommand.OPTS_NONE;
var queryOptions = timeout;
// Build final query
var finalQueryObject = queryObject == null ? {} : queryObject;
// Validate the type of query
finalQueryObject = (finalQueryObject instanceof this.db.bson_serializer.ObjectID || Object.prototype.toString.call(finalQueryObject) === '[object ObjectID]') ? {'_id':finalQueryObject} : finalQueryObject;
// Build special selector
var specialSelector = {'query':finalQueryObject};
// Build full collection name
var collectionName = (this.db.databaseName ? this.db.databaseName + "." : '') + this.collectionName;
// Execute the command
var queryCommand = new QueryCommand(this.db, collectionName, queryOptions, 0, 1, specialSelector, fields);
this.db.executeCommand(queryCommand, function(err, result) {
if(!err && result.documents[0] && result.documents[0]['$err']) return callback(result.documents[0]['$err'], null);
callback(err, result.documents[0]);
if (queryObject instanceof this.db.bson_serializer.ObjectID ||
'[object ObjectID]' === toString.call(queryObject)) {
queryObject = { '_id': queryObject };
}
var query = { 'query': queryObject };
// If we have a timeout set
var timeout = null != options.timeout
? options.timeout
: QueryCommand.OPTS_NONE;
// Make sure we can do slaveOk commands
if (options.slaveOk || this.slaveOk || this.db.slaveOk) {
timeout |= QueryCommand.OPTS_SLAVE;
}
var collectionName = (this.db.databaseName ? this.db.databaseName + '.' : '')
+ this.collectionName;
var queryCommand = new QueryCommand(
this.db
, collectionName
, timeout
, 0
, 1
, query
, fields);
this.db.executeCommand(queryCommand, {read:true}, function (err, result) {
if (!err && result.documents[0] && result.documents[0]['$err']) {
return callback(result.documents[0]['$err']);
}
callback(err, result && result.documents && result.documents[0]);
});
};
Collection.prototype.createIndex = function(fieldOrSpec, unique, callback) {
this.db.createIndex(this.collectionName, fieldOrSpec, unique, callback);
/**
* Creates an index on this collection.
*
* @param {Object} fieldOrSpec
* @param {Object} options
* @param {Function} callback
*/
Collection.prototype.createIndex = function createIndex (fieldOrSpec, options, callback) {
this.db.createIndex(this.collectionName, fieldOrSpec, options, callback);
};
Collection.prototype.ensureIndex = function(fieldOrSpec, unique, callback) {
this.db.ensureIndex(this.collectionName, fieldOrSpec, unique, callback);
/**
* Ensures the index exists on this collection.
*
* @param {Object} fieldOrSpec
* @param {Object} options
* @param {Function} callback
*/
Collection.prototype.ensureIndex = function ensureIndex (fieldOrSpec, options, callback) {
this.db.ensureIndex(this.collectionName, fieldOrSpec, options, callback);
};
Collection.prototype.indexInformation = function(callback) {
this.db.indexInformation(this.collectionName, callback);
/**
* Retrieves this collections index info.
*
* @param {Object} options -
* full: {Bool} set to true to remove raw index information
* @param {Function} callback
*/
Collection.prototype.indexInformation = function indexInformation (options, callback) {
// Unpack calls
var args = Array.prototype.slice.call(arguments, 0);
callback = args.pop();
options = args.length ? args.shift() : {};
// Call the index information
this.db.indexInformation(this.collectionName, options, callback);
};
Collection.prototype.dropIndex = function(indexName, callback) {
this.db.dropIndex(this.collectionName, indexName, callback);
/**
* Drops an index from this collection.
*
* @param {String} name
* @param {Function} callback
*/
Collection.prototype.dropIndex = function dropIndex (name, callback) {
this.db.dropIndex(this.collectionName, name, callback);
};
Collection.prototype.dropIndexes = function(callback) {
this.db.dropIndex(this.collectionName, "*", function(err, result) {
if(err == null && result.documents[0].ok == 1) {
/**
* Drops all indexes from this collection.
*
* @param {Function} callback
*/
Collection.prototype.dropIndexes = function dropIndexes (callback) {
this.db.dropIndex(this.collectionName, '*', function (err, result) {
if (err) {
callback(err);
} else if (1 == result.documents[0].ok) {
callback(null, true);
} else {
err != null ? callback(err, null) : callback(new Error("map-reduce failed: " + result.documents[0].errmsg), false);
callback(new Error("map-reduce failed: " + result.documents[0].errmsg), false);
}

@@ -501,49 +829,123 @@ });

Collection.prototype.mapReduce = function(map, reduce, options, callback) {
var args = Array.prototype.slice.call(arguments, 2);
callback = args.pop();
var self = this;
// Parse version of server if available
var version = this.db.version != null ? parseInt(this.db.version.replace(/\./g, '')) : 0;
/**
* Map reduce.
*
* @param {Function|String} map
* @param {Function|String} reduce
* @param {Objects} options
* @param {Function} callback
*/
Collection.prototype.mapReduce = function mapReduce (map, reduce, options, callback) {
if ('function' === typeof options) callback = options, options = {};
// Set default to be inline if we are dealing with a v 1.7.6 > server
if(version > 0 && version > 176) {
options = args.length ? args.shift() : {out:'inline'};
if(options.out == null) options.out = 'inline';
var version = 'string' === typeof this.db.version
? parseInt(this.db.version.replace(/\./g, ''))
: 0;
if (version > 0 && version > 176) {
if (null == options.out) options.out = 'inline';
}
if(Object.prototype.toString.call(map) === '[object Function]') map = map.toString();
if(Object.prototype.toString.call(reduce) === '[object Function]') reduce = reduce.toString();
// Build command object for execution
var mapCommandHash = {mapreduce:this.collectionName, map:map, reduce:reduce};
if ('function' === typeof map) {
map = map.toString();
}
if ('function' === typeof reduce) {
reduce = reduce.toString();
}
if ('function' === typeof options.finalize) {
options.finalize = options.finalize.toString();
}
var mapCommandHash = {
mapreduce: this.collectionName
, map: map
, reduce: reduce
};
// Add any other options passed in
for(var name in options) {
for (var name in options) {
mapCommandHash[name] = options[name];
}
// Execute command against server
this.db.executeCommand(DbCommand.createDbCommand(this.db, mapCommandHash), function(err, result) {
if(err == null && result.documents[0].ok == 1) {
//return the results, if the map/reduce is invoked with inline option
if(result.documents[0].results) {
return callback(err, result.documents[0].results);
var self = this;
var cmd = DbCommand.createDbCommand(this.db, mapCommandHash);
this.db.executeCommand(cmd, {read:true}, function (err, result) {
if (err) {
return callback(err);
}
if (1 != result.documents[0].ok) {
return callback(result.documents[0]);
}
// invoked with inline?
if (result.documents[0].results) {
return callback(null, result.documents[0].results);
}
// Create a collection object that wraps the result collection
self.db.collection(result.documents[0].result, function (err, collection) {
if (!options.include_statistics) {
return callback(err, collection);
}
// Create a collection object that wraps the result collection
self.db.collection(result.documents[0].result, function(err, collection) {
if(options.include_statistics) {
var stats = {
processtime: result.documents[0].timeMillis,
counts: result.documents[0].counts
};
callback(err, collection, stats);
} else {
callback(err, collection);
}
});
} else {
err != null ? callback(err, null, null) : callback(result.documents[0], null, null);
}
var stats = {
processtime: result.documents[0].timeMillis
, counts: result.documents[0].counts
};
callback(err, collection, stats);
});
});
};
Collection.prototype.group = function(keys, condition, initial, reduce, command, callback) {
/**
* Group function helper
*/
var groupFunction = function () {
var c = db[ns].find(condition);
var map = new Map();
var reduce_function = reduce;
while (c.hasNext()) {
var obj = c.next();
var key = {};
for (var i = 0, len = keys.length; i < len; ++i) {
var k = keys[i];
key[k] = obj[k];
}
var aggObj = map.get(key);
if (aggObj == null) {
var newObj = Object.extend({}, key);
aggObj = Object.extend(newObj, initial);
map.put(key, aggObj);
}
reduce_function(obj, aggObj);
}
return { "result": map.values() };
}.toString();
/**
* Group.
*
* @param {Object|Array|Function|Code} keys
* @param {TODO} condition
* @param {TODO} initial
* @param {Function|Code} reduce
* @param {Boolean} command
* @param {Function} callback
*/
Collection.prototype.group = function group (keys, condition, initial, reduce, command, callback) {
var args = Array.prototype.slice.call(arguments, 3);

@@ -554,24 +956,33 @@ callback = args.pop();

if(command) {
var hash = {},
reduceFunction = reduce != null && reduce instanceof this.db.bson_serializer.Code ? reduce : new this.db.bson_serializer.Code(reduce);
var Code = this.db.bson_serializer.Code
if (!Array.isArray(keys) && keys instanceof Object && typeof(keys) !== 'function') {
keys = Object.keys(keys);
}
if (reduce instanceof Function) {
reduce = reduce.toString();
}
if (command) {
var reduceFunction = reduce instanceof Code
? reduce
: new Code(reduce);
var selector = {
group:
{
'ns': this.collectionName,
'$reduce': reduceFunction,
'cond': condition,
'initial': initial
group: {
'ns': this.collectionName
, '$reduce': reduceFunction
, 'cond': condition
, 'initial': initial
}
};
if(keys.constructor == Function)
{
var keyFunction = keys != null && keys instanceof this.db.bson_serializer.Code ? keys : new this.db.bson_serializer.Code(keys);
selector.group.$keyf = keyFunction;
}
else
{
keys.forEach(function(key) {
if ('function' === typeof keys) {
selector.group.$keyf = keys instanceof Code
? keys
: new Code(keys);
} else {
var hash = {};
keys.forEach(function (key) {
hash[key] = 1;

@@ -581,57 +992,33 @@ });

}
this.db.executeCommand(DbCommand.createDbCommand(this.db, selector), function(err, result) {
var cmd = DbCommand.createDbCommand(this.db, selector);
this.db.executeCommand(cmd, {read:true}, function (err, result) {
if (err) return callback(err);
var document = result.documents[0];
if(err == null && document.retval != null) {
callback(null, document.retval);
} else {
err != null ? callback(err, null) : callback(new Error("group command failed: " + document.errmsg), null);
if (null == document.retval) {
return callback(new Error("group command failed: " + document.errmsg));
}
callback(null, document.retval);
});
} else {
// Create execution scope
var scope = reduce != null && reduce instanceof this.db.bson_serializer.Code ? reduce.scope : {};
// Create scope for execution
scope['ns'] = this.collectionName;
scope['keys'] = keys;
scope['condition'] = condition;
scope['initial'] = initial;
// Define group function
var groupFunction = function() {
var c = db[ns].find(condition);
var map = new Map();
var reduce_function = reduce;
while (c.hasNext()) {
var obj = c.next();
var scope = reduce != null && reduce instanceof Code
? reduce.scope
: {};
var key = {};
for (var i = 0; i < keys.length; i++) {
var k = keys[i];
key[k] = obj[k];
}
scope.ns = this.collectionName;
scope.keys = keys;
scope.condition = condition;
scope.initial = initial;
var aggObj = map.get(key);
if (aggObj == null) {
var newObj = Object.extend({}, key);
aggObj = Object.extend(newObj, initial);
map.put(key, aggObj);
}
reduce_function(obj, aggObj);
}
return {"result": map.values()};
};
// Pass in the function text to execute within mongodb.
var groupfn = groupFunction.replace(/ reduce;/, reduce.toString() + ';');
// Turn function into text and replace the "result" function of the grouping function
var groupFunctionString = groupFunction.toString().replace(/ reduce;/, reduce.toString() + ';');
// Execute group
this.db.eval(new this.db.bson_serializer.Code(groupFunctionString, scope), function(err, results) {
if(err != null) {
callback(err, null);
} else {
if(results.constructor == Object) {
callback(err, results.result);
} else {
callback(err, results);
}
}
this.db.eval(new Code(groupfn, scope), function (err, results) {
if (err) return callback(err, null);
callback(null, results.result || results);
});

@@ -641,9 +1028,21 @@ }

Collection.prototype.options = function(callback) {
this.db.collectionsInfo(this.collectionName, function(err, cursor) {
// Fetch the object from the cursor
cursor.nextObject(function(err, document) {
callback(null, (document != null ? document.options : document));
/**
* Options.
*
* @param {Function} callback
*/
Collection.prototype.options = function options (callback) {
this.db.collectionsInfo(this.collectionName, function (err, cursor) {
if (err) return callback(err);
cursor.nextObject(function (err, document) {
callback(err, document && document.options || null);
});
});
};
/**
* Expose.
*/
exports.Collection = Collection;

@@ -1,2 +0,4 @@

var BinaryParser = require('../bson/binary_parser').BinaryParser;
var BinaryParser = require('../bson/binary_parser').BinaryParser,
debug = require('util').debug,
inspect = require('util').inspect;

@@ -9,13 +11,2 @@ /**

BaseCommand.prototype.toBinary = function() {
// Get the command op code
var op_code = this.getOpCode();
// Get the command data structure
var command = this.getCommand();
// Total Size of command
var totalSize = 4*4 + command.length;
// Create the command with the standard header file
return BinaryParser.fromInt(totalSize) + BinaryParser.fromInt(this.requestId) + BinaryParser.fromInt(0) + BinaryParser.fromInt(op_code) + command;
};
var id = 1;

@@ -36,2 +27,2 @@ BaseCommand.prototype.getRequestId = function() {

BaseCommand.OP_DELETE = 2006;
BaseCommand.OP_KILL_CURSORS = 2007;
BaseCommand.OP_KILL_CURSORS = 2007;
var QueryCommand = require('./query_command').QueryCommand,
InsertCommand = require('./insert_command').InsertCommand,
MD5 = require('../crypto/md5').MD5,
inherits = require('sys').inherits;
inherits = require('util').inherits,
debug = require('util').debug,
crypto = require('crypto'),
inspect = require('util').inspect;

@@ -44,8 +46,15 @@ /**

DbCommand.createAuthenticationCommand = function(db, username, password, nonce) {
// Use node md5 generator
var md5 = crypto.createHash('md5');
// Generate keys used for authentication
var hash_password = MD5.hex_md5(username + ":mongo:" + password);
var key = MD5.hex_md5(nonce + username + hash_password);
md5.update(username + ":mongo:" + password);
var hash_password = md5.digest('hex');
// Final key
md5 = crypto.createHash('md5');
md5.update(nonce + username + hash_password);
var key = md5.digest('hex');
// Creat selector
var selector = {'authenticate':1, 'user':username, 'nonce':nonce, 'key':key};
// Create db command
return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, selector, null);
return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NONE, 0, -1, selector, null);
};

@@ -77,9 +86,20 @@

DbCommand.createGetLastErrorCommand = function(db) {
return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'getlasterror':1}, null);
DbCommand.createGetLastErrorCommand = function(options, db) {
var args = Array.prototype.slice.call(arguments, 0);
db = args.pop();
options = args.length ? args.shift() : {};
// Final command
var command = {'getlasterror':1};
// If we have an options Object let's merge in the fields (fsync/wtimeout/w)
if('object' === typeof options) {
for(var name in options) {
command[name] = options[name]
}
}
// Execute command
return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command, null);
};
DbCommand.createGetLastStatusCommand = function(db) {
return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'getlasterror':1}, null);
};
DbCommand.createGetLastStatusCommand = DbCommand.createGetLastErrorCommand;

@@ -94,7 +114,16 @@ DbCommand.createGetPreviousErrorsCommand = function(db) {

DbCommand.createCreateIndexCommand = function(db, collectionName, fieldOrSpec, unique) {
var finalUnique = unique == null ? false : unique;
DbCommand.createCreateIndexCommand = function(db, collectionName, fieldOrSpec, options) {
var finalUnique = options == null && !(options instanceof Object) ? false : options;
var fieldHash = {};
var indexes = [];
var keys;
var sparse;
var background;
// If the options is a hash
if(options instanceof Object) {
finalUnique = options['unique'] != null ? options['unique'] : false;
sparse = options['sparse'] != null ? options['sparse'] : false;
background = options['background'] != null ? options['background'] : false;
}

@@ -136,3 +165,6 @@ // Get all the fields accordingly

// Build the selector
var selector = {'ns':(db.databaseName + "." + collectionName), 'unique':finalUnique, 'key':fieldHash, 'name':indexName};
var selector = {'ns':(db.databaseName + "." + collectionName), 'key':fieldHash, 'name':indexName};
selector['unique'] = finalUnique;
selector['sparse'] = sparse;
selector['background'] = background;
// Create the insert command for the index and return the document

@@ -142,2 +174,6 @@ return new InsertCommand(db, db.databaseName + "." + DbCommand.SYSTEM_INDEX_COLLECTION, false).add(selector);

DbCommand.logoutCommand = function(db, command_hash) {
return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null);
}
DbCommand.createDropIndexCommand = function(db, collectionName, indexName) {

@@ -154,1 +190,5 @@ return new DbCommand(db, db.databaseName + "." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, {'deleteIndexes':collectionName, 'index':indexName}, null);

};
DbCommand.createAdminDbCommand = function(db, command_hash) {
return new DbCommand(db, "admin." + DbCommand.SYSTEM_COMMAND_COLLECTION, QueryCommand.OPTS_NO_CURSOR_TIMEOUT, 0, -1, command_hash, null);
};
var BaseCommand = require('./base_command').BaseCommand,
BinaryParser = require('../bson/binary_parser').BinaryParser,
inherits = require('sys').inherits;
inherits = require('util').inherits,
debug = require('util').debug,
inspect = require('util').inspect;

@@ -10,3 +11,3 @@ /**

BaseCommand.call(this);
this.collectionName = collectionName;

@@ -19,5 +20,3 @@ this.selector = selector;

DeleteCommand.prototype.getOpCode = function() {
return BaseCommand.OP_DELETE;
};
DeleteCommand.OP_DELETE = 2006;

@@ -33,5 +32,63 @@ /*

*/
DeleteCommand.prototype.getCommand = function() {
// Generate the command string
return BinaryParser.fromInt(0) + BinaryParser.encode_cstring(this.collectionName) + BinaryParser.fromInt(0) + this.db.bson_serializer.BSON.serialize(this.selector);
DeleteCommand.prototype.toBinary = function() {
// Calculate total length of the document
var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + this.db.bson_serializer.BSON.calculateObjectSize(this.selector) + (4 * 4);
// Let's build the single pass buffer command
var _index = 0;
var _command = new Buffer(totalLengthOfCommand);
// Write the header information to the buffer
_command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
_command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
_command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
_command[_index] = totalLengthOfCommand & 0xff;
// Adjust index
_index = _index + 4;
// Write the request ID
_command[_index + 3] = (this.requestId >> 24) & 0xff;
_command[_index + 2] = (this.requestId >> 16) & 0xff;
_command[_index + 1] = (this.requestId >> 8) & 0xff;
_command[_index] = this.requestId & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the op_code for the command
_command[_index + 3] = (DeleteCommand.OP_DELETE >> 24) & 0xff;
_command[_index + 2] = (DeleteCommand.OP_DELETE >> 16) & 0xff;
_command[_index + 1] = (DeleteCommand.OP_DELETE >> 8) & 0xff;
_command[_index] = DeleteCommand.OP_DELETE & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the collection name to the command
_index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
_command[_index - 1] = 0;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Serialize the selector
var documentLength = this.db.bson_serializer.BSON.serializeWithBufferAndIndex(this.selector, this.checkKeys, _command, _index) - _index + 1;
// Write the length to the document
_command[_index + 3] = (documentLength >> 24) & 0xff;
_command[_index + 2] = (documentLength >> 16) & 0xff;
_command[_index + 1] = (documentLength >> 8) & 0xff;
_command[_index] = documentLength & 0xff;
// Update index in buffer
_index = _index + documentLength;
// Add terminating 0 for the object
_command[_index - 1] = 0;
return _command;
};
var BaseCommand = require('./base_command').BaseCommand,
BinaryParser = require('../bson/binary_parser').BinaryParser,
inherits = require('sys').inherits;
inherits = require('util').inherits,
debug = require('util').debug,
inspect = require('util').inspect,
binaryutils = require('../bson/binary_utils');

@@ -19,9 +21,77 @@ /**

GetMoreCommand.prototype.getOpCode = function() {
return BaseCommand.OP_GET_MORE;
};
GetMoreCommand.OP_GET_MORE = 2005;
GetMoreCommand.prototype.getCommand = function() {
// Generate the command string
return BinaryParser.fromInt(0) + BinaryParser.encode_cstring(this.collectionName) + BinaryParser.fromInt(this.numberToReturn) + this.db.bson_serializer.BSON.encodeLong(this.cursorId);
GetMoreCommand.prototype.toBinary = function() {
// debug("======================================================= GETMORE")
// debug("================ " + this.db.bson_serializer.BSON.calculateObjectSize(this.query))
// Calculate total length of the document
var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 8 + (4 * 4);
// Let's build the single pass buffer command
var _index = 0;
var _command = new Buffer(totalLengthOfCommand);
// Write the header information to the buffer
_command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
_command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
_command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
_command[_index] = totalLengthOfCommand & 0xff;
// Adjust index
_index = _index + 4;
// Write the request ID
_command[_index + 3] = (this.requestId >> 24) & 0xff;
_command[_index + 2] = (this.requestId >> 16) & 0xff;
_command[_index + 1] = (this.requestId >> 8) & 0xff;
_command[_index] = this.requestId & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the op_code for the command
_command[_index + 3] = (GetMoreCommand.OP_GET_MORE >> 24) & 0xff;
_command[_index + 2] = (GetMoreCommand.OP_GET_MORE >> 16) & 0xff;
_command[_index + 1] = (GetMoreCommand.OP_GET_MORE >> 8) & 0xff;
_command[_index] = GetMoreCommand.OP_GET_MORE & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the collection name to the command
_index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
_command[_index - 1] = 0;
// Number of documents to return
_command[_index + 3] = (this.numberToReturn >> 24) & 0xff;
_command[_index + 2] = (this.numberToReturn >> 16) & 0xff;
_command[_index + 1] = (this.numberToReturn >> 8) & 0xff;
_command[_index] = this.numberToReturn & 0xff;
// Adjust index
_index = _index + 4;
// Encode the cursor id
var low_bits = this.cursorId.getLowBits();
// Encode low bits
_command[_index + 3] = (low_bits >> 24) & 0xff;
_command[_index + 2] = (low_bits >> 16) & 0xff;
_command[_index + 1] = (low_bits >> 8) & 0xff;
_command[_index] = low_bits & 0xff;
// Adjust index
_index = _index + 4;
var high_bits = this.cursorId.getHighBits();
// Encode high bits
_command[_index + 3] = (high_bits >> 24) & 0xff;
_command[_index + 2] = (high_bits >> 16) & 0xff;
_command[_index + 1] = (high_bits >> 8) & 0xff;
_command[_index] = high_bits & 0xff;
// Adjust index
_index = _index + 4;
return _command;
};
var BaseCommand = require('./base_command').BaseCommand,
BinaryParser = require('../bson/binary_parser').BinaryParser,
inherits = require('sys').inherits;
inherits = require('util').inherits,
debug = require('util').debug,
inspect = require('util').inspect;

@@ -19,2 +20,5 @@ /**

// OpCodes
InsertCommand.OP_INSERT = 2002;
InsertCommand.prototype.add = function(document) {

@@ -25,6 +29,2 @@ this.documents.push(document);

InsertCommand.prototype.getOpCode = function() {
return BaseCommand.OP_INSERT;
};
/*

@@ -38,9 +38,65 @@ struct {

*/
InsertCommand.prototype.getCommand = function() {
var command_string = '';
InsertCommand.prototype.toBinary = function() {
// Calculate total length of the document
var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + (4 * 4);
// var docLength = 0
for(var i = 0; i < this.documents.length; i++) {
command_string = command_string + this.db.bson_serializer.BSON.serialize(this.documents[i], this.checkKeys);
// Calculate size of document
totalLengthOfCommand += this.db.bson_serializer.BSON.calculateObjectSize(this.documents[i]);
}
// Build the command string
return BinaryParser.fromInt(0) + BinaryParser.encode_cstring(this.collectionName) + command_string;
};
// Let's build the single pass buffer command
var _index = 0;
var _command = new Buffer(totalLengthOfCommand);
// Write the header information to the buffer
_command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
_command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
_command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
_command[_index] = totalLengthOfCommand & 0xff;
// Adjust index
_index = _index + 4;
// Write the request ID
_command[_index + 3] = (this.requestId >> 24) & 0xff;
_command[_index + 2] = (this.requestId >> 16) & 0xff;
_command[_index + 1] = (this.requestId >> 8) & 0xff;
_command[_index] = this.requestId & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the op_code for the command
_command[_index + 3] = (InsertCommand.OP_INSERT >> 24) & 0xff;
_command[_index + 2] = (InsertCommand.OP_INSERT >> 16) & 0xff;
_command[_index + 1] = (InsertCommand.OP_INSERT >> 8) & 0xff;
_command[_index] = InsertCommand.OP_INSERT & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the collection name to the command
_index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
_command[_index - 1] = 0;
// Write all the bson documents to the buffer at the index offset
for(var i = 0; i < this.documents.length; i++) {
// Serialize the document straight to the buffer
var documentLength = this.db.bson_serializer.BSON.serializeWithBufferAndIndex(this.documents[i], this.checkKeys, _command, _index) - _index + 1;
// Write the length to the document
_command[_index + 3] = (documentLength >> 24) & 0xff;
_command[_index + 2] = (documentLength >> 16) & 0xff;
_command[_index + 1] = (documentLength >> 8) & 0xff;
_command[_index] = documentLength & 0xff;
// Update index in buffer
_index = _index + documentLength;
// Add terminating 0 for the object
_command[_index - 1] = 0;
}
return _command;
};
var BaseCommand = require('./base_command').BaseCommand,
BinaryParser = require('../bson/binary_parser').BinaryParser,
inherits = require('sys').inherits;
inherits = require('util').inherits,
binaryutils = require('../bson/binary_utils'),
debug = require('util').debug,
inspect = require('util').inspect;

@@ -17,5 +19,3 @@ /**

KillCursorCommand.prototype.getOpCode = function() {
return BaseCommand.OP_KILL_CURSORS;
};
KillCursorCommand.OP_KILL_CURSORS = 2007;

@@ -30,10 +30,73 @@ /*

*/
KillCursorCommand.prototype.getCommand = function() {
var self = this;
// Generate the command string
var command_string = BinaryParser.fromInt(0) + BinaryParser.fromInt(this.cursorIds.length);
this.cursorIds.forEach(function(cursorId) {
command_string = command_string + self.db.bson_serializer.BSON.encodeLong(cursorId);
});
return command_string;
KillCursorCommand.prototype.toBinary = function() {
// Calculate total length of the document
var totalLengthOfCommand = 4 + 4 + (4 * 4) + (this.cursorIds.length * 8);
// Let's build the single pass buffer command
var _index = 0;
var _command = new Buffer(totalLengthOfCommand);
// Write the header information to the buffer
_command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
_command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
_command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
_command[_index] = totalLengthOfCommand & 0xff;
// Adjust index
_index = _index + 4;
// Write the request ID
_command[_index + 3] = (this.requestId >> 24) & 0xff;
_command[_index + 2] = (this.requestId >> 16) & 0xff;
_command[_index + 1] = (this.requestId >> 8) & 0xff;
_command[_index] = this.requestId & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the op_code for the command
_command[_index + 3] = (KillCursorCommand.OP_KILL_CURSORS >> 24) & 0xff;
_command[_index + 2] = (KillCursorCommand.OP_KILL_CURSORS >> 16) & 0xff;
_command[_index + 1] = (KillCursorCommand.OP_KILL_CURSORS >> 8) & 0xff;
_command[_index] = KillCursorCommand.OP_KILL_CURSORS & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Number of cursors to kill
var numberOfCursors = this.cursorIds.length;
_command[_index + 3] = (numberOfCursors >> 24) & 0xff;
_command[_index + 2] = (numberOfCursors >> 16) & 0xff;
_command[_index + 1] = (numberOfCursors >> 8) & 0xff;
_command[_index] = numberOfCursors & 0xff;
// Adjust index
_index = _index + 4;
// Encode all the cursors
for(var i = 0; i < this.cursorIds.length; i++) {
// Encode the cursor id
var low_bits = this.cursorIds[i].getLowBits();
// Encode low bits
_command[_index + 3] = (low_bits >> 24) & 0xff;
_command[_index + 2] = (low_bits >> 16) & 0xff;
_command[_index + 1] = (low_bits >> 8) & 0xff;
_command[_index] = low_bits & 0xff;
// Adjust index
_index = _index + 4;
var high_bits = this.cursorIds[i].getHighBits();
// Encode high bits
_command[_index + 3] = (high_bits >> 24) & 0xff;
_command[_index + 2] = (high_bits >> 16) & 0xff;
_command[_index + 1] = (high_bits >> 8) & 0xff;
_command[_index] = high_bits & 0xff;
// Adjust index
_index = _index + 4;
}
return _command;
};
var BaseCommand = require('./base_command').BaseCommand,
BinaryParser = require('../bson/binary_parser').BinaryParser,
inherits = require('sys').inherits;
inherits = require('util').inherits,
debug = require('util').debug,
inspect = require('util').inspect;

@@ -10,3 +12,3 @@ /**

BaseCommand.call(this);
this.collectionName = collectionName;

@@ -23,5 +25,3 @@ this.queryOptions = queryOptions;

QueryCommand.prototype.getOpCode = function() {
return BaseCommand.OP_QUERY;
};
QueryCommand.OP_QUERY = 2004;

@@ -39,12 +39,107 @@ /*

*/
QueryCommand.prototype.getCommand = function() {
// Generate the command string
var command_string = BinaryParser.fromInt(this.queryOptions) + BinaryParser.encode_cstring(this.collectionName);
command_string = command_string + BinaryParser.fromInt(this.numberToSkip) + BinaryParser.fromInt(this.numberToReturn);
command_string = command_string + this.db.bson_serializer.BSON.serialize(this.query);
QueryCommand.prototype.toBinary = function() {
// debug("======================================================= QUERY")
// debug("================ " + this.db.bson_serializer.BSON.calculateObjectSize(this.query))
// Calculate total length of the document
var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + 4 + this.db.bson_serializer.BSON.calculateObjectSize(this.query) + (4 * 4);
// Calculate extra fields size
if(this.returnFieldSelector != null) {
var count = 0; for(var name in this.returnFieldSelector) { count += 1; }
if(count > 0) command_string = command_string + this.db.bson_serializer.BSON.serialize(this.returnFieldSelector);
if(Object.keys(this.returnFieldSelector).length > 0) {
totalLengthOfCommand += this.db.bson_serializer.BSON.calculateObjectSize(this.returnFieldSelector);
}
}
return command_string;
// Let's build the single pass buffer command
var _index = 0;
var _command = new Buffer(totalLengthOfCommand);
// Write the header information to the buffer
_command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
_command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
_command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
_command[_index] = totalLengthOfCommand & 0xff;
// Adjust index
_index = _index + 4;
// Write the request ID
_command[_index + 3] = (this.requestId >> 24) & 0xff;
_command[_index + 2] = (this.requestId >> 16) & 0xff;
_command[_index + 1] = (this.requestId >> 8) & 0xff;
_command[_index] = this.requestId & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the op_code for the command
_command[_index + 3] = (QueryCommand.OP_QUERY >> 24) & 0xff;
_command[_index + 2] = (QueryCommand.OP_QUERY >> 16) & 0xff;
_command[_index + 1] = (QueryCommand.OP_QUERY >> 8) & 0xff;
_command[_index] = QueryCommand.OP_QUERY & 0xff;
// Adjust index
_index = _index + 4;
// Write the query options
_command[_index + 3] = (this.queryOptions >> 24) & 0xff;
_command[_index + 2] = (this.queryOptions >> 16) & 0xff;
_command[_index + 1] = (this.queryOptions >> 8) & 0xff;
_command[_index] = this.queryOptions & 0xff;
// Adjust index
_index = _index + 4;
// Write the collection name to the command
_index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
_command[_index - 1] = 0;
// Write the number of documents to skip
_command[_index + 3] = (this.numberToSkip >> 24) & 0xff;
_command[_index + 2] = (this.numberToSkip >> 16) & 0xff;
_command[_index + 1] = (this.numberToSkip >> 8) & 0xff;
_command[_index] = this.numberToSkip & 0xff;
// Adjust index
_index = _index + 4;
// Write the number of documents to return
_command[_index + 3] = (this.numberToReturn >> 24) & 0xff;
_command[_index + 2] = (this.numberToReturn >> 16) & 0xff;
_command[_index + 1] = (this.numberToReturn >> 8) & 0xff;
_command[_index] = this.numberToReturn & 0xff;
// Adjust index
_index = _index + 4;
// Serialize the query document straight to the buffer
var documentLength = this.db.bson_serializer.BSON.serializeWithBufferAndIndex(this.query, this.checkKeys, _command, _index) - _index + 1;
// debug(inspect("===================== documentLength :: " + documentLength))
// Write the length to the document
_command[_index + 3] = (documentLength >> 24) & 0xff;
_command[_index + 2] = (documentLength >> 16) & 0xff;
_command[_index + 1] = (documentLength >> 8) & 0xff;
_command[_index] = documentLength & 0xff;
// Update index in buffer
_index = _index + documentLength;
// Add terminating 0 for the object
_command[_index - 1] = 0;
// Push field selector if available
if(this.returnFieldSelector != null) {
if(Object.keys(this.returnFieldSelector).length > 0) {
var documentLength = this.db.bson_serializer.BSON.serializeWithBufferAndIndex(this.returnFieldSelector, this.checkKeys, _command, _index) - _index + 1;
// Write the length to the document
_command[_index + 3] = (documentLength >> 24) & 0xff;
_command[_index + 2] = (documentLength >> 16) & 0xff;
_command[_index + 1] = (documentLength >> 8) & 0xff;
_command[_index] = documentLength & 0xff;
// Update index in buffer
_index = _index + documentLength;
// Add terminating 0 for the object
_command[_index - 1] = 0;
}
}
// debug("------------------------------------------------------------------------")
// debug(inspect(_command))
return _command;
};

@@ -57,2 +152,4 @@

QueryCommand.OPTS_OPLOG_REPLY = 8;
QueryCommand.OPTS_NO_CURSOR_TIMEOUT = 16;
QueryCommand.OPTS_NO_CURSOR_TIMEOUT = 16;
QueryCommand.OPTS_AWAIT_DATA = 32;
QueryCommand.OPTS_EXHAUST = 64;
var BaseCommand = require('./base_command').BaseCommand,
BinaryParser = require('../bson/binary_parser').BinaryParser,
inherits = require('sys').inherits;
inherits = require('util').inherits,
debug = require('util').debug,
inspect = require('util').inspect;

@@ -9,3 +10,3 @@ /**

var UpdateCommand = exports.UpdateCommand = function(db, collectionName, spec, document, options) {
BaseCommand.apply(this);
BaseCommand.call(this);

@@ -29,7 +30,4 @@ this.collectionName = collectionName;

UpdateCommand.OP_UPDATE = 2001;
UpdateCommand.prototype.getOpCode = function() {
return BaseCommand.OP_UPDATE;
};
/*

@@ -45,6 +43,80 @@ struct {

*/
UpdateCommand.prototype.getCommand = function() {
// Generate the command string
var command_string = BinaryParser.fromInt(0) + BinaryParser.encode_cstring(this.collectionName);
return command_string + BinaryParser.fromInt(this.flags) + this.db.bson_serializer.BSON.serialize(this.spec) + this.db.bson_serializer.BSON.serialize(this.document, false);
UpdateCommand.prototype.toBinary = function() {
// Calculate total length of the document
var totalLengthOfCommand = 4 + Buffer.byteLength(this.collectionName) + 1 + 4 + this.db.bson_serializer.BSON.calculateObjectSize(this.spec) +
this.db.bson_serializer.BSON.calculateObjectSize(this.document) + (4 * 4);
// Let's build the single pass buffer command
var _index = 0;
var _command = new Buffer(totalLengthOfCommand);
// Write the header information to the buffer
_command[_index + 3] = (totalLengthOfCommand >> 24) & 0xff;
_command[_index + 2] = (totalLengthOfCommand >> 16) & 0xff;
_command[_index + 1] = (totalLengthOfCommand >> 8) & 0xff;
_command[_index] = totalLengthOfCommand & 0xff;
// Adjust index
_index = _index + 4;
// Write the request ID
_command[_index + 3] = (this.requestId >> 24) & 0xff;
_command[_index + 2] = (this.requestId >> 16) & 0xff;
_command[_index + 1] = (this.requestId >> 8) & 0xff;
_command[_index] = this.requestId & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the op_code for the command
_command[_index + 3] = (UpdateCommand.OP_UPDATE >> 24) & 0xff;
_command[_index + 2] = (UpdateCommand.OP_UPDATE >> 16) & 0xff;
_command[_index + 1] = (UpdateCommand.OP_UPDATE >> 8) & 0xff;
_command[_index] = UpdateCommand.OP_UPDATE & 0xff;
// Adjust index
_index = _index + 4;
// Write zero
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
_command[_index++] = 0;
// Write the collection name to the command
_index = _index + _command.write(this.collectionName, _index, 'utf8') + 1;
_command[_index - 1] = 0;
// Write the update flags
_command[_index + 3] = (this.flags >> 24) & 0xff;
_command[_index + 2] = (this.flags >> 16) & 0xff;
_command[_index + 1] = (this.flags >> 8) & 0xff;
_command[_index] = this.flags & 0xff;
// Adjust index
_index = _index + 4;
// Serialize the spec document
var documentLength = this.db.bson_serializer.BSON.serializeWithBufferAndIndex(this.spec, this.checkKeys, _command, _index) - _index + 1;
// Write the length to the document
_command[_index + 3] = (documentLength >> 24) & 0xff;
_command[_index + 2] = (documentLength >> 16) & 0xff;
_command[_index + 1] = (documentLength >> 8) & 0xff;
_command[_index] = documentLength & 0xff;
// Update index in buffer
_index = _index + documentLength;
// Add terminating 0 for the object
_command[_index - 1] = 0;
// Serialize the document
var documentLength = this.db.bson_serializer.BSON.serializeWithBufferAndIndex(this.document, this.checkKeys, _command, _index) - _index + 1;
// Write the length to the document
_command[_index + 3] = (documentLength >> 24) & 0xff;
_command[_index + 2] = (documentLength >> 16) & 0xff;
_command[_index + 1] = (documentLength >> 8) & 0xff;
_command[_index] = documentLength & 0xff;
// Update index in buffer
_index = _index + documentLength;
// Add terminating 0 for the object
_command[_index - 1] = 0;
return _command;
};

@@ -51,0 +123,0 @@

var net = require('net'),
debug = require('util').debug,
inspect = require('util').inspect,
EventEmitter = require("events").EventEmitter,
BinaryParser = require('./bson/binary_parser').BinaryParser,
inherits = require('sys').inherits;
inherits = require('util').inherits,
Server = require('./connections/server').Server,
binaryutils = require("./bson/binary_utils");
var Connection = exports.Connection = function(host, port, autoReconnect) {
var Connection = exports.Connection = function(host, port, autoReconnect, options) {
this.options = options == null ? {} : options;
this.host = host;

@@ -11,6 +16,7 @@ this.port = port;

this.drained = true;
// Fetch the poolsize
this.poolSize = this.options["poolSize"] == null ? 1 : this.options["poolSize"];
// Reconnect buffer for messages
this.messages = [];
// Message sender
var self = this;
// Status messages

@@ -21,2 +27,8 @@ this.sizeOfMessage = 0;

this.stubBuffer = '';
this.connected = false;
// Connection pool variables
this.pool = [];
this.poolByReference = {};
this.poolIndex = 0;
};

@@ -26,132 +38,302 @@

// Functions to open the connection
Connection.prototype.open = function() {
// Assign variable to point to local scope object
var self = this;
// Create the associated connection
this.connection = net.createConnection(this.port, this.host);
// Set up the net client
this.connection.setEncoding("binary");
// Add connnect listener
this.connection.addListener("connect", function() {
this.setEncoding("binary");
this.setTimeout(0);
this.setNoDelay();
self.emit("connect");
});
this.connection.addListener("error", function(err) {
self.emit("error", err);
});
this.connection.addListener("timeout", function(err) {
self.emit("timeout", err);
});
// Add a close listener
this.connection.addListener("close", function() {
self.emit("close");
});
// Listener for receive data
this.receiveListener = function(result) {
var getConnection = function(self) {
return self.pool[self.poolIndex++ % self.pool.length];
}
// Setup the connection pool
var setupConnectionPool = function(self, poolSize, reconnect) {
// Pool off connections and status variables
var connectionPool = [];
var connectedTo = 0;
var errors = 0;
var connectionError = null;
//
// Listener that handles callbacks for the connection
// Uses the internal object states to keep individual tcp connections seperate
// var receiveListener = function(result, fd) {
// fd = fd == null ? this.fd : fd;
//
// // Fetch the pool reference
// var conObj = self.poolByReference[fd];
//
// // Check if we have an unfinished message
// if(conObj != null && conObj.bytesRead > 0 && conObj.sizeOfMessage > 0) {
// // Calculate remaing bytes to fetch
// var remainingBytes = conObj.sizeOfMessage - conObj.bytesRead;
// // Check if we have multiple packet messages and save the pieces otherwise emit the message
// if(remainingBytes > result.length) {
// conObj.buffer = conObj.buffer + result; conObj.bytesRead = conObj.bytesRead + result.length;
// } else {
// // Cut off the remaining message
// conObj.buffer = conObj.buffer + result.substr(0, remainingBytes);
// // Emit the message
// self.emit("data", conObj.buffer);
// // Reset the variables
// conObj.buffer = ''; conObj.bytesRead = 0; conObj.sizeOfMessage = 0;
// // If message is longer than the current one, keep parsing
// if(remainingBytes < result.length) {
// receiveListener(result.substr(remainingBytes, (result.length - remainingBytes)), fd);
// }
// }
// } else if(conObj != null){
// if(conObj.stubBuffer.length > 0) {
// result = conObj.stubBuffer + result;
// conObj.stubBuffer = '';
// }
//
// if(result.length > 4) {
// var sizeOfMessage = BinaryParser.toInt(result.substr(0, 4));
// // We got a partial message, store the result and wait for more
// if(sizeOfMessage > result.length) {
// conObj.buffer = conObj.buffer + result; conObj.bytesRead = result.length; conObj.sizeOfMessage = sizeOfMessage;
// } else if(sizeOfMessage == result.length) {
// self.emit("data", result);
// } else if(sizeOfMessage < result.length) {
// self.emit("data", result.substr(0, sizeOfMessage));
// receiveListener(result.substr(sizeOfMessage, (result.length - sizeOfMessage)), fd);
// }
// } else {
// conObj.stubBuffer = result;
// }
// }
// };
var receiveListener = function(result, fd) {
fd = fd == null ? this.fd : fd;
// Fetch the pool reference
var conObj = self.poolByReference[fd];
// Check if we have an unfinished message
if(self.bytesRead > 0 && self.sizeOfMessage > 0) {
if(conObj != null && conObj.bytesRead > 0 && conObj.sizeOfMessage > 0) {
// Calculate remaing bytes to fetch
var remainingBytes = self.sizeOfMessage - self.bytesRead;
var remainingBytes = conObj.sizeOfMessage - conObj.bytesRead;
// Check if we have multiple packet messages and save the pieces otherwise emit the message
if(remainingBytes > result.length) {
self.buffer = self.buffer + result; self.bytesRead = self.bytesRead + result.length;
// conObj.buffer = conObj.buffer + result; conObj.bytesRead = conObj.bytesRead + result.length;
// Let's copy all the results into a new buffer
var buffer = new Buffer(conObj.buffer.length + result.length);
conObj.buffer.copy(buffer, 0, 0, conObj.buffer.length);
result.copy(buffer, conObj.buffer.length, 0, result.length);
// Save the reference to the new buffer
conObj.buffer = buffer;
// Adjust the number of read bytes
conObj.bytesRead = conObj.bytesRead + result.length;
} else {
// Cut off the remaining message
self.buffer = self.buffer + result.substr(0, remainingBytes);
var buffer = new Buffer(conObj.buffer.length + remainingBytes);
conObj.buffer.copy(buffer, 0, 0, conObj.buffer.length);
result.copy(buffer, conObj.buffer.length, 0, remainingBytes);
// Emit the message
self.emit("data", self.buffer);
self.emit("data", buffer);
// Reset the variables
self.buffer = ''; self.bytesRead = 0; self.sizeOfMessage = 0;
conObj.buffer = new Buffer(0); conObj.bytesRead = 0; conObj.sizeOfMessage = 0;
// If message is longer than the current one, keep parsing
if(remainingBytes < result.length) {
self.receiveListener(result.substr(remainingBytes, (result.length - remainingBytes)));
receiveListener(result.slice(remainingBytes, result.length), fd);
}
}
} else {
if(self.stubBuffer.length > 0) {
result = self.stubBuffer + result;
self.stubBuffer = '';
} else if(conObj != null){
if(conObj.stubBuffer.length > 0) {
// Add missing stub files
var buffer = new Buffer(conObj.stubBuffer.length + result.length);
conObj.stubBuffer.copy(buffer, 0, 0, conObj.stubBuffer.length);
result.copy(buffer, conObj.stubBuffer.length, 0, result.length);
// New result contains the former missing bytes as well as the incoming payload
result = buffer;
conObj.stubBuffer = new Buffer(0);
}
if(result.length > 4) {
var sizeOfMessage = BinaryParser.toInt(result.substr(0, 4));
// var sizeOfMessage = BinaryParser.toInt(result.toString('binary', 0, 4));
var sizeOfMessage = binaryutils.decodeUInt32(result, 0);
// We got a partial message, store the result and wait for more
if(sizeOfMessage > result.length) {
self.buffer = self.buffer + result; self.bytesRead = result.length; self.sizeOfMessage = sizeOfMessage;
// Create a new buffer with the correct size
var buffer = new Buffer(conObj.buffer.length + result.length);
conObj.buffer.copy(buffer, 0, 0, self.buffer.length);
result.copy(buffer, conObj.buffer.length, 0, result.length);
conObj.buffer = buffer;
// Adjust variables
conObj.bytesRead = result.length; conObj.sizeOfMessage = sizeOfMessage;
} else if(sizeOfMessage == result.length) {
self.emit("data", result);
} else if(sizeOfMessage < result.length) {
self.emit("data", result.substr(0, sizeOfMessage));
self.receiveListener(result.substr(sizeOfMessage, (result.length - sizeOfMessage)));
self.emit("data", result.slice(0, sizeOfMessage));
receiveListener(result.slice(sizeOfMessage, result.length), fd);
}
} else {
self.stubBuffer = result;
conObj.stubBuffer = result;
}
}
};
// Fill the pool
for(var i = 0; i < poolSize; i++) {
// Create the associated connection
var connection = net.createConnection(self.port, self.host);
// Set up the net client
// connection.setEncoding("binary");
// connection.setTimeout(0);
// connection.setNoDelay();
// Add connnect listener
connection.addListener("connect", function() {
// // this.setEncoding("binary");
this.setTimeout(0);
this.setNoDelay();
// Update number of connected to server
connectedTo = connectedTo + 1;
});
connection.addListener("error", function(err) {
// Update number of errors
errors = errors + 1;
connectionError = err;
});
connection.addListener("timeout", function(err) {
// Update number of errors
errors = errors + 1;
connectionError = err;
});
// Add a close listener
connection.addListener("close", function() {
self.emit("close");
});
// Add connection to the pool array
connectionPool.push({"connection": connection,
"sizeOfMessage": 0,
"bytesRead": 0,
"buffer": new Buffer(0),
"stubBuffer": ''});
// Add a receieved data connection
this.connection.addListener("data", this.receiveListener);
};
// Add the listener to the connection
connection.addListener("data", receiveListener);
}
// Function that wait for connection to finish up
var waitForConnections = function() {
// Emit a connect message once all connections are up
if(connectedTo == connectionPool.length) {
if(reconnect == null || !reconnect) {
self.connected = true;
self.poolByReference = {};
// Save the connections by the fd reference
self.pool.forEach(function(con) {
self.poolByReference[con.connection.fd] = con;
});
self.emit("connect");
} else {
self.connected = false;
self.emit("reconnect");
}
} else if(errors + connectedTo == connectionPool.length) {
if(reconnect == null || !reconnect) {
self.connected = false;
self.emit("error", connectionError);
} else {
self.connected = false;
self.emit("reconnect");
}
} else {
process.nextTick(waitForConnections);
}
}
// Wait until we are done connected to all pool entries before emitting connect signal
process.nextTick(waitForConnections);
// Return the pool
return connectionPool;
}
// Functions to open the connection
Connection.prototype.open = function() {
var self = this;
// Create the pool with connections
this.pool = setupConnectionPool(this, this.poolSize);
}
Connection.prototype.close = function() {
if(this.connection) this.connection.end();
this.connected = false;
// Close all entries in the pool
for(var i = 0; i < this.pool.length; i++) {
this.pool[i].connection.end();
}
};
Connection.prototype.send = function(command) {
Connection.prototype.send = function(command, rawConnection) {
var self = this;
// If we are executing the commnand on the entire pool
var connection = null;
// If we are forcing the use of a connection
if(rawConnection != null) {
connection = rawConnection;
} else {
connection = getConnection(self).connection;
}
// Check if the connection is closed
try {
if ( this.connection.readyState != "open" )
throw 'notConnected';
if(command.constructor == String) {
this.connection.write(command, "binary");
if (connection.readyState != "open") {
throw 'notConnected';
}
// Send the command, if it's an array of commands execute them all on the same connection
if(Array.isArray(command)) {
for(var i = 0; i < command.length; i++) {
connection.write(command[i].toBinary());
}
} else {
this.connection.write(command.toBinary(), "binary");
}
connection.write(command.toBinary());
}
} catch(err) {
// Check if the connection is closed
if(this.connection.readyState != "open" && this.autoReconnect) {
if(connection.readyState != "open" && self.autoReconnect) {
// Add the message to the queue of messages to send
this.messages.push(command);
self.messages.push(command);
// Initiate reconnect if no current running
if(this.connection.currently_reconnecting == null) {
this.connection.currently_reconnecting = true;
// Create the associated connection
var new_connection = net.createConnection(this.port, this.host);
// Set up the net client
new_connection.setEncoding("binary");
new_connection.addListener( "error", function( err ) {
self.emit( "error", err );
self.connection.currently_reconnecting = null;
});
// Add connnect listener
new_connection.addListener("connect", function() {
this.setEncoding("binary");
this.setTimeout(0);
this.setNoDelay();
// Add the listener
this.addListener("data", self.receiveListener);
// assign the new ready connection
self.connection = this;
// send all the messages
if(self.currently_reconnecting == null || self.currently_reconnecting == false) {
self.currently_reconnecting = true;
// Create the pool with connections
self.pool = setupConnectionPool(self, self.poolSize, true);
self.poolByReference = {};
// Save the connections by the fd reference
self.pool.forEach(function(con) {
self.poolByReference[con.connection.fd] = con;
})
// Wait for a reconnect and send all the messages
self.on("resend", function() {
self.currently_reconnecting = false;
// Fire the message again
while(self.messages.length > 0) {
var msg = self.messages.shift();
if(msg.constructor == String) {
this.write(msg, "binary");
} else {
this.write(msg.toBinary(), "binary");
}
// this.write(self.messages.shift().toBinary(), "binary");
}
});
// Fetch a connection and resend messages
connection = getConnection(self).connection;
// Fetch the a message
var command = self.messages.shift();
// Fire
if(Array.isArray(command)) {
for(var i = 0; i < command.length; i++) {
connection.write(command[i].toBinary());
}
} else {
connection.write(command.toBinary());
}
}
})
}
} else {
// Set connected to false
self.connected = false;
// Throw error
throw err;

@@ -161,2 +343,3 @@ }

};
/**

@@ -166,160 +349,20 @@ * Wrtie command without an attempt of reconnect

*/
Connection.prototype.sendwithoutReconnect = function(command) {
var self = this;
var connection = this.connection;
// Check if the connection is closed
if ( this.connection.readyState != "open" ) {
if (connection.readyState != "open") {
throw new Error( 'Connection closed!' );
}
try {
this.connection.write(command.toBinary(), "binary");
connection.write(command.toBinary(), "binary");
} catch(err) {
// no need to reconnect since called by latest master
// and already went through send() function
throw err;
// no need to reconnect since called by latest master
// and already went through send() function
throw err;
};
};
// Some basic defaults
Connection.DEFAULT_PORT = 27017;
var Server = exports.Server = function(host, port, options) {
this.host = host;
this.port = port;
this.options = options == null ? {} : options;
this.internalConnection;
this.internalMaster = false;
// Setters and getters
this.__defineGetter__("autoReconnect", function() { return this.options['auto_reconnect'] == null ? false : this.options['auto_reconnect']; });
this.__defineGetter__("connection", function() { return this.internalConnection; });
this.__defineSetter__("connection", function(connection) { this.internalConnection = connection; });
this.__defineGetter__("master", function() { return this.internalMaster; });
this.__defineSetter__("master", function(value) { this.internalMaster = value; });
this.__defineGetter__("masterConnection", function() { return this.internalConnection; });
};
Server.prototype.close = function(callback) {
this.connection.close(callback);
};
// Server pair object used to support a failover connection set
var ServerPair = exports.ServerPair = function(leftServer, rightServer) {
if(leftServer == null || rightServer == null || !(leftServer instanceof Server) || !(rightServer instanceof Server)) {
throw Error("Both left/right must be defined and off the type Server");
}
this.leftServer = leftServer;
this.rightServer = rightServer;
// Containst the master server entry
this.master = null;
this.target = null;
// Setters and getters
this.__defineGetter__("autoReconnect", function() {
if(this.target != null) return this.target.autoReconnect;
if(this.masterConnection != null) return this.masterConnection.autoReconnect;
});
this.__defineGetter__("masterConnection", function() {
if(this.target != null && this.target instanceof Server) return this.target.masterConnection;
if(this.leftServer.master) return this.leftServer.masterConnection;
if(this.rightServer.master) return this.rightServer.masterConnection;
return null;
});
};
ServerPair.prototype.setTarget = function(target) {
this.target = target;
this.servers = [];
};
ServerPair.MASTER = 0;
ServerPair.SHADOW_MASTER = 1;
// Server cluster (one master and multiple read slaves)
var ServerCluster = exports.ServerCluster = function(servers) {
// Containst the master server entry
this.master = null;
this.target = null;
if(servers.constructor != Array || servers.length == 0) {
throw Error("The parameter must be an array of servers and contain at least one server");
} else if(servers.constructor == Array || servers.length > 0) {
var count = 0;
servers.forEach(function(server) {
if(server instanceof Server) count = count + 1;
});
if(count < servers.length) {
throw Error("All server entries must be of type Server");
} else {
this.servers = servers;
}
}
// Setters and getters
this.__defineGetter__("autoReconnect", function() {
if(this.target != null) return this.target.autoReconnect;
if(this.masterConnection != null) return this.masterConnection.autoReconnect;
});
this.__defineGetter__("masterConnection", function() {
// Allow overriding to a specific connection
if(this.target != null && this.target instanceof Server) {
return this.target.masterConnection;
} else {
var finalServer = null;
this.servers.forEach(function(server) {
if(server.master == true) finalServer = server;
});
return finalServer != null ? finalServer.masterConnection : finalServer;
}
});
};
ServerCluster.prototype.setTarget = function(target) {
this.target = target;
};
/**
* ReplSetServers constructor provides master-slave functionality
*
* @param serverArr{Array of type Server}
* @return constructor of ServerCluster
*
*/
var ReplSetServers = exports.ReplSetServers = function(servers) {
// Contains the master server entry
this.master = null;
this.target = null;
if(servers.constructor != Array || servers.length == 0) {
throw Error("The parameter must be an array of servers and contain at least one server");
} else if(servers.constructor == Array || servers.length > 0) {
var count = 0;
servers.forEach(function(server) {
if(server instanceof Server) count = count + 1;
});
if(count < servers.length) {
throw Error("All server entries must be of type Server");
} else {
this.servers = servers;
}
}
// Setters and getters
this.__defineGetter__("autoReconnect", function() {
if(this.target != null) return this.target.autoReconnect;
if(this.masterConnection != null) return this.masterConnection.autoReconnect;
});
this.__defineGetter__("masterConnection", function() {
// Allow overriding to a specific connection
if(this.target != null && this.target instanceof Server) {
return this.target.masterConnection;
} else {
var finalServer = null;
this.servers.forEach(function(server) {
if(server.master == true && ( server.connection.connection.readyState == "open") ) finalServer = server;
});
return finalServer != null ? finalServer.masterConnection : finalServer;
}
});
};
ReplSetServers.prototype.setTarget = function(target) {
this.target = target;
};
Connection.DEFAULT_PORT = 27017;
var QueryCommand = require('./commands/query_command').QueryCommand,
GetMoreCommand = require('./commands/get_more_command').GetMoreCommand,
KillCursorCommand = require('./commands/kill_cursor_command').KillCursorCommand,
Integer = require('./goog/math/integer').Integer,
Long = require('./goog/math/long').Long;
Long = require('./goog/math/long').Long,
debug = require('util').debug,
inspect = require('util').inspect;

@@ -56,3 +57,3 @@ /**

this.batchSizeValue = batchSize == null ? 0 : batchSize;
this.slaveOk = slaveOk == null ? false : slaveOk;
this.slaveOk = slaveOk == null ? collection.slaveOk : slaveOk;

@@ -115,27 +116,23 @@ this.totalNumberOfRecords = 0;

if (!callback) {
if(!callback) {
throw Error('callback is mandatory');
}
try {
if(this.tailable) {
callback(new Error("Tailable cursor cannot be converted to array"), null);
} else if(this.state != Cursor.CLOSED) {
var items = [];
this.each(function(err, item) {
if (item != null) {
items.push(item);
} else {
callback(err, items);
if(this.tailable) {
callback(new Error("Tailable cursor cannot be converted to array"), null);
} else if(this.state != Cursor.CLOSED) {
var items = [];
items = null;
}
this.each(function(err, item) {
if(err != null) return callback(err, null);
item = null;
});
} else {
callback(new Error("Cursor is closed"), null);
}
} catch(err) {
callback(new Error(err.toString()), null);
if (item != null) {
items.push(item);
} else {
callback(err, items);
items = null;
}
});
} else {
callback(new Error("Cursor is closed"), null);
}

@@ -173,2 +170,4 @@ };

self.nextObject(function(err, item) {
if(err != null) return callback(err, null);
if(item != null) {

@@ -178,2 +177,3 @@ callback(null, item);

} else {
// Close the cursor if done
self.state = Cursor.CLOSED;

@@ -238,2 +238,3 @@ callback(err, null);

if(typeof direction === "function") { callback = direction; direction = null; }
if(this.tailable) {

@@ -249,2 +250,3 @@ callback(new Error("Tailable cursor doesn't support sorting"), null);

}
this.sortValue = order;

@@ -270,2 +272,3 @@ callback(null, this);

callback = callback || function(){};
if(this.tailable) {

@@ -283,2 +286,3 @@ callback(new Error("Tailable cursor doesn't support limit"), null);

}
return this;

@@ -301,2 +305,3 @@ };

callback = callback || function(){};
if(this.tailable) {

@@ -314,2 +319,3 @@ callback(new Error("Tailable cursor doesn't support skip"), null);

}
return this;

@@ -332,2 +338,3 @@ };

callback = callback || function(){};
if(this.state == Cursor.CLOSED) {

@@ -356,4 +363,3 @@ callback(new Error("Cursor is closed"), null);

}
}
else {
} else {
requestedLimit = this.batchSizeValue;

@@ -375,13 +381,12 @@ }

if (!this.timeout) {
queryOptions += QueryCommand.OPTS_NO_CURSOR_TIMEOUT;
queryOptions |= QueryCommand.OPTS_NO_CURSOR_TIMEOUT;
}
if (this.tailable != null) {
queryOptions += QueryCommand.OPTS_TAILABLE_CURSOR;
this.skipValue = this.limitValue = 0;
queryOptions |= QueryCommand.OPTS_TAILABLE_CURSOR;
this.skipValue = this.limitValue = 0;
}
if (this.slaveOk) {
queryOptions += QueryCommand.OPTS_SLAVE;
queryOptions |= QueryCommand.OPTS_SLAVE;
}
// limitValue of -1 is a special case used by Db#eval

@@ -463,23 +468,28 @@ var numberToReturn = this.limitValue == -1 ? -1 : this.limitRequest();

var self = this;
if(self.state == Cursor.INIT) {
try {
var commandHandler = function(err, result) {
if(!err && result.documents[0] && result.documents[0]['$err']) {
self.close(function() {callback(result.documents[0]['$err'], null);});
return;
}
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;
try {
if(err != null && result == null) return callback(err, null);
if(!err && result.documents[0] && result.documents[0]['$err']) {
// Let's keep the cursor open
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;
} catch(err) {
callback(new Error(err.toString()), null);
}
};
self.db.executeCommand(self.generateQueryCommand(), commandHandler);
self.db.executeCommand(self.generateQueryCommand(), {read:true}, commandHandler);
commandHandler = null;

@@ -494,3 +504,4 @@ } catch(err) {

} else {
self.close(function() {callback(null, null);});
// Force cursor to stay open
return self.close(function() {callback(null, null);});
}

@@ -521,33 +532,38 @@ }

// Execute the command
self.db.executeCommand(getMoreCommand, function(err, result) {
self.db.executeCommand(getMoreCommand, {read:true}, function(err, result) {
try {
if(err != null) callback(err, null);
self.cursorId = result.cursorId;
self.totalNumberOfRecords += result.numberReturned;
// Determine if there's more documents to fetch
if(result.numberReturned > 0) {
if (self.limitValue > 0) {
var excessResult = self.totalNumberOfRecords - self.limitValue;
self.cursorId = result.cursorId;
self.totalNumberOfRecords += result.numberReturned;
// Determine if there's more documents to fetch
if(result.numberReturned > 0) {
if (self.limitValue > 0) {
var excessResult = self.totalNumberOfRecords - self.limitValue;
if (excessResult > 0) {
result.documents.splice(-1*excessResult, excessResult);
if (excessResult > 0) {
result.documents.splice(-1*excessResult, excessResult);
}
}
self.items = self.items.concat(result.documents);
callback(null, self.items.shift());
} else if(self.tailable) {
self.getMoreTimer = setTimeout(function() {self.getMore(callback);}, 500);
} else {
self.close(function() {callback(null, null);});
}
self.items = self.items.concat(result.documents);
callback(null, self.items.shift());
} else if(self.tailable) {
self.getMoreTimer = setTimeout(function() {self.getMore(callback);}, 500);
} else {
self.close(function() {callback(null, null);});
result = null;
} catch(err) {
callback(new Error(err.toString()), null);
}
result = null;
});
getMoreCommand = null;
getMoreCommand = null;
} catch(err) {
var handleClose = function() {
var handleClose = function() {
callback(new Error(err.toString()), null);
};
self.close(handleClose);

@@ -574,4 +590,6 @@ handleClose = null;

cursor.nextObject(function(err, item) {
if(err != null) return callback(err, null);
// close the cursor
cursor.close(function(err, result) {
if(err != null) return callback(err, null);
callback(null, item);

@@ -599,3 +617,5 @@ });

function execute(command) {
self.db.executeCommand(command, function(err,result) {
self.db.executeCommand(command, {read:true}, function(err,result) {
if(err != null) return callback(err, null);
if (!self.queryRun && result) {

@@ -607,2 +627,3 @@ self.queryRun = true;

}
if (result.documents && result.documents.length) {

@@ -634,2 +655,3 @@ try {

}
return stream;

@@ -652,3 +674,3 @@ };

var command = new KillCursorCommand(this.db, [this.cursorId]);
this.db.executeCommand(command, null);
this.db.executeCommand(command, {read:true}, null);
} catch(err) {}

@@ -658,6 +680,6 @@ }

this.cursorId = self.db.bson_serializer.Long.fromInt(0);
this.state = Cursor.CLOSED;
this.state = Cursor.CLOSED;
// callback for backward compatibility
if (callback) {
if(callback) {
callback(null, this);

@@ -664,0 +686,0 @@ } else {

@@ -8,11 +8,10 @@ var QueryCommand = require('./commands/query_command').QueryCommand,

Collection = require('./collection').Collection,
Server = require('./connection').Server,
ServerPair = require('./connection').ServerPair,
ServerCluster = require('./connection').ServerCluster,
ReplSetServers = require('./connection').ReplSetServers,
Server = require('./connections/server').Server,
ReplSetServers = require('./connections/repl_set_servers').ReplSetServers,
Cursor = require('./cursor').Cursor,
MD5 = require('./crypto/md5').MD5,
EventEmitter = require('events').EventEmitter,
inherits = require('sys').inherits,
sys = require('sys');
inherits = require('util').inherits,
crypto = require('crypto'),
debug = require('util').debug,
inspect = require('util').inspect;

@@ -23,16 +22,19 @@ var Db = exports.Db = function(databaseName, serverConfig, options) {

this.serverConfig = serverConfig;
this.options = options == null ? {} : options;
this.options = options == null ? {} : options;
// Contains all the connections for the db
try {
this.bson_serializer = this.options.native_parser ? require('../../external-libs/bson/bson') : require('./bson/bson');
this.bson_deserializer = this.options.native_parser ? require('../../external-libs/bson/bson') : require('./bson/bson');
this.native_parser = this.options.native_parser;
var serializer = this.options.native_parser ? require('../../external-libs/bson') : require('./bson/bson');
this.bson_serializer = serializer;
this.bson_deserializer = serializer;
} catch (err) {
// If we tried to instantiate the native driver
throw "Native bson parser not compiled, please compile or avoud using native_parser=true";
throw "Native bson parser not compiled, please compile or avoid using native_parser=true";
}
this.connections = [];
// State of the db connection
this.state = 'notConnected';
this.pkFactory = this.options.pk == null ? this.bson_serializer.ObjectID : this.options.pk;
this.forceServerObjectId = this.options.forceServerObjectId != null ? this.options.forceServerObjectId : false;
// Added strict

@@ -42,2 +44,30 @@ this.strict = this.options.strict == null ? false : this.options.strict;

this.isInitializing = true;
this.auths = [];
// Allow slaveOk
this.slaveOk = this.options["slave_ok"] == null ? false : this.options["slave_ok"];
var self = this;
// Add a listener for the reconnect event
this.serverConfig.on("reconnect", function() {
// Number of current auths
var authLength = self.auths.length;
var numberOfReadyAuth = 0;
if(authLength > 0) {
// If we have any auths fire off the auth message to all the connections
for(var i = 0; i < authLength; i++) {
// Execute auth commands
self.authenticate(self.auths[i].username, self.auths[i].password, function(err, result) {
numberOfReadyAuth = numberOfReadyAuth + 1;
if(numberOfReadyAuth == self.auths.length) {
self.serverConfig.emit("resend");
}
});
}
} else {
self.serverConfig.emit("resend");
}
});
};

@@ -48,258 +78,13 @@

Db.prototype.open = function(callback) {
var self = this;
var self = this;
// Set up connections
if(self.serverConfig instanceof Server) {
self.serverConfig.connection = new Connection(self.serverConfig.host, self.serverConfig.port, self.serverConfig.autoReconnect);
self.connections.push(self.serverConfig.connection);
var server = self.serverConfig;
self.serverConfig.connection.addListener("connect", function() {
// Create a callback function for a given connection
var connectCallback = function(err, reply) {
if(err != null) {
return callback(err, null);
} else if(reply.documents[0].ismaster == 1) {
self.serverConfig.master = true;
} else if(reply.documents[0].ismaster == 0) {
self.serverConfig.master = false;
}
// emit a message saying we got a master and are ready to go and change state to reflect it
if(self.state == 'notConnected') {
self.state = 'connected';
//
// Call the server version function via admin to adapt to changes from 1.7.6 >
self.admin(function(err, admindb) {
admindb.serverInfo(function(err, doc) {
if(err != null) return callback(err, null);
// Store the db version
self.version = doc.version;
callback(null, self);
});
});
} else {
callback("connection already opened");
}
};
// Create db command and Add the callback to the list of callbacks by the request id (mapping outgoing messages to correct callbacks)
var db_command = DbCommand.createIsMasterCommand(self);
self.addListener(db_command.getRequestId().toString(), connectCallback);
self.notReplied[db_command.getRequestId().toString()] = this;
// Let's send a request to identify the state of the server
this.send(db_command);
if(self.serverConfig instanceof Server || self.serverConfig instanceof ReplSetServers) {
self.serverConfig.connect(self, function(err, result) {
if(err != null) return callback(err, null);
// Callback
return callback(null, self);
});
self.serverConfig.connection.addListener("data", function(message) {
// Parse the data as a reply object
var reply = new MongoReply(self, message);
// Emit message
self.emit(reply.responseTo.toString(), null, reply);
// Remove the listener
if ( self.notReplied[ reply.responseTo.toString()]) {
delete self.notReplied[ reply.responseTo.toString()];
self.removeListener(reply.responseTo.toString(), self.listeners( reply.responseTo.toString())[0] );
}
});
self.serverConfig.connection.addListener("error", function(err) {
if(self.listeners("error") != null && self.listeners("error").length > 0) self.emit("error", err);
self.state = "notConnected"
return callback(err, null);
});
// Emit timeout and close events so the client using db can figure do proper error handling (emit contains the connection that triggered the event)
self.serverConfig.connection.addListener("timeout", function() { self.emit("timeout", this); });
self.serverConfig.connection.addListener("close", function() { self.emit("close", this); });
// Open the connection
self.serverConfig.connection.open();
} else if(self.serverConfig instanceof ServerPair || self.serverConfig instanceof ServerCluster) {
var serverConnections = self.serverConfig instanceof ServerPair ? [self.serverConfig.leftServer, self.serverConfig.rightServer] : self.serverConfig.servers;
var numberOfCheckedServers = 0;
serverConnections.forEach(function(server) {
server.connection = new Connection(server.host, server.port, server.autoReconnect);
self.connections.push(server.connection);
var handleServerConnection = function() {
numberOfCheckedServers+=1;
if(numberOfCheckedServers == serverConnections.length) {
if(self.masterConnection) {
// emit a message saying we got a master and are ready to go and change state to reflect it
self.state = 'connected';
callback(null, self);
} else {
// emit error only when all servers are checked and connecting to them failed.
self.state = "notConnected"
callback(new Error("Failed connecting to any of the servers in the cluster"), null);
}
}
}
server.connection.addListener("connect", function() {
// Create a callback function for a given connection
var connectCallback = function(err, reply) {
if(err != null) {
callback(err, null);
} else {
if(reply.documents[0].ismaster == 1) {
// Locate the master connection and save it
self.masterConnection = server.connection;
server.master = true;
} else {
server.master = false;
}
handleServerConnection();
}
};
// Create db command and Add the callback to the list of callbacks by the request id (mapping outgoing messages to correct callbacks)
var db_command = DbCommand.createIsMasterCommand(self);
self.addListener(db_command.getRequestId().toString(), connectCallback);
// Let's send a request to identify the state of the server
this.send(db_command);
});
server.connection.addListener("data", function(message) {
// Parse the data as a reply object
var reply = new MongoReply(self, message);
// Emit error if there is one
reply.responseHasError ? self.emit(reply.responseTo.toString(), reply.documents[0], reply) : self.emit(reply.responseTo.toString(), null, reply);
// Remove the listener
self.removeListener(reply.responseTo.toString(), self.listeners(reply.responseTo.toString())[0]);
});
server.connection.addListener("error", function(err) {
handleServerConnection();
});
// Emit timeout and close events so the client using db can figure do proper error handling (emit contains the connection that triggered the event)
server.connection.addListener("timeout", function() { self.emit("timeout", this); });
server.connection.addListener("close", function() { self.emit("close", this); });
// Open the connection
server.connection.open();
});
} else if ( self.serverConfig instanceof ReplSetServers ) {
var serverConnections = self.serverConfig instanceof ServerPair ? [self.serverConfig.leftServer, self.serverConfig.rightServer] : self.serverConfig.servers;
var numberOfConnectedServers = 0;
var numberOfErrorServers = 0;
self.serverConfig.addresses = {};
var initServer = function(server) {
self.serverConfig.addresses[ server.host + ':' + server.port ] = 1;
server.connection = new Connection(server.host, server.port, server.autoReconnect);
//console.log( 'Connect to ' + server.host + ':' + server.port );
self.connections.push(server.connection);
server.connection.addListener("connect", function() {
// Create a callback function for a given connection
var connectCallback = function(err, reply) {
if(err != null) {
callback(err, null);
} else {
if(reply.documents[0].ismaster == 1) {
// Locate the master connection and save it
self.masterConnection = server.connection;
server.master = true;
} else {
server.master = false;
}
if ( self.serverConfig instanceof ReplSetServers && ( reply.documents[0].hosts != undefined ) ) {
var replicas = reply.documents[0].hosts;
for( var i in replicas ) {
if ( replicas[i] in self.serverConfig.addresses )
continue;
self.serverConfig.addresses[ replicas[i] ] = 1;
var ipAndPort = replicas[i].split(":");
var newServer = new Server( ipAndPort[0], parseInt( ipAndPort[1]), { auto_reconnect: true} );
console.log( 'Added ' + replicas[i] + ' to the replica set' );
serverConnections.push( newServer );
initServer( newServer );
}
}
// emit a message saying we got a master and are ready to go and change state to reflect it
if(++numberOfConnectedServers == serverConnections.length && (self.state == 'notConnected')) {
self.state = 'connected';
self.isInitializing = false;
return callback(null, self);
}
if ( self.serverConfig instanceof ReplSetServers && server.master ) {
//we have the master we are ok, wait for others (if any) to connect too
self.state = 'connected';
}
if ( self.serverConfig instanceof ReplSetServers && ( (numberOfConnectedServers + numberOfErrorServers ) == serverConnections.length )) {
self.isInitializing = false;
if ( self.state == 'connected' ) {
return callback( null, self );
} else {
return callback( new Error( 'No master available'), null );
}
}
}
};
// Create db command and Add the callback to the list of callbacks by the request id (mapping outgoing messages to correct callbacks)
var db_command = DbCommand.createIsMasterCommand(self);
self.addListener(db_command.getRequestId().toString(), connectCallback);
self.notReplied[db_command.getRequestId().toString()] = this;
// Let's send a request to identify the state of the server
this.send(db_command);
server.connection.addListener("data", function(message) {
// Parse the data as a reply object
var reply = new MongoReply(self, message);
// Emit error if there is one
reply.responseHasError ? self.emit(reply.responseTo.toString(), reply.documents[0], reply) : self.emit(reply.responseTo.toString(), null, reply);
// Remove the listener
//if ( self.listeners(reply.responseTo.toString()).length )
if ( self.notReplied [ reply.responseTo.toString()] ) {
delete self.notReplied[ reply.responseTo.toString()];
self.removeListener(reply.responseTo.toString(), self.listeners(reply.responseTo.toString())[0]);
}
});
});
server.connection.addListener("error", function(err) {
if ( self.serverConfig instanceof ReplSetServers && self.isInitializing) {
//we only have one error, if the rest are ok there is no problem
numberOfErrorServers++;
//console.log( server.host + ':' + server.port + ' down!!!'+ err );
if ( (numberOfErrorServers + numberOfConnectedServers) == serverConnections.length) {
self.isInitializing = false;
if ( self.state == 'connected' ) {
return callback( null, self );
} else {
return callback( new Error( 'No master available'), null );
}
}
} else if ( self.serverConfig instanceof ReplSetServers ) {
for ( var i in self.notReplied ) {
//console.log( 'delete event ' + i );
if ( self.notReplied[i] == this ) {
delete self.notReplied[i];
self.emit( i, null, { documents: [{'$err':'Connection closed'}] } );
self.removeListener( i, self.listeners( i )[0]);
}
}
} else {
return callback(err, null);
}
});
// Emit timeout and close events so the client using db can figure do proper error handling (emit contains the connection that triggered the event)
server.connection.addListener("timeout", function() { self.emit("timeout", this); });
server.connection.addListener("close", function() { self.emit("close", this); });
// Open the connection
server.connection.open();
};
serverConnections.forEach( initServer );
} else {
return callback(Error("Server parameter must be of type Server, ServerPair, ServerCluster or ReplSetServers"), null);
return callback(Error("Server parameter must be of type Server or ReplSetServers"), null);
}

@@ -309,5 +94,6 @@ };

Db.prototype.close = function() {
this.connections.forEach(function(connection) {
connection.close();
});
// Remove all listeners
this.removeAllListeners("reconnect");
// Close connection
this.serverConfig.close();
// Clear out state of the connection

@@ -318,2 +104,3 @@ this.state = "notConnected"

Db.prototype.admin = function(callback) {
if(callback == null) return new Admin(this);
callback(null, new Admin(this));

@@ -349,3 +136,7 @@ };

self.collectionsInfo(collection_name, function(err, cursor) {
if(err != null) return callback(err, null);
cursor.toArray(function(err, documents) {
if(err != null) return callback(err, null);
// List of result documents that have been filtered

@@ -367,16 +158,19 @@ var filtered_documents = [];

**/
Db.prototype.collection = function(collectionName, callback) {
Db.prototype.collection = function(collectionName, options, callback) {
var self = this;
if(typeof options === "function") { callback = options; options = {}; }
try {
if(self.strict) {
if(options && options.safe || this.strict) {
self.collectionNames(collectionName, function(err, collections) {
if(err != null) return callback(err, null);
if(collections.length == 0) {
callback(new Error("Collection " + collectionName + " does not exist. Currently in strict mode."), null);
return callback(new Error("Collection " + collectionName + " does not exist. Currently in strict mode."), null);
} else {
return callback(null, new Collection(self, collectionName, self.pkFactory));
return callback(null, new Collection(self, collectionName, self.pkFactory, options));
}
});
} else {
return callback(null, new Collection(self, collectionName, self.pkFactory));
return callback(null, new Collection(self, collectionName, self.pkFactory, options));
}

@@ -395,5 +189,6 @@ } catch(err) {

self.collectionNames(function(err, documents) {
if(err != null) return callback(err, null);
var collections = [];
documents.forEach(function(document) {
collections.push(new Collection(self, document.name.replace(self.databaseName + ".", '')));
collections.push(new Collection(self, document.name.replace(self.databaseName + ".", ''), self.pkFactory));
});

@@ -427,2 +222,4 @@ // Return the collection objects

new Cursor(this, new Collection(this, DbCommand.SYSTEM_COMMAND_COLLECTION), selector, {}, 0, -1).nextObject(function(err, result) {
if(err != null) return callback(err, null);
if(result.ok == 1) {

@@ -438,2 +235,4 @@ callback(null, result.retval);

this.collection(dbRef.namespace, function(err, collection) {
if(err != null) return callback(err, null);
collection.findOne({'_id':dbRef.oid}, function(err, result) {

@@ -446,13 +245,29 @@ callback(err, result);

/**
Authenticate against server
Logout user from server
Fire off on all connections and remove all auth info
**/
Db.prototype.authenticate = function(username, password, callback) {
Db.prototype.logout = function(options, callback) {
var self = this;
// Execute command
this.executeCommand(DbCommand.createGetNonceCommand(self), function(err, reply) {
if(err == null) {
// Nonce used to make authentication request with md5 hash
var nonce = reply.documents[0].nonce;
// Execute command
self.executeCommand(DbCommand.createAuthenticationCommand(self, username, password, nonce), function(err, result) {
// If the first object is a function
if(typeof options === "function") { callback = options; options = {}}
// Let's generate the logout command object
var logoutCommand = DbCommand.logoutCommand(self, {logout:1, socket:options['socket']});
// For all the connections let's execute the command
var rawConnections = self.serverConfig.allRawConnections();
var numberOfExpectedReturns = rawConnections.length;
for(var index = 0; index < numberOfExpectedReturns; index++) {
// Execute the logout on all raw connections
self.executeCommand(logoutCommand, {writer: rawConnections[index].connection}, function(err, result) {
// Ajust the number of expected results
numberOfExpectedReturns = numberOfExpectedReturns - 1;
// If we are done let's evaluate
if(numberOfExpectedReturns <= 0) {
// Reset auth
self.auths = [];
// Handle any errors
if(err == null && result.documents[0].ok == 1) {

@@ -462,8 +277,56 @@ callback(null, true);

err != null ? callback(err, false) : callback(new Error(result.documents[0].errmsg), false);
}
}
});
}
}
/**
Authenticate against server
**/
Db.prototype.authenticate = function(username, password, callback) {
var self = this;
// Add the auth details to the connection for reconnects or update existing if any
var found = false;
for(var i = 0; i < self.auths.length; i++) {
// If we have found an existing auth, update the password
if(self.auths[i].username == username) {
found = true;
self.auths[i].password = password;
}
}
// Push the new auth if we have no previous record
if(!found) self.auths.push({'username':username, 'password':password});
// Figure out the number of times we need to trigger
var rawConnections = self.serverConfig.allRawConnections();
var numberOfExpectedReturns = rawConnections.length;
// Execute the commands
for(var i = 0; i < numberOfExpectedReturns; i++) {
// Execute command
var createNonceCallback = function(index) {
return function(err, reply) {
if(err == null) {
// Nonce used to make authentication request with md5 hash
var nonce = reply.documents[0].nonce;
// Execute command
self.executeCommand(DbCommand.createAuthenticationCommand(self, username, password, nonce), {writer: rawConnections[index].connection}, function(err, result) {
// Ajust the number of expected results
numberOfExpectedReturns = numberOfExpectedReturns - 1;
// If we are done let's evaluate
if(numberOfExpectedReturns <= 0) {
if(err == null && result.documents[0].ok == 1) {
callback(null, true);
} else {
err != null ? callback(err, false) : callback(new Error(result.documents[0].errmsg), false);
}
}
});
} else {
callback(err, null);
}
});
} else {
callback(err, null);
}
}
});
this.executeCommand(DbCommand.createGetNonceCommand(self), {writer: rawConnections[i].connection}, createNonceCallback(i));
}
};

@@ -475,7 +338,11 @@

Db.prototype.addUser = function(username, password, callback) {
var userPassword = MD5.hex_md5(username + ':mongo:' + password);
// Use node md5 generator
var md5 = crypto.createHash('md5');
// Generate keys used for authentication
md5.update(username + ":mongo:" + password);
var userPassword = md5.digest('hex');
// Fetch a user collection
this.collection(DbCommand.SYSTEM_USER_COLLECTION, function(err, collection) {
// Insert the user into the system users collections
collection.insert({user: username, pwd: userPassword}, function(err, documents) {
collection.insert({user: username, pwd: userPassword}, {safe:true}, function(err, documents) {
callback(err, documents);

@@ -505,9 +372,2 @@ });

/**
Logout user (if authenticated)
**/
Db.prototype.logout = function(callback) {
this.executeCommand(DbCommand.createLogoutCommand(this), callback);
};
/**
Create Collection

@@ -519,6 +379,7 @@ **/

options = args.length ? args.shift() : null;
var self = this;
// Check if we have the name
this.collectionNames(collectionName, function(err, collections) {
if(err != null) return callback(err, null);
var found = false;

@@ -537,3 +398,3 @@ collections.forEach(function(collection) {

// Create a new collection and return it
self.executeCommand(DbCommand.createCreateCollectionCommand(self, collectionName, options), function(err, result) {
self.executeCommand(DbCommand.createCreateCollectionCommand(self, collectionName, options), {read:false, safe:true}, function(err, result) {
if(err == null && result.documents[0].ok == 1) {

@@ -576,10 +437,12 @@ callback(null, new Collection(self, collectionName, self.pkFactory));

**/
Db.prototype.lastError = function(callback) {
this.executeCommand(DbCommand.createGetLastErrorCommand(this), function(err, error) {
callback(err, error.documents);
Db.prototype.lastError = function(options, callback) {
if ('function' === typeof options) callback = options, options = {};
this.executeCommand(DbCommand.createGetLastErrorCommand(options, this), function(err, error) {
callback(err, error && error.documents);
});
};
Db.prototype.error = function(callback) {
this.lastError(callback);
Db.prototype.error = function(options, callback) {
this.lastError(options, callback);
};

@@ -611,2 +474,9 @@

/**
Runs a command on the database as admin
**/
Db.prototype.executeDbAdminCommand = function(command_hash, callback) {
this.executeCommand(DbCommand.createAdminDbCommand(this, command_hash), callback);
};
/**
Resets the error history of the mongo instance

@@ -621,7 +491,15 @@ **/

**/
Db.prototype.createIndex = function(collectionName, fieldOrSpec, unique, callback) {
if(callback == null) { callback = unique; unique = null; }
var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, unique);
this.executeCommand(command, function(result) {});
callback(null, command.documents[0].name);
Db.prototype.createIndex = function(collectionName, fieldOrSpec, options, callback) {
if(callback == null) { callback = options; options = null; }
var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, options);
this.executeCommand(command, {read:false, safe:true}, function(err, result) {
if(err != null) return callback(err, null);
result = result && result.documents;
if (result[0].err) {
callback(new Error(result[0].err));
} else {
callback(null, command.documents[0].name);
}
});
};

@@ -632,5 +510,5 @@

**/
Db.prototype.ensureIndex = function(collectionName, fieldOrSpec, unique, callback) {
if(callback == null) { callback = unique; unique = null; }
var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, unique);
Db.prototype.ensureIndex = function(collectionName, fieldOrSpec, options, callback) {
if(callback == null) { callback = options; options = null; }
var command = DbCommand.createCreateIndexCommand(this, collectionName, fieldOrSpec, options);
var index_name = command.documents[0].name;

@@ -640,4 +518,16 @@ var self = this;

this.indexInformation(collectionName, function(err, collectionInfo) {
if(!collectionInfo[index_name]) self.executeCommand(command, function(result) {});
return callback(null, index_name);
if(!collectionInfo[index_name]) {
self.executeCommand(command, {read:false, safe:true}, function(err, result) {
if(err != null) return callback(err, null);
result = result && result.documents;
if (result[0].err) {
callback(new Error(result[0].err));
} else {
callback(null, command.documents[0].name);
}
});
} else {
return callback(null, index_name);
}
});

@@ -665,13 +555,26 @@ };

**/
Db.prototype.indexInformation = function(collectionName, callback) {
if(typeof collectionName === "function") { callback = collectionName; collectionName = null;}
Db.prototype.indexInformation = function(collectionName, options, callback) {
// Unpack calls
var args = Array.prototype.slice.call(arguments, 0);
callback = args.pop();
collectionName = args.length ? args.shift() : null;
options = args.length ? args.shift() : {};
// If we specified full information
var full = options['full'] == null ? false : options['full'];
// Build selector for the indexes
var selector = collectionName != null ? {ns: (this.databaseName + "." + collectionName)} : {};
var info = {};
// Iterate through all the fields of the index
new Cursor(this, new Collection(this, DbCommand.SYSTEM_INDEX_COLLECTION), selector).each(function(err, index) {
// Return the info when finished
if(index == null) {
callback(null, info);
} else {
new Cursor(this, new Collection(this, DbCommand.SYSTEM_INDEX_COLLECTION), selector).toArray(function(err, indexes) {
if(err != null) return callback(err, null);
// Contains all the information
var info = {};
// if full defined just return all the indexes directly
if(full) return callback(null, indexes);
// Process all the indexes
for(var i = 0; i < indexes.length; i++) {
var index = indexes[i];
// Let's unpack the object
info[index.name] = [];

@@ -682,2 +585,5 @@ for(var name in index.key) {

}
// Return all the indexes
callback(null, info);
});

@@ -698,45 +604,102 @@ };

**/
Db.prototype.executeCommand = function(db_command, callback) {
Db.prototype.executeCommand = function(db_command, options, callback) {
var self = this;
var args = Array.prototype.slice.call(arguments, 1);
callback = args.pop();
options = args.length ? args.shift() : {};
// Options unpacking
var read = options['read'] != null ? options['read'] : false;
var safe = options['safe'] != null ? options['safe'] : false;
// Let's us pass in a writer to force the use of a connection (used for admin where we need to peform 2 calls against the same connection)
var rawConnection = options['writer'] != null ? options['writer'] : null;
var errorCommand = null;
if(safe == true) {
errorCommand = DbCommand.createGetLastErrorCommand(safe, this);
}
// If we have a callback execute
if(callback instanceof Function) {
var listenToCommand = errorCommand != null ? errorCommand : db_command;
// Add the callback to the list of callbacks by the request id (mapping outgoing messages to correct callbacks)
this.addListener(db_command.getRequestId().toString(), callback);
if ( self.serverConfig.masterConnection != null ) {
this.notReplied[db_command.getRequestId().toString()] = self.serverConfig.masterConnection;
}
this.on(listenToCommand.getRequestId().toString(), callback);
if(self.serverConfig.primary != null) {
this.notReplied[listenToCommand.getRequestId().toString()] = self.serverConfig.primary;
}
}
// Correctly handle serialization errors
var checkMasterHandler = function(err, reply, dbinstance){
if (err == null){
try{
if ( dbinstance.backup.server ) { // use slave this ONE time
self.notReplied[db_command.getRequestId().toString()] = dbinstance.backup.server.connection;
dbinstance.backup.server.connection.send( db_command);
dbinstance.backup.server = null;
} else {
self.notReplied[db_command.getRequestId().toString()] = dbinstance.serverConfig.masterConnection;
dbinstance.serverConfig.masterConnection.send(db_command);
}
} catch ( err ) {
// Clean up callback if it exists
if(this.notReplied[db_command.getRequestId().toString()] != null) {
delete self.notReplied[db_command.getRequestId().toString()];
}
if(callback instanceof Function) {
return callback(err, null);
}
try{
// Attempt forcing a reconnect if we have a replicaset server
if(self.serverConfig instanceof ReplSetServers && !self.serverConfig.isConnected()) {
// Initialize
self.isInitializing = true;
// Number of retries
var retries = self.serverConfig.retries;
// Attempts a reconnect
var reconnectAttempt = function() {
// Try reconnect
self.serverConfig.connect(self, function(err, result) {
// Initialize
self.isInitializing = true;
// Set retries
retries = retries - 1;
// If we fail retry the connec
if(err != null && retries > 0) {
// Wait an try again
setTimeout(reconnectAttempt, self.serverConfig.reconnectWait);
} else {
// Ensure we catch any errors happening and report them (especially important for replicaset servers)
try {
if(err != null && callback instanceof Function) return callback(err, null);
// for the other instances fire the message
var writer = read ? self.serverConfig.checkoutReader() : self.serverConfig.checkoutWriter();
// If we got safe set
if(errorCommand != null) {
writer.send([db_command, errorCommand], rawConnection);
} else {
writer.send(db_command, rawConnection)
}
} catch (err) {
// Set server config to disconnected if it's a replicaset
if(self.serverConfig instanceof ReplSetServers && err == "notConnected") {
// Just clear up all connections as we need to perform a complete reconnect for the call
self.serverConfig.disconnect();
}
// Signal an error
if(!(err instanceof Error)) err = new Error(err);
if(callback instanceof Function) {
if(errorCommand != null) delete self.notReplied[errorCommand.getRequestId().toString()];
return callback(err, null);
}
}
}
});
}
// Force a reconnect after self.serverConfig.reconnectWait seconds
setTimeout(reconnectAttempt, self.serverConfig.reconnectWait);
} else {
// for the other instances fire the message
var writer = read ? self.serverConfig.checkoutReader() : self.serverConfig.checkoutWriter();
// If we got safe set
if(errorCommand != null) {
writer.send([db_command, errorCommand], rawConnection);
} else {
writer.send(db_command, rawConnection)
}
} else {
// XXX : LOOP!!!!!!
setTimeout( self.checkMaster_(self, checkMasterHandler), 50 );
}
} catch(err){
// Set server config to disconnected if it's a replicaset
if(self.serverConfig instanceof ReplSetServers && err == "notConnected") {
// Just clear up all connections as we need to perform a complete reconnect for the call
self.serverConfig.disconnect();
}
};
try{
self.serverConfig.masterConnection.send(db_command);
} catch(err){
if(callback instanceof Function) {
delete self.notReplied[db_command.getRequestId().toString()];
// Signal an error
if(!(err instanceof Error)) err = new Error(err);
if(callback instanceof Function) {
if(errorCommand != null) delete self.notReplied[errorCommand.getRequestId().toString()];
return callback(err, null);

@@ -746,3 +709,3 @@ }

// Return error object
return err;
return err;
}

@@ -785,121 +748,2 @@ };

});
}
/**
* Checks for latest master by calling isMasterCommand on each server
* of serverConfig
* @param dbcopy{instance of db}
*
**/
Db.prototype.checkMaster_ = function(dbcopy, returnback) {
var self = dbcopy;
var hasReturned = false;
var answers = 0;
dbcopy.backup = {};
var servers = dbcopy.serverConfig.servers;
if(Array.isArray(servers)) {
for(var serveri = 0; serveri < servers.length; serveri++) {
var server = servers[serveri];
server.master = false;
if(server.connection.connection.readyState == "open" || server.connection.autoReconnect) {
var db_cmnd = DbCommand.createIsMasterCommand(dbcopy);
var connect_Callback = function(err, reply) {
if(err != null) {
if (!hasReturned && ( ++answers == dbcopy.serverConfig.servers.length)) {
if (dbcopy.backup.server && dbcopy.backup.reply) {
dbcopy.masterConnection = dbcopy.backup.server.connection;
return returnback( null, dbcopy.backup.reply, dbcopy );
} else {
return returnback( new Error( 'No master found' ) );
}
}
} else {
if(reply.documents[0].ismaster == 1) {
// Locate the master connection and save it
dbcopy.masterConnection = server.connection;
server.master = true;
hasReturned = true;
return returnback(null, reply, dbcopy);
} else {
server.master = false;
// we may not have a master so we keep a secondary server,
// that is able to respond, just in case
dbcopy.backup.server = server;
dbcopy.backup.reply = reply;
if ( !hasReturned && ( ++answers == dbcopy.serverConfig.servers.length )) {
if ( dbcopy.backup.server && dbcopy.backup.reply ) {
dbcopy.masterConnection = dbcopy.backup.server.connection;
return returnback( null, dbcopy.backup.reply, dbcopy );
} else {
return returnback(new Error( 'No master found' ));
}
}
}
}
}
dbcopy.addListener(db_cmnd.getRequestId().toString(), connect_Callback);
self.notReplied[db_cmnd.getRequestId().toString()] = server.connection;
if(server.connection.connection.readyState == "open") {
server.connection.sendwithoutReconnect(db_cmnd);
} else {
// This if it's closed it may not have a listener
// The listener is of general use so we need not use one for every command
if (!server.connection.listeners("data").length) {
server.connection.addListener("data", function(message) {
// Parse the data as a reply object
var reply = null;
if ( message ) {
reply = new MongoReply(self, message);
} else {
reply = {};
reply.responseHasError = true;
reply.documents = ['Error connecting'];
}
// Emit error if there is one
reply.responseHasError ? self.emit(reply.responseTo.toString(), reply.documents[0], reply) : self.emit(reply.responseTo.toString(), null, reply);
// Remove the listener
if(self.notReplied[ reply.responseTo.toString()]) {
delete self.notReplied[ reply.responseTo.toString()];
self.removeListener(reply.responseTo.toString(), self.listeners( reply.responseTo.toString())[0]);
}
});
}
if (server.connection.listeners("error").length == 0) {
server.connection.addListener("error", function(err) {
dbcopy.emit("error", err);
server.master = false;
});
}
// Emit timeout and close events so the client using db can figure do proper error handling (emit contains the connection that triggered the event)
if (server.connection.listeners("timeout").length == 0) {
server.connection.addListener("timeout", function() { dbcopy.emit("timeout", this); });
}
if (server.connection.listeners("close").length == 0) {
server.connection.addListener("close", function() { dbcopy.emit("close", this); });
}
server.connection.send(db_cmnd);
}
} else {
server.master = false;
if (!hasReturned && ( ++answers == dbcopy.serverConfig.servers.length)) {
if (dbcopy.backup.server && dbcopy.backup.reply) {
dbcopy.masterConnection = dbcopy.backup.server.connection;
return returnback( null, dbcopy.backup.reply, dbcopy );
} else {
return returnback( new Error( 'No master found' ) );
}
}
}
}
}
}
}
var BinaryParser = require('../bson/binary_parser').BinaryParser,
sys = require('sys');
sys = require('util'),
debug = require('util').debug,
inspect = require('util').inspect;

@@ -22,2 +24,3 @@ /**

var mongoObjectFinal = mongoObject == null ? {} : mongoObject;
this.objectId = mongoObjectFinal._id == null ? new file.db.bson_serializer.ObjectID() : mongoObjectFinal._id;

@@ -38,2 +41,3 @@ this.chunkNumber = mongoObjectFinal.n == null ? 0 : mongoObjectFinal.n;

this.data = mongoObjectFinal.data;
} else if(mongoObjectFinal.data instanceof Buffer) {
} else {

@@ -63,3 +67,3 @@ throw Error("Illegal chunk format");

Chunk.prototype.write = function(data, callback) {
this.data.write(data.toString('binary'), this.internalPosition);
this.data.write(data, this.internalPosition);
this.internalPosition = this.data.length();

@@ -78,2 +82,5 @@ callback(null, this);

Chunk.prototype.read = function(length) {
// Default to full read if no index defined
length = length == null || length == 0 ? this.length() : length;
if(this.length() - this.internalPosition + 1 >= length) {

@@ -89,16 +96,16 @@ var data = this.data.read(this.internalPosition, length);

Chunk.prototype.readSlice = function(length) {
if ((this.length() - this.internalPosition + 1) >= length) {
var data = null;
if (this.data.buffer != null) { //Pure BSON
data = this.data.buffer.slice(this.internalPosition, this.internalPosition + length);
} else { //Native BSON
data = new Buffer(length);
//todo there is performance degradation! we need direct Binary::write() into buffer with offset support!
length = data.write(this.data.read(this.internalPosition, length), 'binary', 0);
}
this.internalPosition = this.internalPosition + length;
return data;
} else {
return null;
if ((this.length() - this.internalPosition + 1) >= length) {
var data = null;
if (this.data.buffer != null) { //Pure BSON
data = this.data.buffer.slice(this.internalPosition, this.internalPosition + length);
} else { //Native BSON
data = new Buffer(length);
//length = data.write(this.data.read(this.internalPosition, length), 'binary', 0);
length = this.data.readInto(data, this.internalPosition);
}
this.internalPosition = this.internalPosition + length;
return data;
} else {
return null;
}
};

@@ -148,6 +155,7 @@

self.file.chunkCollection(function(err, collection) {
collection.remove({'_id':self.objectId}, function(err, collection) {
collection.remove({'_id':self.objectId}, {safe:true}, function(err, result) {
if(self.data.length() > 0) {
self.buildMongoObject(function(mongoObject) {
collection.insert(mongoObject, function(collection) {
collection.insert(mongoObject, {safe:true}, function(err, collection) {
callback(null, self);

@@ -154,0 +162,0 @@ });

@@ -13,9 +13,11 @@ /**

DbCommand = require('../commands/db_command').DbCommand,
Integer = require('../goog/math/integer').Integer,
// ObjectID = require('../bson/bson').ObjectID,
Buffer = require('buffer').Buffer,
fs = require('fs'),
util = require('util'),
debug = require('util').debug,
inspect = require('util').inspect,
Stream = require('stream').Stream;
var REFERENCE_BY_FILENAME = 0,
REFERENCE_BY_ID = 1;

@@ -49,5 +51,11 @@ /**

*/
var GridStore = exports.GridStore = function(db, filename, mode, options) {
this.db = db;
this.filename = filename;
var GridStore = exports.GridStore = function(db, fileIdObject, mode, options) {
this.db = db;
// set grid referencetype
this.referenceBy = typeof fileIdObject == 'string' ? 0 : 1;
this.filename = fileIdObject;
this.fileId = fileIdObject;
// Set up the rest
this.mode = mode == null ? "r" : mode;

@@ -57,3 +65,5 @@ this.options = options == null ? {} : options;

this.position = 0;
// Set default chunk size
this.internalChunkSize = Chunk.DEFAULT_CHUNK_SIZE;
/**

@@ -66,3 +76,4 @@ * The chunk size used by this file.

*/
this.__defineGetter__("chunkSize", function() { return this.internalChunkSize; });
this.__defineGetter__("chunkSize", function() {
return this.internalChunkSize; });
this.__defineSetter__("chunkSize", function(value) {

@@ -99,3 +110,26 @@ if(!(this.mode[0] == "w" && this.position == 0 && this.uploadDate == null)) {

var self = this;
if((self.mode == "w" || self.mode == "w+") && self.db.serverConfig.primary != null) {
// Get files collection
self.collection(function(err, collection) {
// Ensure index on files Collection
collection.ensureIndex([['filename', 1], ['uploadDate', -1]], function(err, index) {
// Get chunk collection
self.chunkCollection(function(err, chunkCollection) {
// Ensure index on chunk collection
chunkCollection.ensureIndex([['files_id', 1], ['n', 1]], function(err, index) {
self._open(callback);
});
});
});
});
} else {
self._open(callback);
}
}
GridStore.prototype._open = function(callback) {
var self = this;
self.collection(function(err, collection) {

@@ -106,27 +140,32 @@ if(err!==null) {

}
// Create the query
var query = self.referenceBy == REFERENCE_BY_ID ? {_id:self.fileId} : {filename:self.filename};
query = self.fileId == null && this.filename == null ? null : query;
// Fetch the chunks
self.chunkCollection(function(err, chunkCollection) {
collection.find({'filename':self.filename}, function(err, cursor) {
// Fetch the file
cursor.nextObject(function(err, doc) {
// Chek if the collection for the files exists otherwise prepare the new one
if(doc != null) {
self.fileId = doc._id;
self.contentType = doc.contentType;
self.internalChunkSize = doc.chunkSize;
self.uploadDate = doc.uploadDate;
self.aliases = doc.aliases;
self.length = doc.length;
self.metadata = doc.metadata;
self.internalMd5 = doc.md5;
} else {
self.fileId = new self.db.bson_serializer.ObjectID();
self.contentType = exports.GridStore.DEFAULT_CONTENT_TYPE;
self.internalChunkSize = Chunk.DEFAULT_CHUNK_SIZE;
self.length = 0;
}
if(query != null) {
collection.find(query, function(err, cursor) {
// Fetch the file
cursor.nextObject(function(err, doc) {
// Chek if the collection for the files exists otherwise prepare the new one
if(doc != null) {
self.fileId = doc._id;
self.contentType = doc.contentType;
self.internalChunkSize = doc.chunkSize;
self.uploadDate = doc.uploadDate;
self.aliases = doc.aliases;
self.length = doc.length;
self.metadata = doc.metadata;
self.internalMd5 = doc.md5;
} else {
self.fileId = self.fileId instanceof self.db.bson_serializer.ObjectID ? self.fileId : new self.db.bson_serializer.ObjectID();
self.contentType = exports.GridStore.DEFAULT_CONTENT_TYPE;
self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize;
self.length = 0;
}
// Process the mode of the object
if(self.mode == "r") {
chunkCollection.ensureIndex([['files_id', 1], ['n', 1]], function(err, index) {
// Process the mode of the object
if(self.mode == "r") {
self.nthChunk(0, function(err, chunk) {

@@ -137,7 +176,4 @@ self.currentChunk = chunk;

});
});
} else if(self.mode == "w") {
self.chunkCollection(function(err, collection2) {
// Create index for the chunks
//chunkCollection.ensureIndex([['files_id', 1], ['n', 1]], function(err, index) {
} else if(self.mode == "w") {
self.chunkCollection(function(err, collection2) {
// Delete any existing chunks

@@ -152,8 +188,5 @@ self.deleteChunks(function(err, result) {

});
//});
});
} else if(self.mode == "w+") {
self.chunkCollection(function(err, collection) {
// Create index for the chunks
//chunkCollection.ensureIndex([['files_id', 1], ['n', 1]], function(err, index) {
});
} else if(self.mode == "w+") {
self.chunkCollection(function(err, collection) {
self.nthChunk(self.lastChunkNumber(), function(err, chunk) {

@@ -167,9 +200,43 @@ // Set the current chunk

});
//});
});
} else {
callback(new Error("Illegal mode " + self.mode), null);
}
});
});
} else {
// Write only mode
self.fileId = new self.db.bson_serializer.ObjectID();
self.contentType = exports.GridStore.DEFAULT_CONTENT_TYPE;
self.internalChunkSize = self.internalChunkSize == null ? Chunk.DEFAULT_CHUNK_SIZE : self.internalChunkSize;
self.length = 0;
// No file exists set up write mode
if(self.mode == "w") {
self.chunkCollection(function(err, collection2) {
// Delete any existing chunks
self.deleteChunks(function(err, result) {
self.currentChunk = new Chunk(self, {'n':0});
self.contentType = self.options['content_type'] == null ? self.contentType : self.options['content_type'];
self.internalChunkSize = self.options['chunk_size'] == null ? self.internalChunkSize : self.options['chunk_size'];
self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
self.position = 0;
callback(null, self);
});
} else {
callback(new Error("Illegal mode " + self.mode), null);
}
});
});
});
} else if(self.mode == "w+") {
self.chunkCollection(function(err, collection) {
self.nthChunk(self.lastChunkNumber(), function(err, chunk) {
// Set the current chunk
self.currentChunk = chunk == null ? new Chunk(self, {'n':0}) : chunk;
self.currentChunk.position = self.currentChunk.data.length();
self.metadata = self.options['metadata'] == null ? self.metadata : self.options['metadata'];
self.position = self.length;
callback(null, self);
});
});
} else {
callback(new Error("Illegal mode " + self.mode), null);
}
}
});

@@ -201,22 +268,32 @@ });

fs.fstat(file, function (err, stats) {
var startIndices = [];
for (var i = 0; i < stats.size; i += self.chunkSize) startIndices.push(i);
startIndices.forEach(function (start, index, startIndices) {
process.nextTick(function () {
fs.read(file, self.chunkSize, start, 'binary', function (err, data, bytesRead) {
var chunk = new Chunk(self, {n: index});
chunk.write(data, function (err, chunk) {
chunk.save(function (err, result) {
if (index == startIndices.length -1) {
self.currentChunk = chunk;
self.close(function (err, result) {
callback(null, self);
});
}
});
var offset = 0;
var index = 0;
var numberOfChunksLeft = Math.min(stats.size / self.chunkSize);
// Write a chunk
var writeChunk = function() {
fs.read(file, self.chunkSize, offset, 'binary', function(err, data, bytesRead) {
offset = offset + bytesRead;
// Create a new chunk for the data
var chunk = new Chunk(self, {n:index++});
chunk.write(data, function(err, chunk) {
chunk.save(function(err, result) {
// Point to current chunk
self.currentChunk = chunk;
if(offset >= stats.size) {
fs.close(file);
self.close(function(err, result) {
return callback(null, result);
})
} else {
return process.nextTick(writeChunk);
}
});
});
});
});
}
// Process the first write
process.nextTick(writeChunk);
});

@@ -244,10 +321,9 @@ });

var finalClose = close == null ? false : close;
string = string instanceof Buffer ? string.toString("binary") : string;
// Check if we are trying to write a buffer and use the right method
if(string instanceof Buffer) return this.writeBuffer(string, close, callback);
// Otherwise let's write the data
if(self.mode[0] != "w") {
callback(new Error(self.filename + " not opened for writing"), null);
callback(new Error((self.referenceBy == REFERENCE_BY_ID ? self.toHexString() : self.filename) + " not opened for writing"), null);
} else {
if((self.currentChunk.position + string.length) > self.chunkSize) {
// sys.puts("==============================================================1")
var previousChunkNumber = self.currentChunk.chunkNumber;

@@ -295,3 +371,3 @@ var leftOverDataSize = self.chunkSize - self.currentChunk.position;

if(self.mode[0] != "w") {
callback(new Error(self.filename + " not opened for writing"), null);
callback(new Error((self.referenceBy == REFERENCE_BY_ID ? self.toHexString() : self.filename) + " not opened for writing"), null);
}

@@ -406,6 +482,6 @@ else {

if(self.uploadDate != null) {
files.remove({'_id':self.fileId}, function(err, collection) {
files.remove({'_id':self.fileId}, {safe:true}, function(err, collection) {
self.buildMongoObject(function(mongoObject) {
files.save(mongoObject, function(err, doc) {
callback(err, doc);
files.save(mongoObject, {safe:true}, function(err, doc) {
callback(err, mongoObject);
});

@@ -417,4 +493,4 @@ });

self.buildMongoObject(function(mongoObject) {
files.save( mongoObject, function(err, doc) {
callback(err, doc);
files.save(mongoObject, {safe:true}, function(err, doc) {
callback(err, mongoObject);
});

@@ -429,4 +505,4 @@ });

self.buildMongoObject(function(mongoObject) {
files.save(mongoObject, function(err, doc) {
callback(err, doc);
files.save(mongoObject, {safe:true}, function(err, doc) {
callback(err, mongoObject);
});

@@ -436,2 +512,4 @@ });

}
} else if(self.mode[0] == "r") {
callback(null, null);
} else {

@@ -472,3 +550,3 @@ callback(new Error("Illegal mode " + self.mode), null);

GridStore.prototype.lastChunkNumber = function() {
return this.db.bson_serializer.BSON.toInt((this.length/this.chunkSize));
return Math.floor(this.length/this.chunkSize);
};

@@ -508,3 +586,3 @@

}
collection.remove({'files_id':self.fileId}, function(err, result) {
collection.remove({'files_id':self.fileId}, {safe:true}, function(err, result) {
callback(null, true);

@@ -532,3 +610,3 @@ });

collection.remove({'_id':self.fileId}, function(err, collection) {
collection.remove({'_id':self.fileId}, {safe:true}, function(err, collection) {
callback(err, self);

@@ -644,4 +722,3 @@ });

GridStore.prototype.read = function(length, buffer, callback) {
var self = this;
var self = this;
var args = Array.prototype.slice.call(arguments, 0);

@@ -655,7 +732,14 @@ callback = args.pop();

var finalLength = length == null ? self.length - self.position : length;
var numberToRead = finalLength;
// debug("===================================================================== read")
// debug(inspect(self.length))
// debug(inspect(self.position))
// debug(finalLength)
if((self.currentChunk.length() - self.currentChunk.position + 1 + finalBuffer.length) >= finalLength) {
// debug("---------------------------- finalLength :: " + finalLength)
finalBuffer = finalBuffer + self.currentChunk.read(finalLength - finalBuffer.length);
numberToRead = numberToRead - finalLength;
// debug("---------------------------- finalLength :: " + finalBuffer.length)
self.position = finalBuffer.length;

@@ -665,3 +749,2 @@ callback(null, finalBuffer);

finalBuffer = finalBuffer + self.currentChunk.read(self.currentChunk.length());
numberToRead = numberToRead - self.currentChunk.length();
// Load the next chunk and read some more

@@ -676,42 +759,38 @@ self.nthChunk(self.currentChunk.chunkNumber + 1, function(err, chunk) {

GridStore.prototype.readBuffer = function(length, buffer, callback) {
var self = this;
var args = Array.prototype.slice.call(arguments, 0);
callback = args.pop();
length = args.length ? args.shift() : null;
buffer = args.length ? args.shift() : null;
var left = Math.min(self.length - self.position, length);
if(buffer===null) {
buffer = new Buffer(left);
}
var leftInCurrentChunk = self.currentChunk.length()-self.currentChunk.position;
// Everything can be read from this chunk
if((leftInCurrentChunk >= left) && leftInCurrentChunk!==0) {
var slice = self.currentChunk.readSlice(left);
self.position += left;
callback(null, slice);
}
else {
if(leftInCurrentChunk > 0) {
var slice = self.currentChunk.readSlice(leftInCurrentChunk);
self.position += leftInCurrentChunk;
slice.copy(buffer, 0, 0, leftInCurrentChunk);
}
var leftForNextChunk = left - leftInCurrentChunk;
var subBuffer = buffer.slice(leftInCurrentChunk, leftInCurrentChunk + leftForNextChunk);
self.nthChunk(self.currentChunk.chunkNumber+1, function(err, chunk) {
self.currentChunk = chunk;
self.readBuffer(leftForNextChunk, subBuffer, function(err, subb) {
if(subb!==subBuffer) {
// readBuffer returned its own buffer slice
subb.copy(buffer, leftInCurrentChunk, 0, subb.length);
}
callback(err, buffer);
});
});
}
var self = this;
var args = Array.prototype.slice.call(arguments, 0);
callback = args.pop();
length = args.length ? args.shift() : null;
buffer = args.length ? args.shift() : null;
// The data is a c-terminated string and thus the length - 1
var finalLength = length == null ? self.length - self.position : length;
var finalBuffer = buffer == null ? new Buffer(finalLength) : buffer;
// Add a index to buffer to keep track of writing position or apply current index
finalBuffer._index = buffer != null && buffer._index != null ? buffer._index : 0;
if((self.currentChunk.length() - self.currentChunk.position + 1 + finalBuffer._index) >= finalLength) {
var slice = self.currentChunk.readSlice(finalLength - finalBuffer._index);
// Copy content to final buffer
slice.copy(finalBuffer, finalBuffer._index);
// Update internal position
self.position = finalBuffer.length;
// Check if we don't have a file at all
if(finalLength == 0 && finalBuffer.length == 0) return callback(new Error("File does not exist"), null);
// Else return data
callback(null, finalBuffer);
} else {
var slice = self.currentChunk.readSlice(self.currentChunk.length());
// Copy content to final buffer
slice.copy(finalBuffer, finalBuffer._index);
// Update index position
finalBuffer._index += slice.length;
// Load next chunk and read more
self.nthChunk(self.currentChunk.chunkNumber + 1, function(err, chunk) {
self.currentChunk = chunk;
self.readBuffer(length, finalBuffer, callback);
});
}
}

@@ -772,3 +851,3 @@

var newChunkNumber = this.db.bson_serializer.BSON.toInt((targetPosition/self.chunkSize));
var newChunkNumber = Math.floor(targetPosition/self.chunkSize);
if(newChunkNumber != self.currentChunk.chunkNumber) {

@@ -850,3 +929,3 @@ if(self.mode[0] == 'w') {

*/
GridStore.DEFAULT_CONTENT_TYPE = 'text/plain';
GridStore.DEFAULT_CONTENT_TYPE = 'binary/octet-stream';
/**

@@ -879,3 +958,3 @@ * Seek mode where the given length is absolute.

*/
GridStore.exist = function(db, name, rootCollection, callback) {
GridStore.exist = function(db, fileIdObject, rootCollection, callback) {
var args = Array.prototype.slice.call(arguments, 2);

@@ -885,5 +964,9 @@ callback = args.pop();

// Fetch collection
var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION;
db.collection(rootCollectionFinal + ".files", function(err, collection) {
collection.find({'filename':name}, function(err, cursor) {
// Build query
var query = typeof fileIdObject == 'string' ? {'filename':fileIdObject} : {'_id':fileIdObject};
// Attempt to locate file
collection.find(query, function(err, cursor) {
cursor.nextObject(function(err, item) {

@@ -906,7 +989,17 @@ callback(null, item == null ? false : true);

*/
GridStore.list = function(db, rootCollection, callback) {
GridStore.list = function(db, rootCollection, options, callback) {
var args = Array.prototype.slice.call(arguments, 1);
callback = args.pop();
rootCollection = args.length ? args.shift() : null;
options = args.length ? args.shift() : {};
// Ensure we have correct values
if(rootCollection != null && typeof rootCollection == 'object') {
options = rootCollection;
rootCollection = null;
}
// Check if we are returning by id not filename
var byId = options['id'] != null ? options['id'] : false;
// Fetch item
var rootCollectionFinal = rootCollection != null ? rootCollection : GridStore.DEFAULT_ROOT_COLLECTION;

@@ -918,3 +1011,3 @@ var items = [];

if(item != null) {
items.push(item.filename);
items.push(byId ? item._id : item.filename);
} else {

@@ -1048,3 +1141,3 @@ callback(null, items);

gridStore.collection(function(err, collection) {
collection.remove({'_id':gridStore.fileId}, function(err, collection) {
collection.remove({'_id':gridStore.fileId}, {safe:true}, function(err, collection) {
callback(err, self);

@@ -1059,79 +1152,79 @@ });

var ReadStream = function(autoclose, gstore) {
if (!(this instanceof ReadStream)) return new ReadStream(autoclose, gstore);
Stream.call(this);
if (!(this instanceof ReadStream)) return new ReadStream(autoclose, gstore);
Stream.call(this);
this.autoclose = !!autoclose;
this.gstore = gstore;
this.autoclose = !!autoclose;
this.gstore = gstore;
this.finalLength = gstore.length - gstore.position;
this.completedLength = 0;
this.finalLength = gstore.length - gstore.position;
this.completedLength = 0;
this.paused = false;
this.readable = true;
this.pendingChunk = null;
this.paused = false;
this.readable = true;
this.pendingChunk = null;
var self = this;
process.nextTick(function() {
self._execute();
});
var self = this;
process.nextTick(function() {
self._execute();
});
};
util.inherits(ReadStream, Stream);
ReadStream.prototype._execute = function() {
if(this.paused === true || this.readable === false) {
return;
}
if (this.paused === true || this.readable === false) {
return;
}
var gstore = this.gstore;
var self = this;
var gstore = this.gstore;
var self = this;
var last = false;
var toRead = 0;
var last = false;
var toRead = 0;
if ((gstore.currentChunk.length() - gstore.currentChunk.position + 1 + self.completedLength) >= self.finalLength) {
toRead = self.finalLength - self.completedLength;
last = true;
} else {
toRead = gstore.currentChunk.length();
}
if ((gstore.currentChunk.length() - gstore.currentChunk.position + 1 + self.completedLength) >= self.finalLength) {
toRead = self.finalLength - self.completedLength;
last = true;
} else {
toRead = gstore.currentChunk.length();
}
var data = gstore.currentChunk.readSlice(toRead);
if (data != null) {
self.completedLength += data.length;
self.pendingChunk = null;
self.emit("data", data);
}
var data = gstore.currentChunk.read(toRead);
if (data != null) {
self.completedLength += data.length;
self.pendingChunk = null;
self.emit("data", data);
if (last === true) {
self.readable = false;
self.emit("end");
if (self.autoclose === true) {
if (gstore.mode[0] == "w") {
gstore.close(function(err, doc) {
if (err) {
self.emit("error", err);
return;
}
self.emit("close", doc);
});
} else {
self.emit("close");
}
}
if (last === true) {
} else {
gstore.nthChunk(gstore.currentChunk.chunkNumber + 1, function(err, chunk) {
if (err) {
self.readable = false;
self.emit("end");
if (self.autoclose === true) {
if (gstore.mode[0] == "w") {
gstore.close(function(err, doc) {
if (err) {
self.emit("error", err);
return;
}
self.emit("close", doc);
});
} else {
self.emit("close");
}
}
} else {
gstore.nthChunk(gstore.currentChunk.chunkNumber + 1, function(err, chunk) {
if (err) {
self.readable = false;
self.emit("error", err);
return;
}
self.pendingChunk = chunk;
if (self.paused === true) {
return;
}
gstore.currentChunk = self.pendingChunk;
self._execute();
});
}
self.emit("error", err);
return;
}
self.pendingChunk = chunk;
if (self.paused === true) {
return;
}
gstore.currentChunk = self.pendingChunk;
self._execute();
});
}
};

@@ -1153,6 +1246,5 @@

GridStore.prototype.stream = function(autoclose) {
return new ReadStream(autoclose, this);
return new ReadStream(autoclose, this);
};
/**

@@ -1162,3 +1254,3 @@ * Pauses this stream, then no farther events will be fired

ReadStream.prototype.pause = function() {
this.paused = true;
this.paused = true;
};

@@ -1170,11 +1262,13 @@

ReadStream.prototype.resume = function() {
this.paused = false;
var self = this;
if (self.pendingChunk) {
self.currentChunk = self.pendingChunk;
process.nextTick(function() {
self._execute();
});
}
this.paused = false;
var self = this;
if (self.pendingChunk) {
self.currentChunk = self.pendingChunk;
process.nextTick(function() {
self._execute();
});
}
};

@@ -1,27 +0,102 @@

var sys = require('sys')
// // Add both the BSON Pure classes and the native ones
var BSONPure = exports.BSONPure = require('./bson/bson');
var BSONNative = null
try {
BSONNative = exports.BSONNative = require('../../external-libs/bson/bson');
exports.BSONPure = require('./bson/bson');
exports.BSONNative = require('../../external-libs/bson/bson');
} catch(err) {
// do nothing
}
[
'bson/binary_parser',
'commands/base_command', 'commands/db_command', 'commands/delete_command',
'commands/get_more_command', 'commands/insert_command', 'commands/kill_cursor_command',
'commands/query_command', 'commands/update_command',
'responses/mongo_reply',
'admin', 'collection', 'connection', 'cursor', 'db',
'goog/math/integer', 'goog/math/long', 'crypto/md5',
'gridfs/chunk', 'gridfs/gridstore'
].forEach(function(path){
var module = require('./' + path);
for (var i in module)
exports[i] = module[i];
[ 'bson/binary_parser'
, 'commands/base_command'
, 'commands/db_command'
, 'commands/delete_command'
, 'commands/get_more_command'
, 'commands/insert_command'
, 'commands/kill_cursor_command'
, 'commands/query_command'
, 'commands/update_command'
, 'responses/mongo_reply'
, 'admin'
, 'collection'
, 'connections/server'
, 'connections/repl_set_servers'
, 'connection'
, 'cursor'
, 'db'
, 'goog/math/long'
, 'gridfs/grid'
, 'gridfs/chunk'
, 'gridfs/gridstore'].forEach(function (path) {
var module = require('./' + path);
for (var i in module) {
exports[i] = module[i];
}
});
// Exports all the classes for the NATIVE JS BSON Parser
exports.native = function() {
var classes = {};
// Map all the classes
[ 'bson/binary_parser'
, '../../external-libs/bson/bson'
, 'commands/base_command'
, 'commands/db_command'
, 'commands/delete_command'
, 'commands/get_more_command'
, 'commands/insert_command'
, 'commands/kill_cursor_command'
, 'commands/query_command'
, 'commands/update_command'
, 'responses/mongo_reply'
, 'admin'
, 'collection'
, 'connections/server'
, 'connections/repl_set_servers'
, 'connection'
, 'cursor'
, 'db'
, 'gridfs/grid'
, 'gridfs/chunk'
, 'gridfs/gridstore'].forEach(function (path) {
var module = require('./' + path);
for (var i in module) {
classes[i] = module[i];
}
});
// Return classes list
return classes;
}
// Exports all the classes for the PURE JS BSON Parser
exports.pure = function() {
var classes = {};
// Map all the classes
[ 'bson/binary_parser'
, './bson/bson'
, 'commands/base_command'
, 'commands/db_command'
, 'commands/delete_command'
, 'commands/get_more_command'
, 'commands/insert_command'
, 'commands/kill_cursor_command'
, 'commands/query_command'
, 'commands/update_command'
, 'responses/mongo_reply'
, 'admin'
, 'collection'
, 'connections/server'
, 'connections/repl_set_servers'
, 'connection'
, 'cursor'
, 'db'
, 'gridfs/grid'
, 'gridfs/chunk'
, 'gridfs/gridstore'].forEach(function (path) {
var module = require('./' + path);
for (var i in module) {
classes[i] = module[i];
}
});
// Return classes list
return classes;
}

@@ -1,4 +0,5 @@

var BinaryParser = require('../bson/binary_parser').BinaryParser,
Integer = require('../goog/math/integer').Integer,
Long = require('../goog/math/long').Long;
var Long = require('../goog/math/long').Long,
debug = require('util').debug,
inspect = require('util').inspect,
binaryutils = require('../bson/binary_utils');

@@ -9,39 +10,51 @@ /**

var MongoReply = exports.MongoReply = function(db, binary_reply) {
// debug("------------------------------------------------------------------------- 1")
// debug(inspect(binary_reply.length))
// // debug(inspect(data))
// for(var j = 0; j < binary_reply.length; j++) {
// // debug("------")
// debug(binary_reply[j] + " :: " + binary_reply.toString('ascii', j, j + 1))
// }
this.documents = [];
var index = 0;
// Unpack the standard header first
var messageLength = BinaryParser.toInt(binary_reply.substr(index, 4));
var messageLength = binaryutils.decodeUInt32(binary_reply, index);
index = index + 4;
// Fetch the request id for this reply
this.requestId = BinaryParser.toInt(binary_reply.substr(index, 4));
this.requestId = binaryutils.decodeUInt32(binary_reply, index);
index = index + 4;
// Fetch the id of the request that triggered the response
this.responseTo = BinaryParser.toInt(binary_reply.substr(index, 4));
this.responseTo = binaryutils.decodeUInt32(binary_reply, index);
// Skip op-code field
index = index + 4 + 4;
// Unpack the reply message
this.responseFlag = BinaryParser.toInt(binary_reply.substr(index, 4));
index = index + 4;
this.responseFlag = binaryutils.decodeUInt32(binary_reply, index);
index = index + 4;
// Unpack the cursor id (a 64 bit long integer)
this.cursorId = new db.bson_serializer.BSON.toLong(BinaryParser.toInt(binary_reply.substr(index, 4)), BinaryParser.toInt(binary_reply.substr(index + 4, 4)));
var low_bits = binaryutils.decodeUInt32(binary_reply, index);
var high_bits = binaryutils.decodeUInt32(binary_reply, index + 4);
this.cursorId = new db.bson_deserializer.Long(low_bits, high_bits);
index = index + 8;
// Unpack the starting from
this.startingFrom = BinaryParser.toInt(binary_reply.substr(index, 4));
this.startingFrom = binaryutils.decodeUInt32(binary_reply, index);
index = index + 4;
// Unpack the number of objects returned
this.numberReturned = BinaryParser.toInt(binary_reply.substr(index, 4));
this.numberReturned = binaryutils.decodeUInt32(binary_reply, index);
index = index + 4;
// Let's unpack all the bson document, 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 = BinaryParser.toInt(binary_reply.substr(index, 4));
// Read the size of the bson object
var bsonObjectSize = binaryutils.decodeUInt32(binary_reply, index);
// sys.debug("--------------------------------------------------- incoming")
// BinaryParser.hprint(binary_reply.substr(index, bsonObjectSize))
// Read the entire object and deserialize it
this.documents.push(db.bson_deserializer.BSON.deserialize(binary_reply.substr(index, bsonObjectSize)));
// Adjust for next object
// debug("================================================================== bsonObjectSize = " + bsonObjectSize)
// Deserialize the object and add to the documents array
this.documents.push(db.bson_deserializer.BSON.deserialize(binary_reply.slice(index, index + bsonObjectSize)));
// Adjust binary index to point to next block of binary bson data
index = index + bsonObjectSize;
}
}
// debug("--------------------------------------------------- docs")
// debug(inspect(this.documents))
};

@@ -48,0 +61,0 @@

{ "name" : "mongodb"
, "description" : "A node.js driver for MongoDB"
, "version" : "0.9.4"
, "version" : "0.9.6-7"
, "author" : "Christian Amor Kvalheim <christkv@gmail.com>"

@@ -20,2 +20,3 @@ , "contributors" : [ "Nathan White <nw@nwhite.net>",

, "freebsd" ]
, "config": { "native" : false }
, "main": "./lib/mongodb/index"

@@ -22,0 +23,0 @@ , "directories" : { "lib" : "./lib/mongodb" }

@@ -83,8 +83,12 @@ Install

GridStore
========
=========
The GridStore class allows for storage of binary files in mongoDB using the mongoDB defined files and chunks collection definition.
See the gridfs.js file under examples/ for how to use it or view the integration tests marked with test_gs_...
For more information have a look at [Gridstore](https://github.com/christkv/node-mongodb-native/blob/master/docs/gridfs.md)
Replicasets
===========
For more information about how to connect to a replicaset have a look at [Replicasets](https://github.com/christkv/node-mongodb-native/blob/master/docs/replicaset.md)
Notes

@@ -158,4 +162,4 @@ ========

If this document doesn't answer your questions, see the source of
[Collection](https://github.com/mongodb/mongo-python-driver/blob/master/pymongo/connection.py)
or [Cursor](https://github.com/mongodb/mongo-python-driver/blob/master/pymongo/cursor.py),
[Collection](https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/collection.js)
or [Cursor](https://github.com/christkv/node-mongodb-native/blob/master/lib/mongodb/cursor.js),
or the documentation at MongoDB for query and update formats.

@@ -162,0 +166,0 @@

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc