diff --git a/.gitignore b/.gitignore index 3b91134..c553561 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /node_modules/ -/tests/coverage/ \ No newline at end of file +/tests/coverage/ +.DS_Store diff --git a/lib/adapters/tingodb.js b/lib/adapters/tingodb.js new file mode 100644 index 0000000..dae40e9 --- /dev/null +++ b/lib/adapters/tingodb.js @@ -0,0 +1,237 @@ +/** + * Module dependencies + * mongodb adapter with a few tweaks to run tingodb + */ +var tingodb = require('tingodb'); +var fs = require('fs'); + +exports.initialize = function initializeSchema(schema, callback) { + if (!tingodb) + return; + var s = schema.settings; + + s.database = s.database || '/db/data'; + s.nativeObjectID = s.nativeObjectID || false; + s.cacheSize = s.cacheSize || 1000; + s.cacheMaxObjSize = s.cacheMaxObjSize || 1024; + s.searchInArray = s.searchInArray || false; + + schema.adapter = new TingoDB(s, schema, callback); +}; + +function TingoDB(s, schema, callback) { + this.name = 'tingodb'; + this._models = {}; + this.collections = {}; + + new tingodb.Db(s.database, s).open(function(err, client) { + if (err) + throw err; + + if(client) + this.client = client; + schema.client = client; + callback(); + }.bind(this)); +} + +TingoDB.prototype.define = function(descr) { + if (!descr.settings) + descr.settings = {}; + var self = this; + this._models[descr.model.modelName] = descr; + setTimeout(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); + } + }); + }, 1000); +}; + +TingoDB.prototype.defineProperty = function(model, prop, params) { + this._models[model].properties[prop] = params; +}; + +TingoDB.prototype.collection = function(name) { + if (!this.collections[name]) { + this.collections[name] = this.client.Collection(this.client, name); + } + return this.collections[name]; +}; + +TingoDB.prototype.ensureIndex = function(model, fields, params, callback) { + this.collection(model).ensureIndex(fields, params); + return callback(null); +}; + +TingoDB.prototype.create = function(model, data, callback) { + if (data.id === null) { + delete data.id; + } + this.collection(model).insert(data, {}, function(err, m) { + callback(err, err ? null : m[0]._id); + }); +}; + +TingoDB.prototype.save = function(model, data, callback) { + var id = data.id; + this.collection(model).update({_id: id}, data, function(err) { + callback(err); + }); +}; + +TingoDB.prototype.exists = function(model, id, callback) { + this.collection(model).findOne({_id: id}, function(err, data) { + callback(err, !err && data); + }); +}; + +TingoDB.prototype.findById = function findById(model, id, callback) { + this.collection(model).findOne({_id: id}, function(err, data) { + if (data) + data.id = id; + callback(err, data); + }); +}; + +TingoDB.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? + } + }); + } + }); +}; + +TingoDB.prototype.destroy = function destroy(model, id, callback) { + this.collection(model).remove({_id: id}, callback); +}; + + +TingoDB.prototype.remove = function remove(model, filter, callback) { + var cond = buildWhere(filter.where); + this.collection(model).remove(cond, callback); +}; + +TingoDB.prototype.all = function all(model, filter, callback) { + if (!filter) { + filter = {}; + } + var query = {}; + if (filter.where) { + query = buildWhere(filter.where); + } + var cursor = this.collection(model).find(query); + + if (filter.order) { + var keys = filter.order; + if (typeof keys === 'string') { + keys = keys.split(','); + } + var args = {}; + for (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) { + cursor.skip(filter.skip); + } else if (filter.offset) { + cursor.skip(filter.offset); + } + cursor.toArray(function(err, data) { + if (err) + return callback(err); + callback(null, data.map(function(o) { + o.id = o._id; + return o; + })); + }); +}; + +TingoDB.prototype.destroyAll = function destroyAll(model, callback) { + this.collection(model).remove({}, callback); +}; + +TingoDB.prototype.count = function count(model, callback, filter) { + var cond = buildWhere(filter); + this.collection(model).count(cond, callback); +}; + +TingoDB.prototype.updateAttributes = function updateAttrs(model, id, data, callback) { + this.collection(model).findAndModify({_id: id}, [['_id', 'asc']], {$set: data}, {}, callback); +}; + +TingoDB.prototype.disconnect = function() { + this.client.close(); +}; + +function buildWhere(filter) { + var query = {}; + Object.keys(filter).forEach(function(k) { + var cond = filter[k]; + var spec = false; + if (k === 'id') { + k = '_id'; + } + 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; +} \ No newline at end of file