├── .gitignore ├── README.md ├── microdb.js ├── package.json └── tests └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | test.db 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | MicroDB 2 | ========== 3 | 4 | Purpose 5 | ------- 6 | nodejs-microdb is a *tiny* in-process database. It has very few methods, but 7 | does feature auto-flushing to disk (for file based stores). It can also be 8 | used in a memory-only mode. 9 | 10 | TOC 11 | --- 12 | * [Usage](#usage) 13 | * [Options](#options) 14 | * [API](#api) 15 | * [Status](#status) 16 | * [Contributors](#contrib) 17 | * [Compatibility](#compat) 18 | * [Licence](#lic) 19 | 20 | Usage 21 | ------------------------- 22 | 23 | var microdb = require('nodejs-microdb'); 24 | var myDB = new microdb({'file':'somefile.db'}); 25 | 26 | Options 27 | ----------------------------- 28 | 29 | When making a new database, you have some choices: 30 | 31 | var myDB = new microdb({ 32 | 'file': '', // The filename to save to, or empty for memory only 33 | 34 | 'savetime': 10, // In minutes, how often to flush to disk (approx) 35 | // Set this to 0 to diable auto-save. 36 | 37 | 'datatype': 0, // Which data-type: 38 | // 0 = Array-based, no keys. (useful for storing lists to disk) 39 | // 1 = Object-based, with keys. (what most key/doc's do) 40 | 41 | 'maxrec': 10, // Maximum number of records (for datatype === 0 ONLY) 42 | 43 | 'flushonexit': true, // Auto-flush when program quits. 44 | // I recommend you leave this on. 45 | 46 | 'defalutClean': false, // Auto-remove incomplete records on sort opertaions. 47 | // This is useful if your document type isn't consistent. 48 | // It can also be turned on per-query. 49 | }); 50 | 51 | API 52 | --------------------- 53 | 54 | ### Datatype === 1 Methods: 55 | 56 | ####MicroData.add(data, \[ident\]); 57 | Add an item to the store. If "ident" is not supplied, it will be created. 58 | Return value is the *"ident"*. 59 | 60 | ####MicroData.del(ident); 61 | Remove a named "ident" from the store. 62 | 63 | ####MicroData.find(key, value); 64 | Find a record "ident" by a named key/value pair. Returns first match, search 65 | order is arbitrary. 66 | 67 | ####MicroData.findAll(key, value); 68 | Return an array of record "ident"'s where key === value. Order is arbitrary. 69 | 70 | ####MicroData.findAllWithKey(key); 71 | Return an array of record "ident"'s the contain 'key'. Order is arbitrary. 72 | 73 | ####MicroData.sortByKey(key, [direction], [alpha], [cleanBad]); 74 | Returns an array of values and "ident"'s, sorted in "direction" (asc/desc). 75 | Set "alpha" to true for alphanumeric sort. If 'cleanBad' is 76 | true, results with one or more 'undefined' values for the specified keys will 77 | not be returned. NOTE: This is just a convience method to the below... 78 | 79 | ####MicroData.sortByKeys(sorts, [cleanBad]); 80 | Returns an array of values and "idents"'s sorted by "sorts" array - where sorts 81 | is an array of \[key, direction, alpha\] arrays. (see above). If 'cleanBad' is 82 | true, results with one or more 'undefined' values for the specified keys will 83 | not be returned. 84 | 85 | 86 | ### Datatype === 0 Methods: 87 | 88 | #### MicroData.add(data); 89 | Add an item to the list. 90 | 91 | #### MicroData.del(num); 92 | Remove item number 'num' from list. 0-based. 93 | 94 | ### Shared Methods: 95 | 96 | ####MicroData.load(); 97 | Load file from disk. Usually called automatically, but if you are before 98 | options.savetime, it might work as an undo. 99 | 100 | ####MicroData.save(); 101 | Save file to disk. This is an anonomized asych method (no callback, it'll do it 102 | eventually. Used internally, I don't recommend it) 103 | 104 | ####MicroData.flush(); 105 | Save file to disk *now*. Synch method. If you think you need to flush the db 106 | yourself, chances are this is the method you want. 107 | 108 | ## Current status 109 | This module is in a development stage. It is broken horribly in places. There 110 | are quite a few features missing. And it has zero room for error - this will 111 | always be intended for internal persistent storage, not a real replacement for 112 | what you should use a real database for. 113 | 114 | 115 | ## Contributors 116 | * [J.T. Sage](https://github.com/jtsage/) 117 | 118 | ## Compatibility 119 | This module was only tested using node >= 0.8.8. There is no reason it shouldn't 120 | run under earlier versions though. 121 | 122 | ## Licence 123 | node-ansibuffer is licensed under the MIT license. Or the BSD license. Or no 124 | license if that's more convient for you. 125 | -------------------------------------------------------------------------------- /microdb.js: -------------------------------------------------------------------------------- 1 | // TINY Database file - *very* simple storage. 2 | "use strict"; 3 | var fs = require('fs'); 4 | 5 | var MicroData = function(opts) { 6 | var self = this; 7 | var defaults = { 8 | 'file': '', 9 | 'savetime': 10, 10 | 'maxrec': 0, 11 | 'datatype': 1, 12 | 'flushonexit': true, 13 | 'defaultClean': false 14 | }; 15 | 16 | this.options = defaults; 17 | 18 | if ( typeof opts === 'object' ) { 19 | for ( var idx in opts ) { this.options[idx] = opts[idx]; } 20 | } 21 | 22 | if ( this.options.datatype === 0 ) { 23 | this.data = []; 24 | } else { 25 | this.data = {}; 26 | } 27 | 28 | if ( this.options.flushonexit === true && this.options.file !== '' ) { 29 | process.on('exit', function () { 30 | self.flush(); 31 | }); 32 | } 33 | 34 | this.findAll = function(key, value) { 35 | if ( self.options.datatype === 0 ) { return false; } 36 | var retArray = []; 37 | for ( var idx in self.data ) { 38 | if ( key in self.data[idx] && self.data[idx][key] === value ) { 39 | retArray.push(idx); 40 | } 41 | } 42 | return retArray; 43 | } 44 | 45 | this.findAllWithKey = function(key) { 46 | if ( self.options.datatype === 0 ) { return false; } 47 | var retArray = []; 48 | for ( var idx in self.data ) { 49 | if ( key in self.data[idx] ) { 50 | retArray.push(idx); 51 | } 52 | } 53 | return retArray; 54 | } 55 | 56 | this.find = function(key, value) { 57 | if ( self.options.datatype === 0 ) { return false; } 58 | for ( var idx in self.data ) { 59 | if ( key in self.data[idx] && self.data[idx][key] === value ) { 60 | return idx; 61 | } 62 | } 63 | return false; 64 | } 65 | 66 | this.sortByKeys = function(sorts,cleanBad) { 67 | if ( self.options.datatype === 0 ) { return false; } 68 | if ( typeof cleanBad === 'undefined' ) { cleanBad = self.options.defaultClean; } 69 | var sorter = []; 70 | 71 | for ( var ident in self.data ) { 72 | var tempArray = [ident] 73 | for ( var sort in sorts ) { 74 | tempArray.push(self.data[ident][sorts[sort][0]]); 75 | } 76 | sorter.push(tempArray); 77 | } 78 | 79 | if ( sorter.length > 0 ) { 80 | for ( var srtIdx = sorts.length; srtIdx > 0; srtIdx-- ) { 81 | if ( typeof sorts[srtIdx-1][2] !== 'undefined' && sorts[srtIdx-1][2] === true ) { 82 | sorter = sorter.sort(function(a,b){ 83 | var multi = ( sorts[srtIdx-1][1] != 'desc' ) ? 1 : -1; 84 | if ( a[srtIdx] < b[srtIdx] ) return -1*multi; 85 | if ( a[srtIdx] > b[srtIdx] ) return 1*multi; 86 | return 0; 87 | }); 88 | } else { 89 | sorter = sorter.sort(function(a,b) { 90 | if ( sorts[srtIdx-1][1] != 'desc' ) { 91 | return a[srtIdx] - b[srtIdx]; 92 | } else { 93 | return b[srtIdx] - a[srtIdx]; 94 | } 95 | }); 96 | } 97 | } 98 | } 99 | 100 | if ( cleanBad === false ) { return sorter; } 101 | 102 | var retSort = []; 103 | for ( var idx = 0; idx < sorter.length; idx++ ) { 104 | var keepMe = true; 105 | for ( var check = 1; check < sorter[idx].length; check++ ) { 106 | if ( typeof sorter[idx][check] === 'undefined' ) { keepMe = false; } 107 | } 108 | if ( keepMe === true ) { retSort.push(sorter[idx]); } 109 | } 110 | return retSort; 111 | } 112 | 113 | this.sortByKey = function(key, direction, alpha, cleanBad) { 114 | return this.sortByKeys([[key,direction,alpha]], cleanBad); 115 | } 116 | 117 | this.startTime = function() { 118 | if ( this.options.savetime > 0 ) { 119 | this.time = setInterval( function() { self.save(); }, this.options.savetime * 1000*60 ); 120 | } 121 | } 122 | 123 | this.add = function(text, num) { 124 | if ( typeof num === 'undefined' && this.options.datatype == 1 ) { 125 | num = new Date().getTime(); 126 | } 127 | if ( this.options.datatype === 0 ) { 128 | this.data.push(text); 129 | if ( this.options.maxrec > 0 ) { 130 | while ( this.data.length > this.options.maxrec ) { 131 | this.data.shift(); 132 | } 133 | } 134 | return this.data.length; 135 | } else { 136 | this.data[num] = text; 137 | return num; 138 | } 139 | } 140 | 141 | this.del = function(num) { 142 | if ( this.options.datatype == 0 ) { 143 | this.data.splice(num,1); 144 | } else { 145 | delete this.data[num]; 146 | } 147 | return this.data.length; 148 | } 149 | 150 | this.save = function() { 151 | if ( this.options.file === '' ) { return true; } 152 | fs.writeFile(this.options.file, JSON.stringify(this.data), function() { return true; }); 153 | } 154 | 155 | this.flush = function() { 156 | if ( this.options.file === '' ) { return true; } 157 | fs.writeFileSync(self.options.file, JSON.stringify(self.data)); 158 | return true; 159 | } 160 | 161 | this.load = function() { 162 | this.data = JSON.parse(fs.readFileSync(this.options.file).toString()); 163 | } 164 | 165 | if ( fs.existsSync(self.options.file) ) { 166 | // Load if the file is there 167 | this.load(); 168 | } else { 169 | // Otherwise, create it. 170 | this.flush(); 171 | } 172 | this.startTime(); 173 | } 174 | 175 | module.exports = MicroData; 176 | 177 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-microdb", 3 | "version": "0.0.2", 4 | "description": "A Micro-sized key/document database - very simple. Auto-flush to disk and in-memory options", 5 | "main": "microdb.js", 6 | "scripts": { 7 | "test": "node ./tests/test.js" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/jtsage/nodejs-microdb.git" 12 | }, 13 | "keywords": [ 14 | "database", 15 | "storage", 16 | "tiny" 17 | ], 18 | "author": "J.T.Sage", 19 | "license": "BSD" 20 | } 21 | -------------------------------------------------------------------------------- /tests/test.js: -------------------------------------------------------------------------------- 1 | MicroDB = require('../microdb.js'); 2 | 3 | x = new MicroDB({'file':'test.db', 'defaultClean':true}); 4 | 5 | x.add({ name: 'Harry', age: 10, cows: 4 },'rec1'); 6 | x.add({ name: 'Tom', age: 10, cows: 5 },'rec2'); 7 | x.add({ name: 'Jack', age: 11, cows: 5 },'rec3'); 8 | x.add({ name: 'John', age: 11, cows: 1 },'rec4'); 9 | 10 | test = { 11 | 'showData': true, 12 | //'showData': false, 13 | 'sortByKey': true, 14 | //'sortByKey': false, 15 | 'sortByKeys': true, 16 | //'sortByKeys': false, 17 | 'find': true, 18 | //'find': false, 19 | }; 20 | if ( test.showData ) { 21 | console.log('DATA::'); 22 | console.log(x.data); 23 | } 24 | 25 | if ( test.sortByKey ) { 26 | console.log('SORT:: KEY: age, desc'); 27 | console.log(x.sortByKey('age','desc')); 28 | console.log('SORT:: KEY: name, asc [alpha]'); 29 | console.log(x.sortByKey('name','asc',true)); 30 | console.log('SORT:: KEY: name, desc [alpha]'); 31 | console.log(x.sortByKey('name','desc',true)); 32 | console.log('SORT:: KEY: ducks, asc'); 33 | console.log(x.sortByKey('ducks','asc',true)); 34 | } 35 | if ( test.sortByKeys ) { 36 | console.log('SORT:: KEYS: age,desc; cows,asc'); 37 | console.log(x.sortByKeys([['age','desc'],['cows','asc']])); 38 | console.log('SORT:: KEYS: age,asc; cows,desc'); 39 | console.log(x.sortByKeys([['age','asc'],['cows','desc']])); 40 | console.log('SORT:: KEYS: age,asc; name,desc,[alpha]'); 41 | console.log(x.sortByKeys([['age','asc'],['name','desc',true]])); 42 | console.log('SORT:: KEYS: cows,desc; name,asc,[alpha]'); 43 | console.log(x.sortByKeys([['cows','desc'],['name','asc',true]])); 44 | } 45 | if ( test.find ) { 46 | console.log('FIND:: age == 10'); 47 | console.log(x.find('age',10)); 48 | console.log('FIND:: name == Tom'); 49 | console.log(x.find('name','Tom')); 50 | console.log('FIND:: name == Dick'); 51 | console.log(x.find('name','Dick')); 52 | console.log('FINDALL:: age == 11'); 53 | console.log(x.findAll('age',11)); 54 | console.log('FINDALL:: age == 32'); 55 | console.log(x.findAll('age',32)); 56 | } 57 | 58 | process.exit(); 59 | --------------------------------------------------------------------------------