prodio-leveldb-store
Version:
A level DB adapter for prodio-stores
204 lines (191 loc) • 5.16 kB
JavaScript
try{
var levelup = require('levelup');
}catch(e){
console.error('LevelUp is NOT installed, please "npm install levelup" to use this module');
throw e;
}
var sift = require('sift');
var uuid = require('bson-objectid');
var async = require('async');
var noop = function(){};
var isNumeric = function (n) {
return !isNaN(parseFloat(n)) && isFinite(n);
};
var _stores = {}, _db, provider;
var Store = module.exports = function(collectionName){
var self = this;
self.collectionName = collectionName;
};
Store.init = function(config){
var location = config.location||'./data';
var providerName = config.provider||'leveldown';
try{
var provider = require(providerName);
}catch(e){
console.error(provider+' is NOT installed, please "npm install '+provider+'" to use this module');
throw e;
}
_db = levelup(location, {db: provider, valueEncoding: 'json'});
};
Store.prototype.get = function(_id, callback){
var self = this;
_db.get(self.collectionName+':'+_id, function(err, record){
if(err){
return callback(err.toString());
}
callback(null, {
root: 'record',
record: record
});
});
};
Store.prototype.insert = function(record, callback){
var self = this, store = self._store, id = record.id || record._id;
if(!id){
id = uuid();
}
record._created = new Date();
record._id = id;
_db.put(self.collectionName+':'+id, record, function(err){
if(err){
return callback(err.toString());
}
return callback(null, {root: 'record', record: record});
});
};
Store.prototype.update = function(_id, record, callback){
var self = this;
_db.get(self.collectionName+':'+_id, function(err, curr){
if(err){
return callback(err.toString());
}
if(record){
record._created = curr._created;
record._updated = new Date();
record._id = _id;
_db.put(self.collectionName+':'+_id, record, function(err){
if(err){
return callback(err.toString());
}
return callback(null, {root: 'record', record: record});
});
}else{
return callback('Record with ID of '+_id+' does not exist!');
}
});
};
var buildCompareFunc = function(o){
var keys = Object.keys(o), val, ord;
var src = 'var cmp = '+(function(a, b){
var v;
if(!isNaN(parseFloat(a)) && isFinite(b)){
v = a-b;
if(v>0) return 1;
if(v<0) return -1;
return 0;
}else{
return (""+a).localeCompare(""+b);
}
}).toString()+'\r\n';
keys.forEach(function(key){
val = o[key];
if(val>0){
ord = 'a.'+key+', b.'+key;
}else if(val<0){
ord = 'b.'+key+', a.'+key;
}
src += 'v = cmp('+ord+');\r\n'+
'if(v!=0) return v\r\n';
});
src+='return 0;';
return new Function('a', 'b', src);
};
Store.prototype.asArray = function(opts, callback){
var self = this, records = [];
var options = opts || {};
var offset = isNumeric(options.offset)?parseInt(options.offset):0;
var limit = isNumeric(options.limit)?parseInt(options.limit):100;
var filter = (function(filter){
if(filter){
return function(rec){
return sift(filter, [rec.value]).length > 0;
};
}else{
return function(){
return true;
};
}
})(options.filter);
var sortFunc = false;
if(options.sort){
sortFunc = buildCompareFunc(options.sort);
}
options = options || {};
_db.createReadStream({
start: self.collectionName+':',
end: self.collectionName+':\xFF'
})
.on('data', function(data){
var ismatch = filter(data);
if(ismatch){
records.push(data.value);
}
})
.on('error', callback)
.on('close', function(){
var count = records.length;
if(sortFunc){
records.sort(sortFunc);
}
records = records.slice(offset, offset+limit);
process.nextTick(function(){
var result = {length: count, count: records.length, limit: limit, offset: offset, root: 'response', response: records};
callback(null, result);
});
});
};
Store.prototype.upsert = function(key, record, callback){
var self = this;
self.asArray({filter: key}, function(err, recs){
if(err){
return callback(err.toString());
}
recs = recs[recs.root];
if((!recs)||recs.length==0){
self.insert(record, callback);
}else{
var results = [];
async.each(recs, function(rec, next){
self.update(rec._id, record, function(err, data){
results.push(data);
next();
});
}, function(){
callback(null, results.length>1?results:results[0]);
});
}
});
};
Store.prototype.delete = function(id, callback){
var self = this;
_db.del(self.collectionName+':'+id, function(err){
if(err){
callback(err.toString());
}
callback(null, true);
});
};
Store.prototype.ensure = function(record, callback){
var self = this;
self.asArray({filter: record}, function(err, recs){
if(err){
return callback(err.toString());
}
recs = recs[recs.root];
if((!recs)||recs.length==0){
self.insert(record, callback);
}else{
callback(null, recs[0]);
}
});
};