Comparing version 0.9.4 to 0.9.6-7
@@ -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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 4 instances in 1 package
Mixed license
License(Experimental) Package contains multiple licenses.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Native code
Supply chain riskContains native code (e.g., compiled binaries or shared libraries). Including native code can obscure malicious behavior.
Found 24 instances in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
183
24642
0
382
10
1527471
2
80
162