You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

251 lines
6.5 KiB

12 years ago
var mongodb = require('mongodb');
12 years ago
var DRIVER_COLLECTION_PROTO = mongodb.Collection.prototype;
var DRIVER_CURSOR_PROTO = mongodb.Cursor.prototype;
var DRIVER_DB_PROTO = mongodb.Db.prototype;
14 years ago
var noop = function() {};
12 years ago
var memoize = function(fn) {
var callFn = function(callback) {
var stack = [callback];
12 years ago
action = function(callback) {
stack.push(callback);
};
12 years ago
fn(function(err, val) {
action = err ? callFn : function(callback) {
callback(null, val);
};
12 years ago
while (stack.length) stack.shift()(err, val);
});
};
12 years ago
var action = callFn;
12 years ago
return function(callback) {
action(callback);
};
};
12 years ago
var forEachMethod = function(oldProto, newProto, fn) {
Object.keys(oldProto).forEach(function(methodName) {
if (oldProto.__lookupGetter__(methodName) || newProto[methodName]) return;
if (methodName[0] === '_' || typeof oldProto[methodName] !== 'function') return;
fn(methodName, oldProto[methodName]);
});
14 years ago
};
12 years ago
var getCallback = function(args) {
var callback = args[args.length-1];
return typeof callback === 'function' ? callback : noop;
};
14 years ago
12 years ago
// Proxy for the native cursor prototype that normalizes method names and
// arguments to fit the mongo shell.
14 years ago
var Cursor = function(oncursor) {
12 years ago
this._get = oncursor;
14 years ago
};
Cursor.prototype.toArray = function() {
12 years ago
this._apply(DRIVER_CURSOR_PROTO.toArray, arguments);
14 years ago
};
12 years ago
14 years ago
Cursor.prototype.next = function() {
12 years ago
this._apply(DRIVER_CURSOR_PROTO.nextObject, arguments);
14 years ago
};
12 years ago
14 years ago
Cursor.prototype.forEach = function() {
12 years ago
this._apply(DRIVER_CURSOR_PROTO.each, arguments);
14 years ago
};
12 years ago
14 years ago
Cursor.prototype.count = function() {
12 years ago
this._apply(DRIVER_CURSOR_PROTO.count, arguments);
14 years ago
};
12 years ago
Cursor.prototype.limit = function() {
return this._config(DRIVER_CURSOR_PROTO.limit, arguments);
14 years ago
};
12 years ago
14 years ago
Cursor.prototype.skip = function() {
12 years ago
return this._config(DRIVER_CURSOR_PROTO.skip, arguments);
14 years ago
};
12 years ago
Cursor.prototype.batchSize = function() {
12 years ago
return this._config(DRIVER_CURSOR_PROTO.batchSize, arguments);
};
14 years ago
12 years ago
Cursor.prototype.sort = function() {
return this._config(DRIVER_CURSOR_PROTO.sort, arguments);
};
12 years ago
Cursor.prototype._apply = function(fn, args) {
this._get(function(err, cursor) {
if (err) return getCallback(args)(err);
fn.apply(cursor, args);
});
14 years ago
12 years ago
return this;
14 years ago
};
12 years ago
Cursor.prototype._config = function(fn, args) {
if (typeof args[args.length-1] !== 'function') return this._apply(fn, args);
args = Array.prototype.slice.call(arguments);
var callback = args.pop();
return fn.apply(this, args).toArray(callback);
14 years ago
};
12 years ago
// Proxy for the native collection prototype that normalizes method names and
// arguments to fit the mongo shell.
var Collection = function(oncollection) {
12 years ago
this._get = oncollection;
14 years ago
};
Collection.prototype.find = function() {
var args = Array.prototype.slice.call(arguments);
12 years ago
var oncollection = this._get;
var oncursor = memoize(function(callback) {
args.push(callback);
oncollection(function(err, collection) {
if (err) return callback(err);
collection.find.apply(collection, args);
});
});
14 years ago
if (typeof args[args.length-1] === 'function') {
var callback = args.pop();
12 years ago
oncursor(function(err, cursor) {
if (err) return callback(err);
cursor.toArray(callback);
});
14 years ago
}
return new Cursor(oncursor);
};
12 years ago
14 years ago
Collection.prototype.findOne = function() { // see http://www.mongodb.org/display/DOCS/Queries+and+Cursors
var args = Array.prototype.slice.call(arguments);
var callback = args.pop();
this.find.apply(this, args).limit(1).next(callback);
};
12 years ago
14 years ago
Collection.prototype.findAndModify = function(options, callback) {
12 years ago
this._apply(DRIVER_COLLECTION_PROTO.findAndModify, [options.query, options.sort || [], options.update || {}, {
new:!!options.new,
remove:!!options.remove,
14 years ago
upsert:!!options.upsert,
fields:options.fields
}, callback]);
};
12 years ago
Collection.prototype.remove = function() {
12 years ago
this._apply(DRIVER_COLLECTION_PROTO.remove, arguments.length === 0 ? [{}] : arguments); // driver has a small issue with zero-arguments in remove
};
12 years ago
Collection.prototype.group = function(group, callback) {
12 years ago
this._apply(DRIVER_COLLECTION_PROTO.group, [group.key, group.cond, group.initial, group.reduce, group.finalize, true], callback);
};
12 years ago
forEachMethod(DRIVER_COLLECTION_PROTO, Collection.prototype, function(methodName, fn) {
Collection.prototype[methodName] = function() { // we just proxy the rest of the methods directly
this._apply(fn, arguments);
};
});
Collection.prototype._apply = function(fn, args) {
this._get(function(err, collection) {
if (err) return getCallback(args)(err);
if (!collection.opts || getCallback(args) === noop) return fn.apply(collection, args);
var safe = collection.opts.safe;
collection.opts.safe = true;
fn.apply(collection, args);
collection.opts.safe = safe;
});
};
14 years ago
12 years ago
var toConnectionString = function(conf) {
var options = [];
var hosts = conf.replSet ? conf.replSet.members || conf.replSet : [conf];
var auth = conf.username ? (conf.username+':'+conf.password+'@') : '';
12 years ago
hosts = hosts.map(function(server) {
if (typeof server === 'string') return server;
return (server.host || '127.0.0.1') + ':' + (server.port || 27017);
}).join(',');
12 years ago
if (conf.slaveOk) options.push('slaveOk=true');
return 'mongodb://'+auth+hosts+'/'+conf.db+'?'+options.join('&');
14 years ago
};
12 years ago
var parseConfig = function(cs) {
if (typeof cs === 'object' && cs) return toConnectionString(cs);
cs = cs.replace(/^\//, '');
if (cs.indexOf('/') < 0) return parseConfig('127.0.0.1/'+cs);
if (cs.indexOf('mongodb://') !== 0) return parseConfig('mongodb://'+cs);
return cs;
};
12 years ago
var connect = function(config, collections) {
14 years ago
var that = {};
12 years ago
var connectionString = parseConfig(config);
12 years ago
var ondb = memoize(function(callback) {
mongodb.Db.connect(connectionString, function(err, db) {
if (err) return callback(err);
that.client = db;
callback(null, db);
});
});
12 years ago
that.bson = mongodb.BSONPure;
that.ObjectId = function(id) {
return new mongodb.BSONNative.ObjectID(id);
};
14 years ago
that.collection = function(name) {
12 years ago
if (that[name]) return that[name];
var oncollection = memoize(function(callback) {
ondb(function(err, db) {
if (err) return callback(err);
db.collection(name, callback);
});
});
return that[name] = new Collection(oncollection);
14 years ago
};
12 years ago
collections = collections || config.collections || [];
collections.forEach(that.collection);
12 years ago
forEachMethod(DRIVER_DB_PROTO, that, function(methodName, fn) {
that[methodName] = function() {
var args = arguments;
12 years ago
ondb(function(err, db) {
if (err) return getCallback(args)(err);
fn.apply(db, args);
});
};
});
14 years ago
return that;
};
12 years ago
connect.connect = connect; // backwards compat
module.exports = connect;