kganser.com

objectDB

ObjectDB is a transactional database library that lets you treat tables as JSON structures of arbitrary size and depth. Elements in the JSON structure are addressable with URI-encoded paths, and written using simple put and delete calls, as well as append and insert for array elements.

ObjectDB is backed by indexedDB, the standard transactional noSQL database available in modern browsers. Its API makes indexedDB much easier to work with, but no less powerful.

Demo

The jsonv editor UI below allows you to modify the JSON structure stored in a database on your browser. To the right are the commands executed on your behalf.

Error: Your browser does not appear to support indexedDB. Data below will not be persisted. See Compatibility.

var db = objectDB.open("demo", {"array":["elem1","elem2"],"null":null,"number":123,"object":{"key":"value"},"string":"hello world"});
db.get();

Usage

Below are examples of objectDB's common uses. For detailed interface specs, see the documentation.

Opening the database

var db = objectDB.open('demo');

Supplying initial data

var db = objectDB.open('demo', {object: {key: 'value'}, array: ['elem1', 'elem2']});

The JSON data specified in the second argument gets populated to any new objectStore involved in the first transaction on this database if an indexedDB upgrade event is triggered.

Custom upgrade handler, database version, error handler

var db = objectDB.open('demo', function(upgrade) {
  upgrade.createObjectStore('newData', {key: 'value'});
}, 2, function(error, blocked) {
  if (blocked) alert('Database is already open in another window. Please close to continue.');
  else console.error(error);
});

If the second argument is a function, it should handle an upgrade event using the provided UpgradeTransaction object. If the third (positive integer) argument is specified and greater than the current database version (1 at database creation), an upgrade event is triggered. An optional fourth argument is both the error handler and blocked operation handler for the database.

Basic reading & writing

The database object returned by open immediately supports basic reads and writes to the default objectStore.

Read all objectStore data

db.get().then(function(data) {
  console.log(data);
});
{object: {key: 'value'}, array: ['elem1', 'elem2']}

Read data at a path

db.get('array').then(function(data) {
  console.log(data);
});
['elem1', 'elem2']

Read data at multiple paths

db.get('object').get('array').then(function(obj, arr) {
  console.log(obj, arr);
});
{key: 'value'}, ['elem1', 'elem2']

Write data

db.put('path', 'data').then(function(error) {
  console.error(error);
});

Reading with a cursor

Get only immediate elements

db.get('', false, 'immediates').then(function(data) {
  console.log(data);
});
{object: {}, array: []}

Filter elements

db.get('array', function(path, array) {
  return function(key) {
    return key < 1;
  };
}).then(function(data) {
  console.log(data);
});
['elem1']

Transactions

Write after read

db.get('number', true).then(function(num) {
  this.put('number', num+1); // transactional increment
});

Transactions on multiple objectStores

db.transaction(true, ['sessions', 'users'])
  .delete('sessions', 'kganser')
  .delete('users', 'kganser');

Compatibility

ObjectDB requires indexedDB support, including array keys. Check the compatibility table and feature tests for your browser.

Documentation

generated from source by doc.js
objectDBopendatabasestringupgrade`{}`jsonUpgradeTransactionversion1numberonErrorundefinederrorDOMErrorblockedbooleanDatabasedeletedatabasestringcallbackerrorundefinedDOMErrorblockedboolean

ObjectDB is backed by indexedDB. An upgrade transaction runs on open if the database version is less than the requested version or does not exist. If upgrade is a json value, the data stores in the first transaction operation on this Database will be populated with this value on an upgrade event. Otherwise, an upgrade will be handled by the given function via UpgradeTransaction.

UpgradeTransactionoldVersionnumbernewVersionnumbercreateObjectStorenamestringdata`{}`jsonUpgradeTransactiondeleteObjectStorenamestringUpgradeTransaction
Databasetransactionwritablefalsebooleanstores'data'string...stringTransactionScopedTransactiongetpath''Pathwritablefalsebooleancursor'deep'Cursorstore'data'stringScopedTransactioncountpath''PathwritablefalsebooleanboundsundefinedBoundsstore'data'stringScopedTransactionputpath''Pathvaluejsonstore'data'stringScopedTransactioninsertpath''Pathvaluejsonstore'data'stringScopedTransactionappendpath''Pathvaluejsonstore'data'stringScopedTransactiondeletepath''Pathstore'data'stringScopedTransactionclosefunction

get, count, put, insert, append, and delete are convenience methods that operate through transaction for a single objectStore and return the corresponding ScopedTransaction. get and count initiate a read-only transaction by default. transaction returns a ScopedTransaction if a single (string) objectStore is specified, and a Transaction if operating on multiple objectStores.

Transactiongetstorestringpath''Pathcursor'deep'CursorTransactioncountstorestringpath''PathboundsundefinedBoundsTransactionputstorestringpath''PathvaluejsonTransactioninsertstorestringpath''PathvaluejsonTransactionappendstorestringpath''PathvaluejsonTransactiondeletestorestringpath''PathTransactionthencallbackthisTransactionjsonundefined...Transaction

A Transaction acting on multiple data stores must specify a data store as the first argument to every operation. Otherwise, these methods correspond to ScopedTransaction methods.

ScopedTransactiongetpath''Pathcursor'deep'CursorScopedTransactioncountpath''PathboundsundefinedBoundsScopedTransactionputpath''PathvaluejsonScopedTransactioninsertpath''PathvaluejsonScopedTransactionappendpath''PathvaluejsonScopedTransactiondeletepath''PathScopedTransactionthencallbackthisScopedTransactionjsonundefined...ScopedTransaction

All methods are chainable and execute on the same transaction in parallel. then assigns a callback for the preceding sequence of operations, or throws an error if no operations are pending. If the transaction is not writable, put, insert, append, and delete throw an error.

By default, cursor buffers all data at the requested path as the result of a get operation. count returns a count of the number of elements in an object or array at path (with optional bounds). insert will splice the given value into the parent array at the specified position, shifting any subsequent elements forward.

When all its pending operations complete, callback is called with the result of each queued operation in order. More operations can be queued onto the same transaction at that time via this.

Results from put, insert, append, and delete are error strings or undefined if successful. get results are json data or undefined if no value exists at the requested path.

Pathstringstringnumber...

Path is either a /-delimited string of encodeURIComponent-encoded keys or an array of unencoded keys and numeric indices to an element within the json data structure; e.g. 'users/123/firstName' or ['users', 123, 'firstName'].

BoundslowerBoundnullstringnumberlowerExclusivefalsebooleanupperBoundnullstringnumberupperExclusivefalseboolean
CursorstringLevelCursorpathstringnumber...arraybooleanbooleanActionLevelCursor
LevelCursorlowerBoundnullstringnumberlowerExclusivefalsebooleanupperBoundnullstringnumberupperExclusivefalsebooleandescendingfalsebooleanactionundefinedActionvalueundefinedkeystringnumbernullvaluejsonjsonundefined
Actionkeystringnumberundefinedstring

Cursor controls how data is traversed and buffered in a get request. String values 'shallow', 'immediates', and 'deep' control the depth of the returned data object. As a function, Cursor is called for each array or object encountered in the requested json structure. It is called with a path array (of strings and/or numeric indices) relative to the requested path (i.e. [] represents the path as requested in get) and an array boolean that is true if the substructure is an array. It returns an Action callback or object with a range and action, or false to prevent recursion into the structure. lowerBound and upperBound restrict the keys/indices traversed for this object/array, and the Action function is called with each key in the requested range, in order. The Action callback can optionally return either 'skip' or 'stop' to exclude the element at the given key from the structure or to exclude and stop iterating, respectively. If specified, the value function receives the value retrieved from each key and returns a value to insert into the parent object or array, or undefined to skip insertion. value is then also called with null key and the object/array value itself. If Cursor is a LevelCursor object, it applies to the top-level object or array.

For example, the following call uses a cursor to fetch only the immediate members of the object at the requested path (equivalent to 'immediates'). Object and array values will be empty:

db.get('path/to/object', false, function(path) {
  return !path.length;
});

The following call will get immediate members of the requested object sorted lexicographically (by code unit value) up to and including key value 'c', but excluding key 'abc' (if any):

db.get('path/to/object', false, function(path) {
  return path.length ? false : {
    upperBound: 'c',
    action: function(key) {
      if (key == 'abc') return 'skip';
    }
  };
});