├── performance ├── db │ └── .keep └── time.js ├── .babelrc ├── .gitignore ├── diskdb.js ├── .travis.yml ├── .editorconfig ├── .jshintrc ├── examples ├── count.js ├── findAll.js ├── findOne.js ├── remove.js ├── update.js ├── saveCollection.js ├── all.js ├── nestedFindOne.js └── nestedFindAll.js ├── Gruntfile.js ├── package.json ├── lib ├── diskdb.js ├── collection.js └── util.js ├── dist ├── diskdb.js ├── collection.js └── util.js ├── CONTRIBUTING.md ├── README.md └── test └── diskdb_test.js /performance/db/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "latest" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | npm-debug.log 3 | test/testdb/ 4 | -------------------------------------------------------------------------------- /diskdb.js: -------------------------------------------------------------------------------- 1 | 2 | require('babel-polyfill'); 3 | module.exports = require('./dist/diskdb'); 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_script: 5 | - npm install -g grunt-cli 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "curly": true, 3 | "eqeqeq": false, 4 | "immed": true, 5 | "latedef": true, 6 | "newcap": true, 7 | "noarg": true, 8 | "sub": true, 9 | "undef": true, 10 | "latedef": false, 11 | "unused": true, 12 | "boss": true, 13 | "eqnull": true, 14 | "node": true, 15 | "browser": true, 16 | "newcap": false, 17 | "esversion": 6 18 | } 19 | -------------------------------------------------------------------------------- /examples/count.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // once you run npm install diskDB, 3 | // var db = require('diskdb'); instead of 4 | var DiskDB = require('..').default; 5 | var db = new DiskDB(); 6 | 7 | db.connect(`${__dirname}/db`, ['articles']); 8 | var article = { 9 | title : 'diskDB rocks', 10 | published : 'today', 11 | rating : '5 stars' 12 | } 13 | var savedArticle = db.articles.save(article); 14 | console.log(db.articles.count()); // will be 1 15 | 16 | // run : node count.js 17 | -------------------------------------------------------------------------------- /examples/findAll.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // once you run npm install diskDB, 3 | // var db = require('diskdb'); instead of 4 | var DiskDB = require('..').default; 5 | var db = new DiskDB(); 6 | 7 | db.connect(`${__dirname}/db`, ['articles']); 8 | var article = { 9 | title : 'diskDB rocks', 10 | published : 'today', 11 | rating : '5 stars' 12 | } 13 | var savedArticle = db.articles.save(article); 14 | var foundArticles = db.articles.find(); 15 | //var foundArticles = db.articles.find({rating : '5 stars'}); 16 | 17 | console.log(foundArticles); 18 | 19 | // run : node findAll.js 20 | -------------------------------------------------------------------------------- /examples/findOne.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // once you run npm install diskDB, 3 | // var db = require('diskdb'); instead of 4 | var DiskDB = require('..').default; 5 | var db = new DiskDB(); 6 | 7 | db.connect(`${__dirname}/db`, ['articles']); 8 | var article = { 9 | title : 'diskDB rocks', 10 | published : 'today', 11 | rating : '5 stars' 12 | } 13 | var savedArticle = db.articles.save(article); 14 | //var foundArticles = db.articles.findOne(); 15 | var foundArticles = db.articles.findOne({rating : '5 stars'}); 16 | 17 | console.log(foundArticles); 18 | 19 | // run : node findOne.js 20 | -------------------------------------------------------------------------------- /examples/remove.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // once you run npm install diskDB, 3 | // var db = require('diskdb'); instead of 4 | var DiskDB = require('..').default; 5 | var db = new DiskDB(); 6 | 7 | db.connect(`${__dirname}/db`, ['articles']); 8 | var article = { 9 | title : 'diskDB rocks', 10 | published : 'today', 11 | rating : '5 stars' 12 | } 13 | var savedArticle = db.articles.save(article); 14 | console.log(db.articles.count()); 15 | //db.articles.remove(); 16 | //db.articles.remove({rating : '5 stars'}); 17 | //db.articles.remove({rating : '5 stars'}, true); 18 | db.articles.remove({rating : '5 stars'}, false); 19 | console.log(db.articles.count()); 20 | // run : node remove.js 21 | -------------------------------------------------------------------------------- /examples/update.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // once you run npm install diskDB, 3 | // var db = require('diskdb'); instead of 4 | var DiskDB = require('..').default; 5 | var db = new DiskDB(); 6 | 7 | db.connect(`${__dirname}/db`, ['articles']); 8 | var article = { 9 | title : 'diskDB rocks', 10 | published : 'today', 11 | rating : '5 stars' 12 | } 13 | var savedArticle = db.articles.save(article); 14 | 15 | var query = { 16 | title : 'diskDB rocks' 17 | }; 18 | 19 | var dataToBeUpdate = { 20 | title : 'diskDB rocks again!', 21 | }; 22 | 23 | var options = { 24 | multi: false, 25 | upsert: false 26 | }; 27 | 28 | var updated = db.articles.update(query, dataToBeUpdate, options); 29 | console.log(updated); // { updated: 1, inserted: 0 } 30 | 31 | // run : node update.js 32 | -------------------------------------------------------------------------------- /examples/saveCollection.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // once you run npm install diskDB, 3 | // var db = require('diskdb'); instead of 4 | var DiskDB = require('..').default; 5 | var db = new DiskDB(); 6 | 7 | db.connect(`${__dirname}/db`, ['articles']); 8 | var article = { 9 | title : 'diskDB rocks', 10 | published : 'today', 11 | rating : '5 stars' 12 | } 13 | 14 | var article2 = { 15 | title : 'diskDB rocks', 16 | published : 'yesterday', 17 | rating : '5 stars' 18 | } 19 | 20 | var article3 = { 21 | title : 'diskDB rocks', 22 | published : 'today', 23 | rating : '4 stars' 24 | } 25 | 26 | //var savedArticle = db.articles.save(article); 27 | //var savedArticle = db.articles.save([article]); 28 | var savedArticle = db.articles.save([article, article2, article3]); 29 | 30 | console.log(savedArticle); 31 | 32 | // run : node saveCollection.js 33 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function(grunt) { 4 | // Show elapsed time at the end 5 | require('time-grunt')(grunt); 6 | // Load all grunt tasks 7 | require('load-grunt-tasks')(grunt); 8 | 9 | // Project configuration. 10 | grunt.initConfig({ 11 | nodeunit: { 12 | files: ['test/**/*_test.js'] 13 | }, 14 | jshint: { 15 | options: { 16 | jshintrc: '.jshintrc', 17 | reporter: require('jshint-stylish') 18 | }, 19 | gruntfile: { 20 | src: 'Gruntfile.js' 21 | }, 22 | lib: { 23 | src: ['lib/**/*.js'] 24 | }, 25 | test: { 26 | src: ['test/**/*.js'] 27 | } 28 | }, 29 | watch: { 30 | gruntfile: { 31 | files: '<%= jshint.gruntfile.src %>', 32 | tasks: ['jshint:gruntfile'] 33 | }, 34 | lib: { 35 | files: '<%= jshint.lib.src %>', 36 | tasks: ['jshint:lib', 'nodeunit'] 37 | }, 38 | test: { 39 | files: '<%= jshint.test.src %>', 40 | tasks: ['jshint:test', 'nodeunit'] 41 | } 42 | } 43 | }); 44 | 45 | // Default task. 46 | grunt.registerTask('default', ['jshint', 'nodeunit']); 47 | 48 | }; 49 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "diskdb", 3 | "version": "0.1.17", 4 | "main": "./diskdb.js", 5 | "description": "A Lightweight Disk based JSON Database with a MongoDB like API", 6 | "homepage": "http://arvindr21.github.io/diskDB", 7 | "bugs": "https://github.com/arvindr21/diskdb/issues", 8 | "author": { 9 | "name": "Arvind Ravulavaru", 10 | "email": "arvind.ravulavaru@gmail.com", 11 | "url": "http://thejackalofjavascript.com/" 12 | }, 13 | "repository": { 14 | "type": "git", 15 | "url": "https://github.com/arvindr21/diskdb" 16 | }, 17 | "license": "MIT", 18 | "files": [ 19 | "lib" 20 | ], 21 | "keywords": [ 22 | "diskDB", 23 | "JSON", 24 | "Database", 25 | "file system", 26 | "CRUD", 27 | "lightweight" 28 | ], 29 | "devDependencies": { 30 | "babel-cli": "^6.18.0", 31 | "babel-polyfill": "^6.16.0", 32 | "babel-preset-latest": "^6.16.0", 33 | "grunt": "^1.0.0", 34 | "grunt-contrib-jshint": "^1.0.0", 35 | "grunt-contrib-nodeunit": "^1.0.0", 36 | "grunt-contrib-watch": "^1.0.0", 37 | "jshint-stylish": "^0.2.0", 38 | "load-grunt-tasks": "^0.4.0", 39 | "time-grunt": "^0.3.1" 40 | }, 41 | "dependencies": { 42 | "chalk": "^0.4.0", 43 | "merge": "^1.1.3", 44 | "uuid": "^3.0.0" 45 | }, 46 | "scripts": { 47 | "compile": "node_modules/.bin/babel lib --out-dir dist", 48 | "test": "npm run-script compile && grunt" 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /examples/all.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // once you run npm install diskDB, 3 | // var db = require('diskdb'); instead of 4 | var DiskDB = require('..').default; 5 | var db = new DiskDB(); 6 | 7 | db.connect(`${__dirname}/db`, ['articles']); 8 | var article = { 9 | title : 'diskDB rocks', 10 | published : 'today', 11 | rating : '5 stars' 12 | } 13 | //save 14 | var savedArticle = db.articles.save(article); 15 | console.log(savedArticle); 16 | 17 | //findAll 18 | var foundArticles = db.articles.find(); 19 | console.log(foundArticles); 20 | 21 | foundArticles = db.articles.find({rating : '5 stars'}); 22 | console.log(foundArticles); 23 | 24 | //findOne 25 | var foundArticles = db.articles.findOne(); 26 | console.log(foundArticles); 27 | 28 | foundArticles = db.articles.findOne({rating : '5 stars'}); 29 | console.log(foundArticles); 30 | 31 | //update 32 | var query = { 33 | title : 'diskDB rocks' 34 | }; 35 | 36 | var dataToBeUpdate = { 37 | title : 'diskDB rocks again!', 38 | }; 39 | 40 | var options = { 41 | multi: false, 42 | upsert: false 43 | }; 44 | 45 | var updated = db.articles.update(query, dataToBeUpdate, options); 46 | console.log(updated); 47 | 48 | // after update 49 | foundArticles = db.articles.findOne({rating : '5 stars'}); 50 | console.log(foundArticles); 51 | 52 | //count 53 | console.log(db.articles.count()); 54 | 55 | //remove 56 | db.articles.remove({rating : '5 stars'}); 57 | db.articles.remove(); 58 | 59 | // db.articles does not exist anymore! 60 | 61 | // run : node all.js 62 | -------------------------------------------------------------------------------- /lib/diskdb.js: -------------------------------------------------------------------------------- 1 | /* 2 | * diskDB 3 | * http://arvindr21.github.io/diskDB 4 | * 5 | * Copyright (c) 2014 Arvind Ravulavaru 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // global modules 12 | import { join } from 'path'; 13 | import { red as e, green as s } from 'chalk'; 14 | 15 | //local modules 16 | import { isValidPath, writeToFile } from './util'; 17 | import Collection from './collection'; 18 | 19 | export default class DiskDB { 20 | 21 | connect(path, collections) { 22 | if (isValidPath(path)) { 23 | this._db = { path }; 24 | console.log(s('Successfully connected to : ' + path)); 25 | if (collections) { 26 | this.loadCollections(collections); 27 | } 28 | } else { 29 | console.log(e('The DB Path [' + path + '] does not seem to be valid. Recheck the path and try again')); 30 | return false; 31 | } 32 | return this; 33 | } 34 | 35 | loadCollections(collections) { 36 | if (!this._db) { 37 | console.log(e('Initialize the DB before you add collections. Use : ', 'db.connect(\'path-to-db\');')); 38 | return false; 39 | } 40 | if (Array.isArray(collections)) { 41 | collections.forEach(collection => { 42 | if (!collection.includes('.json')) { 43 | collection = `${collection}.json`; 44 | } 45 | const collectionFile = join(this._db.path, collection); 46 | if (!isValidPath(collectionFile)) { 47 | writeToFile(collectionFile); 48 | } 49 | const collectionName = collection.replace('.json', ''); 50 | this[collectionName] = new Collection(this, collectionName); 51 | }); 52 | } else { 53 | console.log(e('Invalid Collections Array.', 'Expected Format : ', '[\'collection1\',\'collection2\',\'collection3\']')); 54 | } 55 | return this; 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /examples/nestedFindOne.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // once you run npm install diskDB, 3 | // var db = require('diskdb'); instead of 4 | var DiskDB = require('..').default; 5 | var db = new DiskDB(); 6 | 7 | db.connect(`${__dirname}/db`, ['articles']); 8 | var articleComments = { 9 | title: 'diskDB rocks', 10 | published: '2 days ago', 11 | comments: [{ 12 | name: 'a user', 13 | comment: 'this is cool', 14 | rating: 2 15 | }, { 16 | name: 'b user', 17 | comment: 'this is ratchet', 18 | rating: 3 19 | }, { 20 | name: 'c user', 21 | comment: 'this is awesome', 22 | rating: 2 23 | }] 24 | }, 25 | articleComments2 = { 26 | title: 'diskDB rocks again', 27 | published: '3 days ago', 28 | comments: [{ 29 | name: 'a user', 30 | comment: 'this is cool', 31 | rating: 1 32 | }, { 33 | name: 'b user', 34 | comment: 'this is ratchet', 35 | rating: 1 36 | }, { 37 | name: 'c user', 38 | comment: 'this is awesome', 39 | rating: 2 40 | }] 41 | }, 42 | articleCommentsL3 = { 43 | title: 'diskDB rocks again', 44 | published: '3 days ago', 45 | comments: [{ 46 | name: 'a user', 47 | comment: 'this is cool', 48 | rating: 2, 49 | comments: [{ 50 | name: 'd user', 51 | comment: 'A reply', 52 | rating: 1 53 | }] 54 | }, { 55 | name: 'b user', 56 | comment: 'this is ratchet', 57 | rating: 2 58 | }, { 59 | name: 'c user', 60 | comment: 'this is awesome', 61 | rating: 2 62 | }] 63 | }; 64 | 65 | var savedArticle = db.articles.save(article); 66 | //var foundArticles = db.articles.findOne(); 67 | var foundArticles = db.articles.findOne({ 68 | rating: 2 69 | }); 70 | 71 | console.log(foundArticles); 72 | 73 | // run : node nestedFindOne.js 74 | -------------------------------------------------------------------------------- /examples/nestedFindAll.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // once you run npm install diskDB, 3 | // var db = require('diskdb'); instead of 4 | var DiskDB = require('..').default; 5 | var db = new DiskDB(); 6 | 7 | db.connect(`${__dirname}/db`, ['articles']); 8 | var articleComments = { 9 | title: 'diskDB rocks', 10 | published: '2 days ago', 11 | comments: [{ 12 | name: 'a user', 13 | comment: 'this is cool', 14 | rating: 2 15 | }, { 16 | name: 'b user', 17 | comment: 'this is ratchet', 18 | rating: 3 19 | }, { 20 | name: 'c user', 21 | comment: 'this is awesome', 22 | rating: 2 23 | }] 24 | }, 25 | articleComments2 = { 26 | title: 'diskDB rocks again', 27 | published: '3 days ago', 28 | comments: [{ 29 | name: 'a user', 30 | comment: 'this is cool', 31 | rating: 1 32 | }, { 33 | name: 'b user', 34 | comment: 'this is ratchet', 35 | rating: 1 36 | }, { 37 | name: 'c user', 38 | comment: 'this is awesome', 39 | rating: 2 40 | }] 41 | }, 42 | articleCommentsL3 = { 43 | title: 'diskDB rocks again', 44 | published: '3 days ago', 45 | comments: [{ 46 | name: 'a user', 47 | comment: 'this is cool', 48 | rating: 2, 49 | comments: [{ 50 | name: 'd user', 51 | comment: 'A reply', 52 | rating: 1 53 | }] 54 | }, { 55 | name: 'b user', 56 | comment: 'this is ratchet', 57 | rating: 2 58 | }, { 59 | name: 'c user', 60 | comment: 'this is awesome', 61 | rating: 2 62 | }] 63 | }; 64 | 65 | var savedArticle = db.articles.save([articleComments, articleComments2, articleCommentsL3]); 66 | var foundArticles = db.articles.find(); 67 | var foundArticles = db.articles.find({rating : 1}); 68 | 69 | console.log(foundArticles); 70 | 71 | // run : node nestedFindAll.js 72 | -------------------------------------------------------------------------------- /performance/time.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | // Check the processing time for each of the DB operations with load 3 | var DiskDB = require('..').default; 4 | var db = new DiskDB(); 5 | var fs = require('fs'); 6 | 7 | var path = process.cwd(); 8 | db.connect(path + '/performance/db', ['articles']); 9 | // remove the articles JSON if exists 10 | db.articles.remove(); 11 | // reload collection 12 | db.loadCollections(['articles']); 13 | 14 | var x = 999; 15 | var articles = []; 16 | for (var i = 0; i < x; i++) { 17 | articles.push({ 18 | title: 'diskDB rocks ' + i, 19 | published: 'today ' + i, 20 | rating: '5 stars ' + i 21 | }); 22 | }; 23 | // reload collection 24 | db.loadCollections(['articles']); 25 | 26 | console.log('/****************** Test for '+x+' article(s) *************/'); 27 | 28 | console.time(x+' : Insert(s)'); 29 | var resp = db.articles.save([articles]); 30 | //console.log('save : ' , resp); 31 | console.timeEnd(x+' : Insert(s)'); 32 | 33 | console.time(x+' : Find without query'); 34 | var resp = db.articles.find(); 35 | //console.log('find : ' , resp); 36 | console.timeEnd(x+' : Find without query'); 37 | 38 | console.time(x+' : Find with query'); 39 | var resp = db.articles.find(articles[0]); 40 | //console.log('find : ' , resp); 41 | console.timeEnd(x+' : Find with query'); 42 | 43 | console.time(x+' : Find One without query'); 44 | var resp = db.articles.findOne(); 45 | //console.log('find : ' , resp); 46 | console.timeEnd(x+' : Find One without query'); 47 | 48 | console.time(x+' : Find One with query'); 49 | var resp = db.articles.findOne(articles[0]); 50 | //console.log('find : ' , resp); 51 | console.timeEnd(x+' : Find One with query'); 52 | 53 | console.time(x+' : Update'); 54 | var resp = db.articles.update({ 55 | title: 'diskDB rocks' 56 | }, { 57 | title: 'diskDB is awesome' 58 | }); 59 | //console.log('update : ' , resp); 60 | console.timeEnd(x+' : Update'); 61 | 62 | console.time(x+' : Count'); 63 | var resp = db.articles.count(); 64 | //console.log('count : ' , resp); 65 | console.timeEnd(x+' : Count'); 66 | 67 | console.log("File size before deletion : ",getFilesizeInBytes(db.articles._f)); 68 | 69 | console.time(x+' : remove with query'); 70 | var resp = db.articles.remove(articles[0]); 71 | //console.log('remove : ' , resp); 72 | console.timeEnd(x+' : remove with query'); 73 | 74 | console.time(x+' : remove collection'); 75 | var resp = db.articles.remove(); 76 | //console.log('remove : ' , resp); 77 | console.timeEnd(x+' : remove collection'); 78 | 79 | console.log('/****************** Test for '+x+' article(s) *************/\n\n'); 80 | 81 | // utils 82 | function getFilesizeInBytes(filename) { 83 | var stats = fs.statSync(filename) 84 | var fileSizeInBytes = stats['size'] / 1000000.0; 85 | return fileSizeInBytes + ' MB' 86 | } 87 | 88 | // run node time.js 89 | -------------------------------------------------------------------------------- /dist/diskdb.js: -------------------------------------------------------------------------------- 1 | /* 2 | * diskDB 3 | * http://arvindr21.github.io/diskDB 4 | * 5 | * Copyright (c) 2014 Arvind Ravulavaru 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | // global modules 12 | 13 | Object.defineProperty(exports, "__esModule", { 14 | value: true 15 | }); 16 | 17 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 18 | 19 | //local modules 20 | 21 | 22 | var _path = require('path'); 23 | 24 | var _chalk = require('chalk'); 25 | 26 | var _util = require('./util'); 27 | 28 | var _collection = require('./collection'); 29 | 30 | var _collection2 = _interopRequireDefault(_collection); 31 | 32 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 33 | 34 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 35 | 36 | var DiskDB = function () { 37 | function DiskDB() { 38 | _classCallCheck(this, DiskDB); 39 | } 40 | 41 | _createClass(DiskDB, [{ 42 | key: 'connect', 43 | value: function connect(path, collections) { 44 | if ((0, _util.isValidPath)(path)) { 45 | this._db = { path: path }; 46 | console.log((0, _chalk.green)('Successfully connected to : ' + path)); 47 | if (collections) { 48 | this.loadCollections(collections); 49 | } 50 | } else { 51 | console.log((0, _chalk.red)('The DB Path [' + path + '] does not seem to be valid. Recheck the path and try again')); 52 | return false; 53 | } 54 | return this; 55 | } 56 | }, { 57 | key: 'loadCollections', 58 | value: function loadCollections(collections) { 59 | var _this = this; 60 | 61 | if (!this._db) { 62 | console.log((0, _chalk.red)('Initialize the DB before you add collections. Use : ', 'db.connect(\'path-to-db\');')); 63 | return false; 64 | } 65 | if (Array.isArray(collections)) { 66 | collections.forEach(function (collection) { 67 | if (!collection.includes('.json')) { 68 | collection = collection + '.json'; 69 | } 70 | var collectionFile = (0, _path.join)(_this._db.path, collection); 71 | if (!(0, _util.isValidPath)(collectionFile)) { 72 | (0, _util.writeToFile)(collectionFile); 73 | } 74 | var collectionName = collection.replace('.json', ''); 75 | _this[collectionName] = new _collection2.default(_this, collectionName); 76 | }); 77 | } else { 78 | console.log((0, _chalk.red)('Invalid Collections Array.', 'Expected Format : ', '[\'collection1\',\'collection2\',\'collection3\']')); 79 | } 80 | return this; 81 | } 82 | }]); 83 | 84 | return DiskDB; 85 | }(); 86 | 87 | exports.default = DiskDB; -------------------------------------------------------------------------------- /lib/collection.js: -------------------------------------------------------------------------------- 1 | /* 2 | * diskDB 3 | * http://arvindr21.github.io/diskDB 4 | * 5 | * Copyright (c) 2014 Arvind Ravulavaru 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | import { join } from 'path'; 12 | import { v4 } from 'uuid'; 13 | 14 | import * as util from './util'; 15 | 16 | const UUID = () => v4().replace(/-/g, ''); 17 | 18 | export default class Collection { 19 | 20 | constructor(db, collectionName, opts = {}) { 21 | 22 | this.db = db; 23 | this.opts = opts; 24 | 25 | // throw an exception if the collection's JSON file is invalid? 26 | this.opts.throwParseError = opts.throwParseError === undefined ? false : opts.throwParseError; 27 | 28 | this.collectionName = collectionName; 29 | this._f = join(db._db.path, `${collectionName}.json`); 30 | } 31 | 32 | _parse() { 33 | try { 34 | return JSON.parse(String(util.readFromFile(this._f))); 35 | } catch (err) { 36 | if (this.opts.throwParseError) { 37 | throw err; 38 | } 39 | } 40 | return []; 41 | } 42 | 43 | find(query) { 44 | var collection = this._parse(); 45 | if (!query || Object.keys(query).length === 0) { 46 | return collection; 47 | } 48 | var searcher = new util.ObjectSearcher(); 49 | return searcher.findAllInObject(collection, query, true); 50 | } 51 | 52 | findOne(query) { 53 | var collection = this._parse(); 54 | if (!query) { 55 | return collection[0]; 56 | } 57 | var searcher = new util.ObjectSearcher(); 58 | return searcher.findAllInObject(collection, query, false)[0]; 59 | } 60 | 61 | save(data) { 62 | var collection = this._parse(); 63 | if (typeof data === 'object' && data.length) { 64 | if (data.length === 1) { 65 | if (data[0].length > 0) { 66 | data = data[0]; 67 | } 68 | } 69 | var retCollection = []; 70 | for (var i = data.length - 1; i >= 0; i--) { 71 | var d = data[i]; 72 | d._id = UUID().replace(/-/g, ''); 73 | collection.push(d); 74 | retCollection.push(d); 75 | } 76 | util.writeToFile(this._f, collection); 77 | return retCollection; 78 | } else { 79 | data._id = UUID().replace(/-/g, ''); 80 | collection.push(data); 81 | util.writeToFile(this._f, collection); 82 | return data; 83 | } 84 | } 85 | 86 | update(query, data, options) { 87 | var ret = {}, 88 | collection = this._parse(); // update 89 | var records = util.finder(collection, query, true); 90 | if (records.length) { 91 | if (options && options.multi) { 92 | collection = util.updateFiltered(collection, query, data, true); 93 | ret.updated = records.length; 94 | ret.inserted = 0; 95 | } else { 96 | collection = util.updateFiltered(collection, query, data, false); 97 | ret.updated = 1; 98 | ret.inserted = 0; 99 | } 100 | } else { 101 | if (options && options.upsert) { 102 | data._id = UUID().replace(/-/g, ''); 103 | collection.push(data); 104 | ret.updated = 0; 105 | ret.inserted = 1; 106 | } else { 107 | ret.updated = 0; 108 | ret.inserted = 0; 109 | } 110 | } 111 | util.writeToFile(this._f, collection); 112 | return ret; 113 | } 114 | 115 | remove(query, multi) { 116 | if (query) { 117 | var collection = this._parse(); 118 | if (typeof multi === 'undefined') { 119 | multi = true; 120 | } 121 | collection = util.removeFiltered(collection, query, multi); 122 | util.writeToFile(this._f, collection); 123 | } else { 124 | util.removeFile(this._f); 125 | delete this.db[this.collectionName]; 126 | } 127 | return true; 128 | } 129 | 130 | count() { 131 | return this._parse().length; 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /lib/util.js: -------------------------------------------------------------------------------- 1 | /* 2 | * diskDB 3 | * http://arvindr21.github.io/diskDB 4 | * 5 | * Copyright (c) 2014 Arvind Ravulavaru 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /*jshint -W027*/ 12 | import { existsSync, readFileSync, writeFileSync, unlinkSync } from 'fs'; 13 | import merge from 'merge'; 14 | 15 | const ENCODING = 'utf-8'; 16 | 17 | export const isValidPath = existsSync; 18 | 19 | export const writeToFile = (outputFilename, content = []) => writeFileSync(outputFilename, JSON.stringify(content)); 20 | 21 | export const readFromFile = file => readFileSync(file, ENCODING); 22 | 23 | export const removeFile = unlinkSync; 24 | 25 | export const updateFiltered = (collection, query, data, multi) => { 26 | // break 2 loops at once - multi : false 27 | let i = collection.length - 1; 28 | loop: for (i; i >= 0; i--) { 29 | const c = collection[i]; 30 | for (var p in query) { 31 | if (p in c && c[p] == query[p]) { 32 | collection[i] = merge(c, data); 33 | if (!multi) { 34 | break loop; 35 | } 36 | } 37 | } 38 | } 39 | return collection; 40 | }; 41 | 42 | // [TODO] : Performance 43 | export const removeFiltered = (collection, query, multi) => { 44 | // break 2 loops at once - multi : false 45 | let i = collection.length - 1; 46 | loop: for (i; i >= 0; i--) { 47 | var c = collection[i]; 48 | for (var p in query) { 49 | if (p in c && c[p] == query[p]) { 50 | collection.splice(i, 1); 51 | if (!multi) { 52 | break loop; 53 | } 54 | } 55 | } 56 | } 57 | return collection; 58 | }; 59 | 60 | // [TODO] : Performance 61 | export const finder = (collection, query, multi) => { 62 | const retCollection = []; 63 | let i = collection.length - 1; 64 | loop: for (i; i >= 0; i--) { 65 | var c = collection[i]; 66 | for (var p in query) { 67 | if (p in c && c[p] == query[p]) { 68 | retCollection.push(collection[i]); 69 | if (!multi) { 70 | break loop; 71 | } 72 | } 73 | } 74 | } 75 | return retCollection; 76 | }; 77 | 78 | /** recursive finder **/ 79 | export class ObjectSearcher { 80 | 81 | constructor() { 82 | this.results = []; 83 | this.objects = []; 84 | this.resultIDS = {}; 85 | } 86 | 87 | findAllInObject(object, valueOBj, isMulti) { 88 | 89 | for (var objKey in object) { 90 | this.performSearch(object[objKey], valueOBj, object[objKey]); 91 | if (!isMulti && this.results.length == 1) { 92 | return this.results; 93 | } 94 | } 95 | 96 | while (this.objects.length !== 0) { 97 | var objRef = this.objects.pop(); 98 | this.performSearch(objRef['_obj'], valueOBj, objRef['parent']); 99 | if (!isMulti && this.results.length == 1) { 100 | return this.results; 101 | } 102 | } 103 | 104 | return this.results; 105 | } 106 | 107 | performSearch(object, valueOBj, opt_parentObj) { 108 | 109 | for (var criteria in valueOBj) { 110 | var query = {}; 111 | query[criteria] = valueOBj[criteria]; 112 | this.searchObject(object, query, opt_parentObj); 113 | } 114 | 115 | for (var i = 0; i < this.results.length; i++) { 116 | var result = this.results[i]; 117 | for (var field in valueOBj) { 118 | if (result[field] !== undefined) { 119 | if (result[field] !== valueOBj[field]) { 120 | this.results.splice(i, 1); 121 | } 122 | } 123 | } 124 | } 125 | } 126 | 127 | searchObject(object, valueOBj, opt_parentObj) { 128 | for (var objKey in object) { 129 | 130 | if (typeof object[objKey] != 'object') { 131 | 132 | if (valueOBj[objKey] == object[objKey]) { 133 | if (opt_parentObj !== undefined) { 134 | if (this.resultIDS[opt_parentObj['_id']] === undefined) { 135 | this.results.push(opt_parentObj); 136 | this.resultIDS[opt_parentObj['_id']] = ''; 137 | } 138 | } else { 139 | if (this.resultIDS[object['_id']] === undefined) { 140 | this.results.push(object); 141 | this.resultIDS[object['_id']] = ''; 142 | } 143 | } 144 | } 145 | 146 | } else { 147 | 148 | var obj = object; 149 | if (opt_parentObj !== undefined) { 150 | obj = opt_parentObj; 151 | } 152 | var objRef = { 153 | parent: obj, 154 | _obj: object[objKey] 155 | }; 156 | 157 | this.objects.push(objRef); 158 | 159 | } 160 | } 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /dist/collection.js: -------------------------------------------------------------------------------- 1 | /* 2 | * diskDB 3 | * http://arvindr21.github.io/diskDB 4 | * 5 | * Copyright (c) 2014 Arvind Ravulavaru 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | Object.defineProperty(exports, "__esModule", { 12 | value: true 13 | }); 14 | 15 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 16 | 17 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 18 | 19 | var _path = require('path'); 20 | 21 | var _uuid = require('uuid'); 22 | 23 | var _util = require('./util'); 24 | 25 | var util = _interopRequireWildcard(_util); 26 | 27 | function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } 28 | 29 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 30 | 31 | var UUID = function UUID() { 32 | return (0, _uuid.v4)().replace(/-/g, ''); 33 | }; 34 | 35 | var Collection = function () { 36 | function Collection(db, collectionName) { 37 | var opts = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; 38 | 39 | _classCallCheck(this, Collection); 40 | 41 | this.db = db; 42 | this.opts = opts; 43 | 44 | // throw an exception if the collection's JSON file is invalid? 45 | this.opts.throwParseError = opts.throwParseError === undefined ? false : opts.throwParseError; 46 | 47 | this.collectionName = collectionName; 48 | this._f = (0, _path.join)(db._db.path, collectionName + '.json'); 49 | } 50 | 51 | _createClass(Collection, [{ 52 | key: '_parse', 53 | value: function _parse() { 54 | try { 55 | return JSON.parse(String(util.readFromFile(this._f))); 56 | } catch (err) { 57 | if (this.opts.throwParseError) { 58 | throw err; 59 | } 60 | } 61 | return []; 62 | } 63 | }, { 64 | key: 'find', 65 | value: function find(query) { 66 | var collection = this._parse(); 67 | if (!query || Object.keys(query).length === 0) { 68 | return collection; 69 | } 70 | var searcher = new util.ObjectSearcher(); 71 | return searcher.findAllInObject(collection, query, true); 72 | } 73 | }, { 74 | key: 'findOne', 75 | value: function findOne(query) { 76 | var collection = this._parse(); 77 | if (!query) { 78 | return collection[0]; 79 | } 80 | var searcher = new util.ObjectSearcher(); 81 | return searcher.findAllInObject(collection, query, false)[0]; 82 | } 83 | }, { 84 | key: 'save', 85 | value: function save(data) { 86 | var collection = this._parse(); 87 | if ((typeof data === 'undefined' ? 'undefined' : _typeof(data)) === 'object' && data.length) { 88 | if (data.length === 1) { 89 | if (data[0].length > 0) { 90 | data = data[0]; 91 | } 92 | } 93 | var retCollection = []; 94 | for (var i = data.length - 1; i >= 0; i--) { 95 | var d = data[i]; 96 | d._id = UUID().replace(/-/g, ''); 97 | collection.push(d); 98 | retCollection.push(d); 99 | } 100 | util.writeToFile(this._f, collection); 101 | return retCollection; 102 | } else { 103 | data._id = UUID().replace(/-/g, ''); 104 | collection.push(data); 105 | util.writeToFile(this._f, collection); 106 | return data; 107 | } 108 | } 109 | }, { 110 | key: 'update', 111 | value: function update(query, data, options) { 112 | var ret = {}, 113 | collection = this._parse(); // update 114 | var records = util.finder(collection, query, true); 115 | if (records.length) { 116 | if (options && options.multi) { 117 | collection = util.updateFiltered(collection, query, data, true); 118 | ret.updated = records.length; 119 | ret.inserted = 0; 120 | } else { 121 | collection = util.updateFiltered(collection, query, data, false); 122 | ret.updated = 1; 123 | ret.inserted = 0; 124 | } 125 | } else { 126 | if (options && options.upsert) { 127 | data._id = UUID().replace(/-/g, ''); 128 | collection.push(data); 129 | ret.updated = 0; 130 | ret.inserted = 1; 131 | } else { 132 | ret.updated = 0; 133 | ret.inserted = 0; 134 | } 135 | } 136 | util.writeToFile(this._f, collection); 137 | return ret; 138 | } 139 | }, { 140 | key: 'remove', 141 | value: function remove(query, multi) { 142 | if (query) { 143 | var collection = this._parse(); 144 | if (typeof multi === 'undefined') { 145 | multi = true; 146 | } 147 | collection = util.removeFiltered(collection, query, multi); 148 | util.writeToFile(this._f, collection); 149 | } else { 150 | util.removeFile(this._f); 151 | delete this.db[this.collectionName]; 152 | } 153 | return true; 154 | } 155 | }, { 156 | key: 'count', 157 | value: function count() { 158 | return this._parse().length; 159 | } 160 | }]); 161 | 162 | return Collection; 163 | }(); 164 | 165 | exports.default = Collection; -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to diskDB 2 | 3 | Please take a moment to review this document in order to make the contribution 4 | process easy and effective for everyone involved. 5 | 6 | Following these guidelines helps to communicate that you respect the time of 7 | the developers managing and developing this open source project. In return, 8 | they should reciprocate that respect in addressing your issue or assessing 9 | patches and features. 10 | 11 | 12 | ## Using the issue tracker 13 | 14 | The issue tracker is the preferred channel for [bug reports](#bug-reports), 15 | [features requests](#feature-requests) and [submitting pull 16 | requests](#pull-requests), but please respect the following restrictions: 17 | 18 | * Please **do not** use the issue tracker for personal support requests (use 19 | [Stack Overflow](http://stackoverflow.com) or IRC). 20 | 21 | * Please **do not** derail or troll issues. Keep the discussion on topic and 22 | respect the opinions of others. 23 | 24 | 25 | ## Bug reports 26 | 27 | A bug is a _demonstrable problem_ that is caused by the code in the repository. 28 | Good bug reports are extremely helpful - thank you! 29 | 30 | Guidelines for bug reports: 31 | 32 | 1. **Use the GitHub issue search** — check if the issue has already been 33 | reported. 34 | 35 | 2. **Check if the issue has been fixed** — try to reproduce it using the 36 | latest `master` or development branch in the repository. 37 | 38 | 3. **Isolate the problem** — create a [reduced test 39 | case](http://css-tricks.com/6263-reduced-test-cases/) and a live example. 40 | 41 | A good bug report shouldn't leave others needing to chase you up for more 42 | information. Please try to be as detailed as possible in your report. What is 43 | your environment? What steps will reproduce the issue? What browser(s) and OS 44 | experience the problem? What would you expect to be the outcome? All these 45 | details will help people to fix any potential bugs. 46 | 47 | Example: 48 | 49 | > Short and descriptive example bug report title 50 | > 51 | > A summary of the issue and the browser/OS environment in which it occurs. If 52 | > suitable, include the steps required to reproduce the bug. 53 | > 54 | > 1. This is the first step 55 | > 2. This is the second step 56 | > 3. Further steps, etc. 57 | > 58 | > `` - a link to the reduced test case 59 | > 60 | > Any other information you want to share that is relevant to the issue being 61 | > reported. This might include the lines of code that you have identified as 62 | > causing the bug, and potential solutions (and your opinions on their 63 | > merits). 64 | 65 | 66 | ## Feature requests 67 | 68 | Feature requests are welcome. But take a moment to find out whether your idea 69 | fits with the scope and aims of the project. It's up to *you* to make a strong 70 | case to convince the project's developers of the merits of this feature. Please 71 | provide as much detail and context as possible. 72 | 73 | 74 | ## Pull requests 75 | 76 | Good pull requests - patches, improvements, new features - are a fantastic 77 | help. They should remain focused in scope and avoid containing unrelated 78 | commits. 79 | 80 | **Please ask first** before embarking on any significant pull request (e.g. 81 | implementing features, refactoring code, porting to a different language), 82 | otherwise you risk spending a lot of time working on something that the 83 | project's developers might not want to merge into the project. 84 | 85 | Please adhere to the coding conventions used throughout a project (indentation, 86 | accurate comments, etc.) and any other requirements (such as test coverage). 87 | 88 | Follow this process if you'd like your work considered for inclusion in the 89 | project: 90 | 91 | 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, 92 | and configure the remotes: 93 | 94 | ```bash 95 | # Clone your fork of the repo into the current directory 96 | git clone https://github.com//diskDB 97 | # Navigate to the newly cloned directory 98 | cd diskDB 99 | # Assign the original repo to a remote called "upstream" 100 | git remote add upstream https://github.com/arvindr21/diskDB 101 | ``` 102 | 103 | 2. If you cloned a while ago, get the latest changes from upstream: 104 | 105 | ```bash 106 | git checkout 107 | git pull upstream 108 | ``` 109 | 110 | 3. Create a new topic branch (off the main project development branch) to 111 | contain your feature, change, or fix: 112 | 113 | ```bash 114 | git checkout -b 115 | ``` 116 | 117 | 4. Commit your changes in logical chunks. Please adhere to these [git commit 118 | message guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) 119 | or your code is unlikely be merged into the main project. Use Git's 120 | [interactive rebase](https://help.github.com/articles/interactive-rebase) 121 | feature to tidy up your commits before making them public. 122 | 123 | 5. Locally merge (or rebase) the upstream development branch into your topic branch: 124 | 125 | ```bash 126 | git pull [--rebase] upstream 127 | ``` 128 | 129 | 6. Push your topic branch up to your fork: 130 | 131 | ```bash 132 | git push origin 133 | ``` 134 | 135 | 7. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) 136 | with a clear title and description. 137 | 138 | ## Conventions of commit messages 139 | 140 | Addding files on repo 141 | 142 | ```bash 143 | git commit -m "Add filename" 144 | ``` 145 | 146 | Updating files on repo 147 | 148 | ```bash 149 | git commit -m "Update filename, filename2, filename3" 150 | ``` 151 | 152 | Removing files on repo 153 | 154 | ```bash 155 | git commit -m "Remove filename" 156 | ``` 157 | 158 | Renaming files on repo 159 | 160 | ```bash 161 | git commit -m "Rename filename" 162 | ``` 163 | 164 | Fixing errors and issues on repo 165 | 166 | ```bash 167 | git commit -m "Fixed #issuenumber Message about this fix" 168 | ``` 169 | 170 | Adding features on repo 171 | 172 | ```bash 173 | git commit -m "Add Feature: nameoffeature Message about this feature" 174 | ``` 175 | 176 | Updating features on repo 177 | 178 | ```bash 179 | git commit -m "Update Feature: nameoffeature Message about this update" 180 | ``` 181 | 182 | Removing features on repo 183 | 184 | ```bash 185 | git commit -m "Remove Feature: nameoffeature Message about this" 186 | ``` 187 | 188 | Ignoring Travis CI build on repo 189 | 190 | ```bash 191 | git commit -m "Commit message here [ci-skip]" 192 | ``` 193 | 194 | **IMPORTANT**: By submitting a patch, you agree to allow the project owner to 195 | license your work under the same license as that used by the project. 196 | -------------------------------------------------------------------------------- /dist/util.js: -------------------------------------------------------------------------------- 1 | /* 2 | * diskDB 3 | * http://arvindr21.github.io/diskDB 4 | * 5 | * Copyright (c) 2014 Arvind Ravulavaru 6 | * Licensed under the MIT license. 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /*jshint -W027*/ 12 | 13 | Object.defineProperty(exports, "__esModule", { 14 | value: true 15 | }); 16 | exports.ObjectSearcher = exports.finder = exports.removeFiltered = exports.updateFiltered = exports.removeFile = exports.readFromFile = exports.writeToFile = exports.isValidPath = undefined; 17 | 18 | var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 19 | 20 | var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 21 | 22 | var _fs = require('fs'); 23 | 24 | var _merge = require('merge'); 25 | 26 | var _merge2 = _interopRequireDefault(_merge); 27 | 28 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 29 | 30 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 31 | 32 | var ENCODING = 'utf-8'; 33 | 34 | var isValidPath = exports.isValidPath = _fs.existsSync; 35 | 36 | var writeToFile = exports.writeToFile = function writeToFile(outputFilename) { 37 | var content = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : []; 38 | return (0, _fs.writeFileSync)(outputFilename, JSON.stringify(content)); 39 | }; 40 | 41 | var readFromFile = exports.readFromFile = function readFromFile(file) { 42 | return (0, _fs.readFileSync)(file, ENCODING); 43 | }; 44 | 45 | var removeFile = exports.removeFile = _fs.unlinkSync; 46 | 47 | var updateFiltered = exports.updateFiltered = function updateFiltered(collection, query, data, multi) { 48 | // break 2 loops at once - multi : false 49 | var i = collection.length - 1; 50 | loop: for (i; i >= 0; i--) { 51 | var c = collection[i]; 52 | for (var p in query) { 53 | if (p in c && c[p] == query[p]) { 54 | collection[i] = (0, _merge2.default)(c, data); 55 | if (!multi) { 56 | break loop; 57 | } 58 | } 59 | } 60 | } 61 | return collection; 62 | }; 63 | 64 | // [TODO] : Performance 65 | var removeFiltered = exports.removeFiltered = function removeFiltered(collection, query, multi) { 66 | // break 2 loops at once - multi : false 67 | var i = collection.length - 1; 68 | loop: for (i; i >= 0; i--) { 69 | var c = collection[i]; 70 | for (var p in query) { 71 | if (p in c && c[p] == query[p]) { 72 | collection.splice(i, 1); 73 | if (!multi) { 74 | break loop; 75 | } 76 | } 77 | } 78 | } 79 | return collection; 80 | }; 81 | 82 | // [TODO] : Performance 83 | var finder = exports.finder = function finder(collection, query, multi) { 84 | var retCollection = []; 85 | var i = collection.length - 1; 86 | loop: for (i; i >= 0; i--) { 87 | var c = collection[i]; 88 | for (var p in query) { 89 | if (p in c && c[p] == query[p]) { 90 | retCollection.push(collection[i]); 91 | if (!multi) { 92 | break loop; 93 | } 94 | } 95 | } 96 | } 97 | return retCollection; 98 | }; 99 | 100 | /** recursive finder **/ 101 | 102 | var ObjectSearcher = exports.ObjectSearcher = function () { 103 | function ObjectSearcher() { 104 | _classCallCheck(this, ObjectSearcher); 105 | 106 | this.results = []; 107 | this.objects = []; 108 | this.resultIDS = {}; 109 | } 110 | 111 | _createClass(ObjectSearcher, [{ 112 | key: 'findAllInObject', 113 | value: function findAllInObject(object, valueOBj, isMulti) { 114 | 115 | for (var objKey in object) { 116 | this.performSearch(object[objKey], valueOBj, object[objKey]); 117 | if (!isMulti && this.results.length == 1) { 118 | return this.results; 119 | } 120 | } 121 | 122 | while (this.objects.length !== 0) { 123 | var objRef = this.objects.pop(); 124 | this.performSearch(objRef['_obj'], valueOBj, objRef['parent']); 125 | if (!isMulti && this.results.length == 1) { 126 | return this.results; 127 | } 128 | } 129 | 130 | return this.results; 131 | } 132 | }, { 133 | key: 'performSearch', 134 | value: function performSearch(object, valueOBj, opt_parentObj) { 135 | 136 | for (var criteria in valueOBj) { 137 | var query = {}; 138 | query[criteria] = valueOBj[criteria]; 139 | this.searchObject(object, query, opt_parentObj); 140 | } 141 | 142 | for (var i = 0; i < this.results.length; i++) { 143 | var result = this.results[i]; 144 | for (var field in valueOBj) { 145 | if (result[field] !== undefined) { 146 | if (result[field] !== valueOBj[field]) { 147 | this.results.splice(i, 1); 148 | } 149 | } 150 | } 151 | } 152 | } 153 | }, { 154 | key: 'searchObject', 155 | value: function searchObject(object, valueOBj, opt_parentObj) { 156 | for (var objKey in object) { 157 | 158 | if (_typeof(object[objKey]) != 'object') { 159 | 160 | if (valueOBj[objKey] == object[objKey]) { 161 | if (opt_parentObj !== undefined) { 162 | if (this.resultIDS[opt_parentObj['_id']] === undefined) { 163 | this.results.push(opt_parentObj); 164 | this.resultIDS[opt_parentObj['_id']] = ''; 165 | } 166 | } else { 167 | if (this.resultIDS[object['_id']] === undefined) { 168 | this.results.push(object); 169 | this.resultIDS[object['_id']] = ''; 170 | } 171 | } 172 | } 173 | } else { 174 | 175 | var obj = object; 176 | if (opt_parentObj !== undefined) { 177 | obj = opt_parentObj; 178 | } 179 | var objRef = { 180 | parent: obj, 181 | _obj: object[objKey] 182 | }; 183 | 184 | this.objects.push(objRef); 185 | } 186 | } 187 | } 188 | }]); 189 | 190 | return ObjectSearcher; 191 | }(); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # diskDB [![Build Status](https://secure.travis-ci.org/arvindr21/diskDB.png?branch=master)](https://travis-ci.org/arvindr21/diskDB) [![NPM version](https://badge-me.herokuapp.com/api/npm/diskdb.png)](http://badges.enytc.com/for/npm/diskdb) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/arvindr21/diskDB?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 2 | 3 | [![NPM](https://nodei.co/npm/diskdb.png?downloads=true&stars=true)](https://nodei.co/npm/diskdb/) 4 | 5 | A Lightweight Disk based JSON Database with a MongoDB like API for Node. 6 | 7 | _You will never know that you are interacting with a File System_ 8 | 9 | ## Contents 10 | 11 | * [Getting Started](#getting-started) 12 | * [Documentation](#documentation) 13 | * [Connect](#connect-to-db) 14 | * [Load Collections](#load-collections) 15 | * [Write/Save](#writesave-to-collection) 16 | * [Read](#read-from-collection) 17 | * [Update](#update-collection) 18 | * [Remove](#remove-collection) 19 | * [Count](#count) 20 | * [Examples](#examples) 21 | * [Performance](#performance) 22 | * [Contributing](#contributing) 23 | * [Release History](#release-history) 24 | 25 | ## Getting Started 26 | Install the module locally : 27 | ```bash 28 | $ npm install diskdb 29 | ``` 30 | 31 | ```js 32 | var db = require('diskdb'); 33 | db = db.connect('/path/to/db-folder', ['collection-name']); 34 | // you can access the traditional JSON DB methods here 35 | ``` 36 | 37 | ## Documentation 38 | ### Connect to DB 39 | ```js 40 | db.connect(pathToFolder, ['filename']); 41 | ``` 42 | Filename will be the name of the JSON file. You can omit the extension, diskDB will take care of it for you. 43 | 44 | ```js 45 | var db = require('diskdb'); 46 | db = db.connect('/examples/db', ['articles']); 47 | // or simply 48 | db.connect('/examples/db', ['articles']); 49 | ``` 50 | 51 | This will check for a directory at given path, if it does not exits, diskDB will throw an error and exit. 52 | 53 | If the directory exists but the file/collection does not exist, diskDB will create it for you. 54 | 55 | **Note** : If you have manually created a JSON file, please make sure it contains a valid JSON array, otherwise diskDB 56 | will return an empty array. 57 | 58 | ```js 59 | [] 60 | ``` 61 | Else it will throw an error like 62 | 63 | ```bash 64 | undefined:0 65 | 66 | ^ 67 | SyntaxError: Unexpected end of input 68 | ``` 69 | --- 70 | ### Load Collections 71 | Alternatively you can also load collections like 72 | 73 | ```js 74 | var db = require('diskdb'); 75 | // this 76 | db = db.connect('/examples/db'); 77 | db.loadCollections(['articles']); 78 | //or 79 | db.connect('/examples/db'); 80 | db.loadCollections(['articles']); 81 | //or 82 | db.connect('/examples/db') 83 | .loadCollections(['articles']); 84 | //or 85 | db.connect('/examples/db', ['articles']); 86 | ``` 87 | #### Load Multiple Collections 88 | 89 | ```js 90 | var db = require('diskdb'); 91 | db.connect('/examples/db', ['articles','comments','users']); 92 | ``` 93 | --- 94 | ### Write/Save to Collection 95 | ```js 96 | db.collectionName.save(object); 97 | ``` 98 | Once you have loaded a collection, you can access the collection's methods using the dot notation like 99 | 100 | ```js 101 | db.[collectionName].[methodname] 102 | ``` 103 | To save the data, you can use 104 | ```js 105 | var db = require('diskdb'); 106 | db.connect('db', ['articles']); 107 | var article = { 108 | title : "diskDB rocks", 109 | published : "today", 110 | rating : "5 stars" 111 | } 112 | db.articles.save(article); 113 | // or 114 | db.articles.save([article]); 115 | ``` 116 | The saved data will be 117 | ```js 118 | [ 119 | { 120 | "title": "diskDB rocks", 121 | "published": "today", 122 | "rating": "5 stars", 123 | "_id": "0f6047c6c69149f0be0c8f5943be91be" 124 | } 125 | ] 126 | ``` 127 | You can also save multiple objects at once like 128 | 129 | ```js 130 | var db = require('diskdb'); 131 | db.connect('db', ['articles']); 132 | var article1 = { 133 | title : 'diskDB rocks', 134 | published : 'today', 135 | rating : '5 stars' 136 | } 137 | 138 | var article2 = { 139 | title : 'diskDB rocks', 140 | published : 'yesterday', 141 | rating : '5 stars' 142 | } 143 | 144 | var article3 = { 145 | title : 'diskDB rocks', 146 | published : 'today', 147 | rating : '4 stars' 148 | } 149 | db.articles.save([article1, article2, article3]); 150 | ``` 151 | And this will return the inserted objects 152 | 153 | ```js 154 | [ { title: 'diskDB rocks', 155 | published: 'today', 156 | rating: '4 stars', 157 | _id: 'b1cdbb3525b84e8c822fc78896d0ca7b' }, 158 | { title: 'diskDB rocks', 159 | published: 'yesterday', 160 | rating: '5 stars', 161 | _id: '42997c62e1714e9f9d88bf3b87901f3b' }, 162 | { title: 'diskDB rocks', 163 | published: 'today', 164 | rating: '5 stars', 165 | _id: '4ca1c1597ddc4020bc41b4418e7a568e' } ] 166 | ``` 167 | --- 168 | ### Read from Collection 169 | There are 2 methods available for reading the JSON collection 170 | * db.collectionName.find(query) 171 | * db.collectionName.findOne(query) 172 | 173 | 174 | #### db.collectionName.find() 175 | ```js 176 | var db = require('diskdb'); 177 | db.connect('/examples/db', ['articles']); 178 | db.articles.find(); 179 | ``` 180 | This will return all the records 181 | ```js 182 | [{ 183 | title: 'diskDB rocks', 184 | published: 'today', 185 | rating: '5 stars', 186 | _id: '0f6047c6c69149f0be0c8f5943be91be' 187 | }] 188 | ``` 189 | You can also query with a criteria like 190 | ```js 191 | var db = require('diskdb'); 192 | db.connect('/examples/db', ['articles']); 193 | db.articles.find({rating : "5 stars"}); 194 | ``` 195 | This will return all the articles which have a rating of 5. 196 | 197 | Find can take multiple criteria 198 | ```js 199 | var db = require('diskdb'); 200 | db.connect('/examples/db', ['articles']); 201 | db.articles.find({rating : "5 stars", published: "yesterday"}); 202 | ``` 203 | This will return all the articles with a rating of 5, published yesterday. 204 | 205 | Nested JSON : 206 | 207 | ```js 208 | var articleComments = { 209 | title: 'diskDB rocks', 210 | published: '2 days ago', 211 | comments: [{ 212 | name: 'a user', 213 | comment: 'this is cool', 214 | rating: 2 215 | }, { 216 | name: 'b user', 217 | comment: 'this is ratchet', 218 | rating: 3 219 | }, { 220 | name: 'c user', 221 | comment: 'this is awesome', 222 | rating: 2 223 | }] 224 | } 225 | ``` 226 | ```js 227 | var savedArticle = db.articles.save([articleComments); 228 | foundArticles = db.articles.find({rating : 2}); 229 | ``` 230 | Since diskDB is mostly for light weight data storage, avoid nested structures and huge datasets. 231 | 232 | #### db.collectionName.findOne(query) 233 | ```js 234 | var db = require('diskdb'); 235 | db.connect('/examples/db', ['articles']); 236 | db.articles.findOne(); 237 | ``` 238 | 239 | If you do not pass a query, diskDB will return the first article in the collection. If you pass a query, it will return first article in the filtered data. 240 | 241 | ```js 242 | var db = require('diskdb'); 243 | db.connect('/examples/db', ['articles']); 244 | db.articles.findOne({_id: '0f6047c6c69149f0be0c8f5943be91be'}); 245 | ``` 246 | --- 247 | ### Update Collection 248 | ```js 249 | db.collectionName.update(query, data, options); 250 | ``` 251 | 252 | You can also update one or many objects in the collection 253 | ```js 254 | options = { 255 | multi: false, // update multiple - default false 256 | upsert: false // if object is not found, add it (update-insert) - default false 257 | } 258 | ``` 259 | Usage 260 | ```js 261 | var db = require('diskdb'); 262 | db.connect('/examples/db', ['articles']); 263 | 264 | var query = { 265 | title : 'diskDB rocks' 266 | }; 267 | 268 | var dataToBeUpdate = { 269 | title : 'diskDB rocks again!', 270 | }; 271 | 272 | var options = { 273 | multi: false, 274 | upsert: false 275 | }; 276 | 277 | var updated = db.articles.update(query, dataToBeUpdate, options); 278 | console.log(updated); // { updated: 1, inserted: 0 } 279 | ``` 280 | --- 281 | ### Remove Collection 282 | ```js 283 | db.collectionName.remove(query, multi); 284 | ``` 285 | You can remove the entire collection (including the file) or you can remove the matched objects by passing in a query. When you pass a query, you can either delete all the matched objects or only the first one by passing `multi` as `false`. The default value of `multi` is `true`. 286 | 287 | ```js 288 | var db = require('diskdb'); 289 | db.connect('/examples/db', ['articles']); 290 | db.articles.remove({rating : "5 stars"}); 291 | ``` 292 | ```js 293 | var db = require('diskdb'); 294 | db.connect('/examples/db', ['articles']); 295 | db.articles.remove({rating : "5 stars"}, true); // remove all matched. Default - multi = true 296 | ``` 297 | 298 | ```js 299 | var db = require('diskdb'); 300 | db.connect('/examples/db', ['articles']); 301 | db.articles.remove({rating : "5 stars"}, false); // remove only the first match 302 | ``` 303 | Using remove without any params will delete the file and will remove the db instance. 304 | ```js 305 | var db = require('diskdb'); 306 | db.connect('/examples/db', ['articles']); 307 | db.articles.remove(); 308 | ``` 309 | After the above operation `db.articles` is `undefined`. 310 | 311 | --- 312 | ### Count 313 | ```js 314 | db.collectionName.count(); 315 | ``` 316 | Will return the count of objects in the Collection 317 | ```js 318 | var db = require('diskdb'); 319 | db.connect('/examples/db', ['articles']); 320 | db.articles.count(); // will give the count 321 | ``` 322 | 323 | ## Examples 324 | Refer to the [examples](https://github.com/arvindr21/diskDB/tree/master/examples) folder. 325 | 326 | ## Performance 327 | To validate diskDB's performance and to check if it meets your needs, you can clone this repo and run 328 | 329 | ```bash 330 | $ node performance/time.js 331 | ``` 332 | An average of few tests (run on OS X - 10.9.3 | 2.9GHZ i7 | 8GB 1600MHz DDR3) can be found below 333 | 334 | #### Time taken to process x number of objects (in ms) vs Action Performed 335 | 336 | \# of objects | 1 | 1000 | 10000 | 100000 | 1000000 337 | -----------------------|------------|------------|------------|------------|------------- 338 | Save | 1 ms | 15 ms | 137 ms | 1728 ms | 14425 ms 339 | Find all without query | 0 ms | 2 ms | 12 ms | 204 ms | 2923 ms 340 | Find all with query | 0 ms | 2 ms | 17 ms | 738 ms | 1985 ms 341 | Find one without query | 0 ms | 1 ms | 9 ms | 791 ms | 1676 ms 342 | Find one with query | 0 ms | 1 ms | 8 ms | 219 ms | 1410 ms 343 | Update all records | 1 ms | 7 ms | 61 ms | 206 ms | 48035 ms 344 | Get count | 0 ms | 3 ms | 11 ms | 260 ms | 2420 ms 345 | Remove with query | 0 ms | 7 ms | 59 ms | 984 ms | 48191 ms 346 | Remove collection | 0 ms | 1 ms | 4 ms | 52 ms | 154 ms 347 | File size | 0.000111 MB| 0.116671 MB| 1.196671 MB| 12.26667 MB| 125.66667 MB 348 | 349 | 350 | ## Contributing 351 | See the [CONTRIBUTING Guidelines](https://github.com/arvindr21/diskDB/blob/master/CONTRIBUTING.md) 352 | 353 | ## Release History 354 | * 0.1.x 355 | * Base Module with 356 | * Connect to a Folder 357 | * Access a Collection/File 358 | * Create Read Update Delete on JSON object 359 | * Minor fixes and tests 360 | * Performance improvements 361 | 362 | ## License 363 | Copyright (c) 2014 Arvind Ravulavaru. Licensed under the MIT license. 364 | -------------------------------------------------------------------------------- /test/diskdb_test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var DiskDB = require('..').default; 4 | var path = require('path'); 5 | var fs = require('fs'); 6 | 7 | var diskdb = new DiskDB(); 8 | 9 | /* 10 | ======== A Handy Little Nodeunit Reference ======== 11 | https://github.com/caolan/nodeunit 12 | 13 | Test methods: 14 | test.expect(numAssertions) 15 | test.done() 16 | Test assertions: 17 | test.ok(value, [message]) 18 | test.equal(actual, expected, [message]) 19 | test.notEqual(actual, expected, [message]) 20 | test.deepEqual(actual, expected, [message]) 21 | test.notDeepEqual(actual, expected, [message]) 22 | test.strictEqual(actual, expected, [message]) 23 | test.notStrictEqual(actual, expected, [message]) 24 | test.throws(block, [error], [message]) 25 | test.doesNotThrow(block, [error], [message]) 26 | test.ifError(value) 27 | */ 28 | 29 | var dbPath = 'test/testdb', 30 | collection = ['articles'], 31 | collections = ['comments', 'rating'], 32 | article = { 33 | title: 'diskDB rocks', 34 | published: 'today' 35 | }, 36 | article2 = { 37 | title: 'diskDB rocks', 38 | published: 'yesterday' 39 | }, 40 | article3 = { 41 | title: 'diskDB rocks', 42 | published: 'yesterday' 43 | }, 44 | //nested objects 45 | articleComments = { 46 | title: 'diskDB rocks', 47 | published: '2 days ago', 48 | comments: [{ 49 | name: 'a user', 50 | comment: 'this is cool', 51 | rating: 2 52 | }, { 53 | name: 'b user', 54 | comment: 'this is ratchet', 55 | rating: 3 56 | }, { 57 | name: 'c user', 58 | comment: 'this is awesome', 59 | rating: 2 60 | }] 61 | }, 62 | articleComments2 = { 63 | title: 'diskDB rocks again', 64 | published: '3 days ago', 65 | comments: [{ 66 | name: 'a user', 67 | comment: 'this is cool', 68 | rating: 1 69 | }, { 70 | name: 'b user', 71 | comment: 'this is ratchet', 72 | rating: 1 73 | }, { 74 | name: 'c user', 75 | comment: 'this is awesome', 76 | rating: 2 77 | }] 78 | }, 79 | articleCommentsL3 = { 80 | title: 'diskDB rocks again', 81 | published: '3 days ago', 82 | comments: [{ 83 | name: 'a user', 84 | comment: 'this is cool', 85 | rating: 2, 86 | comments: [{ 87 | name: 'd user', 88 | comment: 'A reply', 89 | rating: 1 90 | }] 91 | }, { 92 | name: 'b user', 93 | comment: 'this is ratchet', 94 | rating: 2 95 | }, { 96 | name: 'c user', 97 | comment: 'this is awesome', 98 | rating: 2 99 | }] 100 | }; 101 | 102 | exports.connectNload = { 103 | setUp: function(done) { 104 | deleteFolderRecursive(dbPath); 105 | 106 | // create the directory 107 | if (!fs.existsSync(dbPath)) { 108 | fs.mkdirSync(dbPath); 109 | } 110 | done(); 111 | }, 112 | 'connect : ': function(test) { 113 | test.expect(1); 114 | test.equal(typeof(diskdb.connect(dbPath, collection)[collection[0]]), 'object', 'Successfully Connected and collection instantiated'); 115 | test.done(); 116 | }, 117 | 'loadCollections : ': function(test) { 118 | test.expect(3); 119 | // connect to DB 120 | diskdb.connect(dbPath); 121 | // load single collecion 122 | test.equal(diskdb.loadCollections(collection)[collection[0]].collectionName, collection[0], 'Loading single collection'); 123 | //load multiple collections 124 | test.equal(diskdb.loadCollections(collections)[collections[0]].collectionName, collections[0], 'Loading multiple collection'); 125 | test.equal(diskdb.loadCollections(collections)[collections[1]].collectionName, collections[1], 'Loading multiple collection'); 126 | test.done(); 127 | }, 128 | 'loadCollectionsWhenFileIsInvalid : ': function(test) { 129 | test.expect(1); 130 | // connect to DB 131 | diskdb.connect(dbPath); 132 | // we manually create the file with an empty and/or invalid JSON content 133 | fs.writeFileSync(path.join(dbPath, 'articles.json'), 'empty string or invalid json'); 134 | // we don't have an exception anymore... 135 | test.deepEqual(diskdb.articles.find(), []); 136 | test.done(); 137 | }, 138 | tearDown: function(callback) { 139 | // remove collections 140 | diskdb.loadCollections(collections); 141 | diskdb[collections[0]].remove(); 142 | diskdb[collections[1]].remove(); 143 | callback(); 144 | }, 145 | }; 146 | 147 | exports.count = { 148 | setUp: function(done) { 149 | // create the directory 150 | if (!fs.existsSync(dbPath)) { 151 | fs.mkdirSync(dbPath); 152 | } 153 | // init diskdb 154 | diskdb.connect(dbPath, collection); 155 | // remove articles collection 156 | diskdb.articles.remove(); 157 | //reinit the collection 158 | diskdb.loadCollections(collection); 159 | 160 | done(); 161 | }, 162 | 'count : ': function(test) { 163 | test.expect(2); 164 | test.equal(diskdb.articles.count(), 0, 'Count should be 0'); 165 | diskdb.articles.save(article); 166 | diskdb.articles.save(article2); 167 | test.equal(diskdb.articles.count(), 2, 'Count should be 2'); 168 | test.done(); 169 | }, 170 | }; 171 | 172 | exports.saveData = { 173 | setUp: function(done) { 174 | // create the directory 175 | if (!fs.existsSync(dbPath)) { 176 | fs.mkdirSync(dbPath); 177 | } 178 | // init diskdb 179 | diskdb.connect(dbPath, collection); 180 | // remove articles collection 181 | diskdb.articles.remove(); 182 | //reinit the collection 183 | diskdb.loadCollections(collection); 184 | 185 | done(); 186 | }, 187 | 'save : ': function(test) { 188 | test.expect(2); 189 | test.equal(diskdb.articles.count(), 0, 'No records before save'); 190 | test.equal(diskdb.articles.save(article).title, article.title, 'One record should get saved'); 191 | test.done(); 192 | }, 193 | 194 | 'save multiple: ': function(test) { 195 | test.expect(3); 196 | test.equal(diskdb.articles.count(), 0, 'No records before save'); 197 | test.equal(diskdb.articles.save([article]).length, 1, 'One record should get saved'); 198 | test.equal(diskdb.articles.save([article, article2]).length, 2, 'Two records should get saved'); 199 | test.done(); 200 | }, 201 | }; 202 | 203 | exports.findAll = { 204 | setUp: function(done) { 205 | // create the directory 206 | if (!fs.existsSync(dbPath)) { 207 | fs.mkdirSync(dbPath); 208 | } 209 | // init diskdb 210 | diskdb.connect(dbPath, collection); 211 | // remove articles collection 212 | diskdb.articles.remove(); 213 | //reinit the collection 214 | diskdb.loadCollections(collection); 215 | done(); 216 | }, 217 | 218 | 'findAll : ': function(test) { 219 | test.expect(6); 220 | 221 | // save three records 222 | diskdb.articles.save(article); 223 | diskdb.articles.save(article2); 224 | diskdb.articles.save(article3); 225 | 226 | // empty find returns all records 227 | test.equal(diskdb.articles.find().length, 3, 'Should find three records'); 228 | 229 | // empty search params returns all records 230 | test.equal(diskdb.articles.find({}).length, 3, 'Should find three records'); 231 | 232 | // find with a query 233 | test.equal(diskdb.articles.find({ 234 | title: 'diskDB rocks' 235 | }).length, 3, 'Should find three records with query'); 236 | 237 | // no record should be returned when the query does not match any records 238 | test.equal(diskdb.articles.find({ 239 | title: 'dummy text' 240 | }).length, 0, 'Should find no records'); 241 | 242 | // multiple search criteria matching one record 243 | test.equal(diskdb.articles.find({ 244 | title: 'diskDB rocks', 245 | published: 'today' 246 | }).length, 1, 'Should find one record'); 247 | 248 | // multiple search criteria matching two records 249 | test.equal(diskdb.articles.find({ 250 | title: 'diskDB rocks', 251 | published: 'yesterday' 252 | }).length, 2, 'Should find two records'); 253 | 254 | test.done(); 255 | }, 256 | 257 | 'findAllNested : ': function(test) { 258 | test.expect(6); 259 | //save two records 260 | diskdb.articles.save(articleComments); 261 | diskdb.articles.save(articleComments2); 262 | 263 | // no query 264 | test.equal(diskdb.articles.find().length, 2, 'Should find two records'); 265 | 266 | // find with a query 267 | test.equal(diskdb.articles.find({ 268 | rating: 2 269 | }).length, 2, 'Should find two records with query'); 270 | 271 | // find with a query 272 | test.equal(diskdb.articles.find({ 273 | rating: 1 274 | }).length, 1, 'Should find one records with query'); 275 | 276 | // no record should be returned when the query does not match any records 277 | test.equal(diskdb.articles.find({ 278 | name: 'dummy text' 279 | }).length, 0, 'Should find no records'); 280 | 281 | // check 3 level deep 282 | diskdb.articles.save(articleCommentsL3); 283 | // no query 284 | test.equal(diskdb.articles.find().length, 3, 'Should find three records'); 285 | 286 | test.equal(diskdb.articles.find({ 287 | rating: 1 288 | }).length, 2, 'Should find two records with query'); 289 | 290 | test.done(); 291 | }, 292 | }; 293 | 294 | exports.findOne = { 295 | setUp: function(done) { 296 | // create the directory 297 | if (!fs.existsSync(dbPath)) { 298 | fs.mkdirSync(dbPath); 299 | } 300 | // init diskdb 301 | diskdb.connect(dbPath, collection); 302 | // remove articles collection 303 | diskdb.articles.remove(); 304 | //reinit the collection 305 | diskdb.loadCollections(collection); 306 | done(); 307 | }, 308 | 309 | 'findOne : ': function(test) { 310 | var query = 'diskDB rocks'; 311 | test.expect(3); 312 | //save two record 313 | diskdb.articles.save(article); 314 | diskdb.articles.save(article2); 315 | 316 | test.equal(diskdb.articles.findOne().published, 'today', 'Should return the first record'); 317 | // find with a query 318 | test.equal(diskdb.articles.findOne({ 319 | title: query 320 | }).title, query, 'Should find One record on query'); 321 | // no record should be returned when the query does not match any records 322 | test.equal(diskdb.articles.find({ 323 | title: 'dummy text' 324 | }).title, undefined, 'No records should be found'); 325 | 326 | test.done(); 327 | }, 328 | 329 | 'findOneNested : ': function(test) { 330 | test.expect(3); 331 | //save two record 332 | diskdb.articles.save(article); 333 | diskdb.articles.save(article2); 334 | diskdb.articles.save(articleComments); 335 | diskdb.articles.save(articleComments2); 336 | diskdb.articles.save(articleCommentsL3); 337 | 338 | test.equal(diskdb.articles.findOne().published, 'today', 'Should return the first record'); 339 | 340 | // find with a query 341 | test.equal(diskdb.articles.findOne({ 342 | rating: 1 343 | }).title, 'diskDB rocks again', 'Should find One record on query get the title of the object'); 344 | 345 | // no record should be returned when the query does not match any records 346 | test.equal(diskdb.articles.find({ 347 | rating: 0 348 | }).title, undefined, 'No records should be found'); 349 | 350 | test.done(); 351 | }, 352 | }; 353 | 354 | exports.update = { 355 | setUp: function(done) { 356 | // create the directory 357 | if (!fs.existsSync(dbPath)) { 358 | fs.mkdirSync(dbPath); 359 | } 360 | // init diskdb 361 | diskdb.connect(dbPath, collection); 362 | // remove articles collection 363 | diskdb.articles.remove(); 364 | //reinit the collection 365 | diskdb.loadCollections(collection); 366 | done(); 367 | }, 368 | 369 | 'update : ': function(test) { 370 | var query = { 371 | 'published': 'today' 372 | }; 373 | var options = { 374 | 'multi': false, 375 | 'upsert': false 376 | }; 377 | 378 | test.expect(4); 379 | //save one record 380 | diskdb.articles.save(article); 381 | // before update 382 | test.equal(diskdb.articles.findOne().published, article.published, 'Should return the same record as inserted'); 383 | // after update 384 | test.equal(diskdb.articles.update(query, article2, options).updated, 1, 'Should return the updated objects count'); 385 | 386 | //change options 387 | query = { 388 | 'dummy': 'not found' 389 | }; 390 | options = { 391 | 'multi': false, 392 | 'upsert': true 393 | }; 394 | // should insert 395 | test.equal(diskdb.articles.update(query, article2, options).inserted, 1, 'Should return the inserted objects count'); 396 | 397 | //change options 398 | query = { 399 | published: 'yesterday' 400 | }; 401 | 402 | options = { 403 | 'multi': true, 404 | 'upsert': true 405 | }; 406 | 407 | // should update 2 record 408 | test.equal(diskdb.articles.update(query, article, options).updated, 2, 'Should return the updated objects count'); 409 | test.done(); 410 | }, 411 | }; 412 | 413 | exports.remove = { 414 | setUp: function(done) { 415 | // create the directory 416 | if (!fs.existsSync(dbPath)) { 417 | fs.mkdirSync(dbPath); 418 | } 419 | // init diskdb 420 | diskdb.connect(dbPath, collection); 421 | // remove articles collection 422 | diskdb.articles.remove(); 423 | //reinit the collection 424 | diskdb.loadCollections(collection); 425 | done(); 426 | }, 427 | 428 | 'remove : ': function(test) { 429 | test.expect(9); 430 | //save two record 431 | diskdb.articles.save(article); 432 | diskdb.articles.save(article); 433 | diskdb.articles.save(article2); 434 | 435 | 436 | //before deletion 437 | test.equal(diskdb.articles.count(), 3, 'There should be 3 records in the collection'); 438 | //deletion -- default true 439 | test.equal(diskdb.articles.remove({ 440 | 'published': 'today' 441 | }), true, 'Deletion should be successful'); 442 | //after deletion 443 | test.equal(diskdb.articles.count(), 1, 'There should be 1 record in the collection'); 444 | 445 | //repopulate data 446 | diskdb.articles.save(article); 447 | diskdb.articles.save(article); 448 | 449 | //deletion -- default true 450 | test.equal(diskdb.articles.remove({ 451 | 'published': 'today' 452 | }, true), true, 'Deletion should be successful'); 453 | //after deletion 454 | test.equal(diskdb.articles.count(), 1, 'There should be 1 record in the collection'); 455 | 456 | //repopulate data 457 | diskdb.articles.save(article); 458 | diskdb.articles.save(article); 459 | 460 | //deletion -- default true 461 | test.equal(diskdb.articles.remove({ 462 | 'published': 'today' 463 | }, false), true, 'Deletion should be successful'); 464 | //after deletion 465 | test.equal(diskdb.articles.count(), 2, 'There should be 2 records in the collection'); 466 | 467 | //remove the collection completely 468 | test.equal(diskdb.articles.remove(), true, 'Deletion should be successful'); 469 | //the collection should not exist any more 470 | test.equal(diskdb.articles, undefined, 'collection should be removed'); 471 | test.done(); 472 | }, 473 | }; 474 | 475 | var deleteFolderRecursive = function(path) { 476 | if (fs.existsSync(path)) { 477 | fs.readdirSync(path).forEach(function(file) { 478 | var curPath = path + '/' + file; 479 | if (fs.lstatSync(curPath).isDirectory()) { // recurse 480 | deleteFolderRecursive(curPath); 481 | } else { // delete file 482 | fs.unlinkSync(curPath); 483 | } 484 | }); 485 | fs.rmdirSync(path); 486 | } 487 | }; 488 | --------------------------------------------------------------------------------