UNPKG

tingodb

Version:

Embedded Node.js database upward compatible with MongoDB

210 lines (187 loc) 5.45 kB
var safe = require('safe'); var tcoll = require('./tcoll.js'); var fs = require('fs'); var path = require('path'); var _ = require('lodash'); var EventEmitter = require('events').EventEmitter; var Buffer = require("safe-buffer").Buffer; var mstore = Object.create(null); function tdb(path_, opts, gopts) { EventEmitter.call(this); this._gopts = gopts; this._path = path.resolve(path_); this._cols = Object.create(null); this._name = opts.name || path.basename(path_); this._stype = gopts.memStore?"mem":"fs"; if (this._stype=="mem") mstore[path_] = this._mstore = mstore[path_] || Object.create(null); // mongodb compat variables this.openCalled = false; } safe.inherits(tdb, EventEmitter); module.exports = tdb; tdb.prototype.open = function (options, cb) { // actually do nothing for now, we are inproc // so nothing to open/close... collection will keep going on their own if (cb==null) cb = options; cb = cb || function () {}; this.openCalled = true; safe.back(cb,null,this); }; tdb.prototype.close = function (forceClose, cb) { var self = this; if (cb==null) cb = forceClose; cb = cb || function () {}; // stop any further operations on current collections safe.eachOf(self._cols, function (c, k, cb) { c._stop(cb); }, safe.sure(cb, function () { // and clean list self._cols = Object.create(null); this.openCalled = false; safe.back(cb,null,this); })); }; tdb.prototype.createIndex = _.rest(function (c, args) { c = this._cols[c]; if (!c) return safe.back(args[args.length - 1], new Error("Collection doesn't exists")); c.createIndex.apply(c, args); }); tdb.prototype.collection = function (cname, opts, cb) { return this._collection(cname, opts,false, cb); }; tdb.prototype.createCollection = function (cname, opts, cb) { return this._collection(cname, opts,true, cb); }; tdb.prototype._nameCheck = function (cname) { var err = null; if (!_.isString(cname)) err = new Error("collection name must be a String"); if (!err && cname.length==0) err = new Error("collection names cannot be empty"); if (!err && cname.indexOf("$")!=-1) err = new Error("collection names must not contain '$'"); if (!err) { var di = cname.indexOf("."); if (di==0 || di==cname.length-1) err = new Error("collection names must not start or end with '.'"); } if (!err && cname.indexOf("..")!=-1) err = new Error("collection names cannot be empty"); return err; }; tdb.prototype._collection = function (cname, opts, create, cb) { var err = this._nameCheck(cname); if (!cb) { cb = opts; opts = {}; } cb = cb || function () {}; if (err) return safe.back(cb, err); var self = this; var c = self._cols[cname]; if (c) { if (opts.strict && create) safe.back(cb, new Error("Collection " + cname + " already exists. Currently in safe mode.")); else safe.back(cb, null, c); return c; } c = new tcoll(this); self._cols[cname] = c; c.init(this, cname, opts, create, function (err) { if (err) { delete self._cols[cname]; cb(err); } else cb(null, c); }); return c; }; tdb.prototype.collectionNames = function (opts, cb) { var self = this; if (_.isUndefined(cb)) { cb = opts; opts = {}; } if (this._stype=="mem") { cb(null, _.map(self._mstore, function (v, e) { return opts.namesOnly ? e : { name: self._name + "." + e }; })); } else { fs.readdir(self._path, safe.sure(cb,function(files) { // some collections ca be on disk and some only in memory, we need both files = _(self._cols).keys().union(files); cb(null, files .reject(function (e) { return /^\./.test(e); }) // ignore hidden linux alike files .map(function (e) { return opts.namesOnly ? e : { name: self._name + "." + e }; }) .value()); })); } }; tdb.prototype.collections = function (cb) { var self = this; self.collectionNames({namesOnly:1},safe.sure(cb, function (names) { safe.forEach(names, function (cname, cb) { self.collection(cname, cb); },safe.sure(cb, function () { cb(null, _.values(self._cols)); })); })); }; tdb.prototype.dropCollection = function (cname, cb) { var self = this; var c = this._cols[cname]; if (!c) { var err = new Error("ns not found"); if (cb) return safe.back(cb, err); throw new err; } c._stop(safe.sure(cb, function (ondisk) { delete self._cols[cname]; if (ondisk) fs.unlink(path.join(self._path,cname),safe.sure(cb, function () { cb(null, true); })); else { if (self._stype=="mem") delete self._mstore[cname]; cb(null,true); } })); }; tdb.prototype.dropDatabase = function (cb) { var self = this; self.collections(safe.sure(cb, function(collections) { safe.forEach(collections, function (c, cb) { self.dropCollection(c.collectionName,cb); },cb); })); }; tdb.prototype.compactDatabase = function (cb) { var self = this; self.collections(safe.sure(cb, function(collections) { safe.forEach(collections, function (c, cb) { c.compactCollection(cb); },cb); })); }; tdb.prototype.renameCollection = function (on,nn,opts,cb) { if (cb==null) { cb = opts; opts = {}; } cb = cb || safe.noop; var old = this._cols[on]; if (old) old.rename(nn, {}, cb); else safe.back(cb); }; tdb.prototype._cloneDeep = function (obj) { var self = this; return _.cloneDeepWith(obj, function (c) { if (c instanceof self.ObjectID) return new c.constructor(c.toString()); if (c instanceof self.Binary) return new c.constructor(new Buffer(c.value(true))); }); };