├── .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 |
--------------------------------------------------------------------------------