ChotaDB

A wrapper over window.localStorage to use it as No-SQL DB.

ChotaDB provides a simple and elegant API to interface with localStorage. localStorage is already a great way to store and access user data but it only supports string to be stored. What if you could store objects and use it like a DB; create collections (like MongoDB), insert records, search, sort, update, remove ...?

Quick look:

ChotaDB provide methods for complete CRUDS operations.

var DB = ChotaDB();

DB.create('Users')
  .insert({ name: 'John' })
  .find({ name: 'John' })
  .update({ name: 'John Doe'})
  .remove();

DB.drop('Users');

Nice, eh!

Why over localStorage?

localStorage is massively supported in browsers and devices currently being used and has a storage capacity ranging from 2MB to 10MB. So why not use it in more powerful way!

Why use it?

On this page

Support/Compatibility

As this library depends upon the availability of localStorage so wherever you can use localStorage, you can use ChotaDB too.

Fortunately for us, localStorage is supported on a wide array of devices and browsers.

It has been supported in browsers since:

In addition to these browsers, ChotaDB is also supported in:

Concepts

ChotaDB tries to follow the same concepts used in MongoDB. Similar to Mongo, you can create collections (known as tables in SQL) to store data.

Storing

Each data record (documents in Mongo and rows in SQL) is stored as an object in collections. In other words, a collection is an array of objects. So a typical collection may look like:

[record, record, ..., record]

Your data (record) can have any schema you like. You can store objects, strings, numbers, arrays and booleans. In one collection, your records do not need to follow the same schema as previous one.

For example:

If you insert one record as:

DB.Users.insert({
  name: 'John'
});

And second one as:

DB.Users.insert({
  first_name: 'John',
  age: 20
});

It is completely fine and won't produce any errors. All the data is encoded to JSON and then stored in localStorage.

Accessing

Similar to Mongo's cursor, ChotaDB creates a set of data for operations. Every method called on a collection can access and modify that set.

For example, current data set in a collection named guys looks like this:

[{
  _id: 1,
  name: "John",
  age: 16
},{
  _id: 2,
  name: "Doe",
  age: 18
}
},{
  _id: 3,
  name: "Smith",
  age: 22
}]

And we perform a find on it to search for all the guys who are 18 years of age or older:

DB.guys.find({
  age: {
    $gte: 18
  }
});

Data set would become:

[{
  _id: 2,
  name: "Doe",
  age: 18
}
},{
  _id: 3,
  name: "Smith",
  age: 22
}]

So all the operations performed will affect this set only. Let's say we update it and add a field name isAdult to all those.

DB.guys.find({
  age: {
    $gte: 18
  }
}).update({
  isAdult: true
});

Current data set would something like:

[{
  _id: 2,
  name: "Doe",
  age: 18,
  isAdult: true
}
},{
  _id: 3,
  name: "Smith",
  age: 22,
  isAdult: true
}]

All the methods on a collection would perform operation on data and return the collection itself except get and count which return the current data set and number of records in it, respectively.

All the data will be returned as an array of objects. Like:

[object, object, ..., object]

ChotaDB supports method chaining, promises, synchronous as well as asynchronous execution for some methods.

Events

ChotaDB supports a set of that you can subscribe to.

More details about events can be read on API wiki site.

You can use the on method on ChotaDB instance to subscribe to any event.

  var DB = ChotaDB();

  DB.on('error', function(err) {
    console.error(err);
  }).on('created', function(res) {
    console.log(res);
  }).on('inserted', function(res) {
    console.log(res);
  }).on('updated', function(res) {
    console.log(res);
  }).on('removed', function(res) {
    console.log(res);
  }).on('dropped', function(res) {
    console.log(res);
  });

How to use it

Installation

Install it via package managers:

Bower:

bower install chotadb

Then include in your file:

<script type="text/javascript" src="bower_components/chotadb/build/chotadb.min.js"></script>

In NodeJS:

npm install chotadb

Then require it like:

var ChotaDB = require('chotadb');

For other web, Cordova & Chrome based projects simply download and include the file in your pages:

<script src="chotadb.min.js"></script>

Usage

Below is an quick overview of what can be done with ChotaDB. For more details, please read API docs.

Start with creating an instance of ChotaDB:

var Store = new ChotaDB();
Create a new collection (same as table in SQL):
var Emails = Store.create('Emails');

If a collection already exists with the same name then its instance will be returned.

Once a collection is created it becomes a property on DB instance. So in this case, you can also access it via Store.Emails.

Insert a record:
Store.Emails.insert({
  title: 'Re: New DB for client-side data',
  from: '[email protected]',
  tags: ['Personal','JS','Client-side'],
  time: Date.now(),
  isRead: false
}).then(function(){
  console.log('Email arrived.');
});

Data inserted into the collection would look something like this:

{
  _id: 1,
  title: "Re: New DB for client-side data",
  from: "[email protected]",
  tags: ['Personal','JS','Client-side'],
  time: 1451302067412,
  isRead: false
}

Notice the extra _id key. It is a reserved key name and is used by ChotaDB to take care of unique and auto-incrementing ID for records in a collection. It is different from MongoDB because MongoDB creates a 12 byte (24 hex characters) string for _id but ChotaDB creates an integer.

You cannot set or change the _id. It will be overwritten even if you did. Use any other key name like id if you have to.

Search records:

Each collections has a method find which can be used for searching.

So for example we wanted to know the number of emails which are unread we'll do something like this:

Store.Emails.find({
  isRead: false
}).count(function(total){
  console.log('You have', total, 'unread emails.');
});

find will look for all the records having their isRead set to false.

If we wanted to access all the emails, we'll call find without any search criteria (object) being passed to it.

Store.Emails.find().each(function(email){
  console.log('Email:', email.title);
});

each iterates over all the records returned.

If we wanted to find the ones having JS tag

Store.Emails.find({
  tags: 'JS'
}).each(function(email){
  console.log('JS Email:', email.title);
});

Or the ones having both Personal & JS tags.

Store.Emails.find({
  tags: ['Personal', 'JS']
}).each(function(email){
  console.log('Personal JS Email:', email.title);
});
Update records:

To update a record, update method can be chained to find to update all the records returned by find.

So for example, we want all the unread emails to be marked as read

Store.Emails.find({
  isRead: false
}).update({
  isRead: true
}).then(function(){
  console.log('All the emails are marked as read.');
});
Remove records:

Let's delete all the emails older than a month.

var nowTime = Date.now();
Store.Emails.find().filter(function(record){
  if( nowTime - record.time >= 2678400000) // 31 days
    return true; // we want to keep this record for next method i.e: remove
})
.remove() // remove the ones we filtered
.then(function(){
  console.log('You do not have any emails older than a month.');
});

Demo:

Open your console on this page and give it a try. We have exposed a global variable named DB for you to play with.

Resources

For further reading and a deeper look into how to use ChotaDB, please read API docs.