caminte
Version:
ORM for every database: redis, mysql, neo4j, mongodb, rethinkdb, postgres, sqlite, tingodb
392 lines (356 loc) • 11.6 kB
JavaScript
/**
* Module dependencies
*/
var utils = require('../utils');
var safeRequire = utils.safeRequire;
var mongodb = safeRequire('mongodb');
var ObjectID = mongodb.ObjectID;
var url = require('url');
exports.initialize = function initializeSchema(schema, callback) {
'use strict';
if (!mongodb) {
return;
}
var s = schema.settings;
if (schema.settings.rs) {
s.rs = schema.settings.rs;
if (schema.settings.url) {
var uris = schema.settings.url.split(',');
s.hosts = [];
s.ports = [];
uris.forEach(function (uri) {
var durl = url.parse(uri);
s.hosts.push(durl.hostname || 'localhost');
s.ports.push(parseInt(durl.port || '27017', 10));
if (!s.database)
s.database = durl.pathname.replace(/^\//, '');
if (!s.username)
s.username = durl.auth && durl.auth.split(':')[0];
if (!s.password)
s.password = durl.auth && durl.auth.split(':')[1];
});
}
s.database = s.database || 'test';
} else {
if (schema.settings.url) {
var durl = url.parse(schema.settings.url);
s.host = durl.hostname;
s.port = durl.port;
s.database = durl.pathname.replace(/^\//, '');
s.username = durl.auth && durl.auth.split(':')[0];
s.password = durl.auth && durl.auth.split(':')[1];
}
s.host = s.host || 'localhost';
s.port = parseInt(s.port || '27017', 10);
s.database = s.database || process.env.USER || 'test';
}
s.safe = s.safe || false;
schema.adapter = new MongoDB(s, schema, callback);
schema.ObjectID = ObjectID;
};
function MongoDB(s, schema, callback) {
var i, n;
this.name = 'mongodb';
this._models = {};
this.collections = {};
this.schema = schema;
this.s = s;
var server;
if (s.rs) {
var sets = [];
for (i = 0, n = s.hosts.length; i < n; i++) {
sets.push(new mongodb.Server(s.hosts[i], s.ports[i], {auto_reconnect: true}));
}
server = new mongodb.ReplSet(sets, {rs_name: s.rs});
} else {
server = new mongodb.Server(s.host, s.port, {});
}
new mongodb.Db(s.database, server, {safe: s.safe}).open(function (err, client) {
if (err) {
throw err;
}
this.client = client;
this.schema = schema;
this.connection()
.then(callback);
}.bind(this));
}
MongoDB.prototype.connection = function () {
var t = this;
return new Promise(function(resolve, reject) {
if (t.s.username && t.s.password) {
t.client.authenticate(t.s.username, t.s.password, function (err, result) {
if (err) {
reject(err);
} else {
t.schema.client = t.client;
resolve();
}
});
} else {
t.schema.client = t.client;
setImmediate(resolve);
}
});
};
MongoDB.prototype.define = function (descr) {
if (!descr.settings)
descr.settings = {};
var self = this;
this._models[descr.model.modelName] = descr;
this.connection().then(function() {
Object.keys(descr.properties).forEach(function (k) {
if (typeof descr.properties[k].index !== 'undefined' || typeof descr.properties[k].unique !== 'undefined') {
var fields = {}, params = {};
fields[k] = 1;
params['name'] = '_' + k + '_';
if (typeof descr.properties[k].unique !== 'undefined') {
params['unique'] = true;
}
self.collection(descr.model.modelName).ensureIndex(fields, params);
}
});
});
};
MongoDB.prototype.defineProperty = function (model, prop, params) {
this._models[model].properties[prop] = params;
};
MongoDB.prototype.collection = function (name) {
if (this.client.collection) {
return this.client.collection(name);
} else {
if (!this.collections[name]) {
this.collections[name] = new mongodb.Collection(this.client, name);
}
return this.collections[name];
}
};
MongoDB.prototype.ensureIndex = function (model, fields, params, callback) {
this.collection(model).ensureIndex(fields, params);
return callback(null);
};
MongoDB.prototype.create = function (model, data, callback) {
if (data.id === null) {
delete data.id;
}
this.collection(model).insert(data, {}, function (err, m) {
var inserted;
inserted = m[0] && m[0]._id ? m[0]._id : null;
inserted = m.ops && m.ops[0] && m.ops[0]._id ? m.ops[0]._id : inserted;
callback(err, err ? null : inserted);
});
};
MongoDB.prototype.save = function (model, data, callback) {
var id = data.id;
id = getObjectId(id);
this.collection(model).update({_id: id}, data, function (err) {
callback(err);
});
};
/**
* Update rows
* @param {String} model
* @param {Object} filter
* @param {Object} data
* @param {Function} callback
*/
MongoDB.prototype.update = function (model, filter, data, callback) {
if ('function' === typeof filter) {
return filter(new Error("Get parametrs undefined"), null);
}
if ('function' === typeof data) {
return data(new Error("Set parametrs undefined"), null);
}
filter = filter.where ? filter.where : filter;
if (filter.id) {
var id = getObjectId(filter.id);
filter.id = id;
}
this.collection(model).update(filter, {'$set': data}, {w: 1, multi: true}, function (err) {
return callback && callback(err, 0);
});
};
MongoDB.prototype.exists = function (model, id, callback) {
id = getObjectId(id);
this.collection(model).findOne({_id: id}, function (err, data) {
return callback && callback(err, !err && data);
});
};
MongoDB.prototype.findById = function findById(model, id, callback) {
var self = this;
id = getObjectId(id);
self.collection(model).findOne({_id: id}, function (err, data) {
if (data) {
data.id = id;
data = self.fromDatabase(model, data);
}
callback(err, data);
});
};
MongoDB.prototype.updateOrCreate = function updateOrCreate(model, data, callback) {
var adapter = this;
if (!data.id)
return this.create(data, callback);
this.find(model, data.id, function (err, inst) {
if (err)
return callback(err);
if (inst) {
adapter.updateAttributes(model, data.id, data, callback);
} else {
delete data.id;
adapter.create(model, data, function (err, id) {
if (err)
return callback(err);
if (id) {
data.id = id;
delete data._id;
callback(null, data);
} else {
callback(null, null); // wtf?
}
});
}
});
};
MongoDB.prototype.destroy = function destroy(model, id, callback) {
id = getObjectId(id);
this.collection(model).remove({_id: id}, callback);
};
MongoDB.prototype.remove = function remove(model, filter, callback) {
var cond = buildWhere(filter.where);
this.collection(model).remove(cond, callback);
};
MongoDB.prototype.all = MongoDB.prototype.find = function all(model, filter, callback) {
if (!filter) {
filter = {};
}
var query = {};
if (filter.where) {
query = buildWhere(filter.where);
}
var self = this, cursor = this.collection(model).find(query);
if (filter.order) {
var keys = filter.order;
if (typeof keys === 'string') {
keys = keys.split(',');
}
var args = {};
for (var index in keys) {
var m = keys[index].match(/\s+(A|DE)SC$/);
var key = keys[index];
key = key.replace(/\s+(A|DE)SC$/, '').trim();
if (m && m[1] === 'DE') {
args[key] = -1;
} else {
args[key] = 1;
}
}
cursor.sort(args);
}
if (filter.limit) {
cursor.limit(filter.limit);
}
if (filter.skip || filter.offset) {
cursor.skip(filter.skip || filter.offset);
}
cursor.toArray(function (err, data) {
if (err) {
return callback(err);
}
callback(null, data.map(function (o) {
return self.fromDatabase(model, o);
}));
});
};
MongoDB.prototype.destroyAll = function destroyAll(model, callback) {
this.collection(model).remove({}, callback);
};
MongoDB.prototype.count = function count(model, callback, filter) {
var cond = {};
if (filter && filter.where) {
cond = buildWhere(filter.where);
} else {
cond = buildWhere(filter);
}
this.collection(model).count(cond, callback);
};
MongoDB.prototype.updateAttributes = function updateAttrs(model, id, data, callback) {
id = getObjectId(id);
this.collection(model).findAndModify({_id: id}, [['_id', 'asc']], {$set: data}, {}, callback);
};
MongoDB.prototype.fromDatabase = function (model, data) {
var props = this._models[model].properties;
var clean = {};
Object.keys(data).forEach(function (key) {
if (!props[key]) {
return;
}
if (props[key].type.name.toString().toLowerCase() === 'date') {
if (data[key]) {
clean[key] = new Date(data[key]);
} else {
clean[key] = data[key];
}
} else {
clean[key] = data[key];
}
});
clean.id = data._id;
return clean;
};
MongoDB.prototype.disconnect = function () {
this.client.close();
};
function getObjectId(id) {
if (typeof id === 'string') {
id = new ObjectID(id);
} else if (typeof id === 'object' && id.constructor === Array) {
id = new ObjectID(id[0]);
}
return id;
}
function buildWhere(filter) {
var query = {};
Object.keys(filter).forEach(function (k) {
var cond = filter[k];
var spec = false;
if (k === 'id') {
k = '_id';
}
if (k === 'or') {
var arrcond = [];
Object.keys(cond).forEach(function (k2) {
var nval = {};
nval[k2] = cond[k2]
arrcond.push(nval);
});
query['$or'] = arrcond;
return;
}
if (cond && cond.constructor.name === 'Object') {
spec = Object.keys(cond)[0];
cond = cond[spec];
}
if (spec) {
if (spec === 'between') {
query[k] = {$gte: cond[0], $lte: cond[1]};
} else {
query[k] = {};
spec = spec === 'inq' ? 'in' : spec;
spec = spec === 'like' ? 'regex' : spec;
if (spec === 'nlike') {
query[k]['$not'] = new RegExp(cond, 'i');
} else {
query[k]['$' + spec] = cond;
}
}
} else {
if (cond === null) {
query[k] = {$type: 10};
} else {
query[k] = cond;
}
}
});
return query;
}