Brannon Dorsey edited this page Jul 13, 2018 · 25 revisions


Note: Try out our WIP jsdoc-generated class reference here.


LokiJS is a document oriented javascript database, somewhat similar to MongoDB. This is only an overview, for full documentation refer to lokijs.org.

It supports indexing, querying and filtering of data collections. LokiJS also supports more advanced features such as mapReduce, transactions and lets you implement custom remote synchronization to save data to a server (or a local file on mobile). Persistence to disk is already implemented for CommonJS environment such as nodejs, on a mobile you just have to request the filesystem and pass lokijs's serialize() as content.

In a browser or mobile environment you only need to include build/lokijs.min.js


1. Document oriented

Data is stored as javascript objects, serialized to JSON for disk persistence.

2. Indexing

You can specify indexes to speed searches on certain object properties. The 'id' field is automatically indexed and uses binary search algorithm to avoid slower for(;;) loops


You can declare custom view functions to return result sets based on complex logic.

4. Map reduce

You can declare map and reduce functions to retrieve aggregate data from your database.

Example usage

If you're working in a node.js environment, run npm install lokijs and make sure to call var loki = require('lokijs')

Creating the db:

var db = new loki('Example');

Create a collection, specifying name, Type, index fields and whether the collection is transactional:

var users = db.addCollection('users', { indices: ['email'] });
// note that indices can be a single string or an array of strings

Note that indexes and transactional flag are optional parameters. Transactions in LokiJS simply allow you to run operations and automatically restore the database to the stage it was before the beginning of the transaction if an error occurred.

Add a bunch of users in the database:

var odin = users.insert( { name : 'odin', email: 'odin.soap@lokijs.org', age: 38 } );
var thor = users.insert( { name : 'thor', email : 'thor.soap@lokijs.org', age: 25 } );
var stan = users.insert( { name : 'stan', email : 'stan.soap@lokijs.org', age: 29 } );
var oliver = users.insert( { name : 'oliver', email : 'oliver.soap@lokijs.org', age: 31 } );
var hector = users.insert( { name : 'hector', email : 'hector.soap@lokijs.org', age: 15} );
var achilles = users.insert( { name : 'achilles', email : 'achilles.soap@lokijs.org', age: 31 } );

Operate an update:

stan.name = 'Stan Laurel';
// update object (this really only syncs the index)

DynamicViews (recommended approach):

var dv = users.addDynamicView('a_complex_view');
dv.applyWhere(function aCustomFilter(obj){
  return obj.name.length  < 5 && obj.age > 30;
//view the data

// apply some changes
users.insert({ name: 'ratatosk', email: 'rata@tosk.r', age: 10320 });
// behold the dynamicview updating itself by inspecting the data

'Where' filter functions:

function ageView(obj){
  return obj.age > 30;
// a little more complicated, users with names longer than 3 characters and age over 30
function aCustomFilter(obj){
  return obj.name.length  < 5 && obj.age > 30;

// test the filters
var result = users.where(ageView);
var anotherResult = users.where(aCustomFilter);

Map Reduce (live example on lokijs.org ):

function getDuration( obj ){
  return obj.complete ? null : obj.duration;

function getAverage( array ){
  var cumulator = 0;
  var i = array.length >>> 0;
  var actual = 0;
    if(array[i] != null){
      cumulator += array[i];
  return ( cumulator / actual).toFixed(2);

var avgDuration = todos.mapReduce( getDuration, getAverage );

Querying via method chaining :

  .find({'age':{'$gt': 25}})
  .where(function(obj){ return obj.name.indexOf("in") != -1 })

Simple named transform :

users.addTransform('progeny', [
    type: 'find',
    value: {
      'age': {'$lte': 40}
    type: 'simplesort',
    property: 'age',
    desc: true

var results = users.chain('progeny').data();