IndexedDB

As you have seen in the previous sections, localStorage is very easy; however, it has the limitation of 5 MB of storage capacity. IndexedDB, on the other hand, does not have this limitation; however, it has a complex API. The main downside of IndexedDB is that it is not fully supported on all major browsers:

IndexedDB

Figure 6.4: Browser support for IndexedDB

At the moment of writing this book, IndexedDB is fully supported by Chrome and Firefox, while Safari and IE have partial support.

A big difference between localStorage and IndexedDB is that IndexedDB is not a key/value store; IndexedDB has collections (tables) and a query API. If you have worked with MongoDB, you will be familiar with the way IndexedDB stores data.

Getting started with IndexedDB

An IndexedDB database is composed of one or more stores. A store is like a JSON container, it contains a collection of JSON. If you have worked with SQL, then a store is like a table. If you have worked with MongoDB, a store is a like a collection. In the same way as MongoDB, IndexedDB is schemaless, which means that you don't need to define the schema of the records (JSONs).

One of the consequences of schemaless is that the data in the collections is not heterogeneous, you can have different types JSON objects in the same store. For example, you can store contact and invoice data in the same store.

IndexedDB is more flexible and powerful than localStorage; however, with great power comes great responsibility. You will have to deal with stores, cursors, indexes, transactions, migrations, and asynchronous API:

Getting started with IndexedDB

Figure 6.5: IndexedDB

Database versions

Databases usually change with time; maybe a new feature needs a new store or adds an index. All IndexedDB databases have a version number. The first time that you create a new database, it starts with version 1. With the help of each version number, you can define the stores and indexes as you need.

IndexedDB does not allow you to create new stores or indexes, unless you have changed the version number. When a new version number is detected, IndexedDB enters a versionchange state and calls the onupgradedneeded() callback, which you can use to modify the database.

Every time you change the version number, you have the opportunity to run database migrations in the onupgradedneeded() callback. Every time you open a connection with IndexedDB, you can specify a version number:

indexedDB.open(<database name>, <version number>)

The first time you open a database, IndexedDB enters the versionchange state and calls the onupgradedneeded()callback.

Creating stores

To create stores on IndexedDB, you need to put the database on the version change state, which you can do in the following two ways:

  1. Create a new database.
  2. Change the version number of the database.

In the following example, we are creating a new database named library:

var request = indexedDB.open("library");

// In this callback the database is in the versionchange state
request.onupgradeneeded = function() {
  // The database did not previously exist, so that
  // we can create object stores and indexes.
vardb = request.result;
var store = db.createObjectStore("books", {keyPath: "isbn"});

  // Populate with initial data.
store.put({
title: "Quarry Memories",
 author: "Fred",
isbn: 123456});
store.put({
title: "Water Buffaloes",
 author: "Fred",
isbn: 234567});
store.put({
title: "Bedrock Nights",
 author: "Barney",
isbn: 345678});
};

request.onsuccess = function() {
window.db = request.result;
};

When the open() method is called, it returns a request object that we can use to register the onscuccess()callback called when the database is successfully opened and is ready to be used. As we are creating a new database, the onupgradeneeded()callback is called.

The database handler is in the result attribute of the request object. You can use the createObjectStore() method of the database handler in order to create a new store:

createObjectStore(name, options)

The first argument of the createObjectStore() method is the name of the store, in our example it is library. The options arguments should be a plain object where the available fields are as follows:

Option name

Description

Default value

autoIncrement

This auto increments the primary key attribute

false

keyPath

This is the attribute name in the objects that is to be used as primary key

null

After the object store creation, a store handler is returned, which you can use to insert new records in the recently created object store. The put()method is used to insert new records in the store it accepts as argument the JSON to be stored:

Creating stores

Figure 6.6: IndexedDB in Google Chrome

As you can see in the preceding image, the object store has the objects that we insert with the put() method in the onupgradeneeded event.

Delete a database

You can always delete a database with the deleteDatabse() method. If you did something wrong and want to start over, just delete the database:

indexedDB.deleteDatabase('library');

Add elements to an object store

You have seen how to create and delete stores. Now, you will see how to connect to a database and add records to an object store outside of the onupgradeneeded() callback:

vartx = db.transaction("books", "readwrite");
var store = tx.objectStore("books");

store.put({
  title: "Quarry Memories",
  author: "Fred",
isbn: 123456
});
store.put({
  title: "Water Buffaloes",
  author: "Fred",
isbn: 234567
});
store.put({
  title: "Bedrock Nights",
  author: "Barney",
isbn: 345678
});

tx.oncomplete = function() {
console.log('Records added!');
};

Note that we are creating an IndexedDB transaction. The IndexedDB specification by W3C defines a transaction as follows:

A transaction is used to interact with the data in a database. Whenever data is read or written to the database it is done by using a transaction.

Transactions offer some protection from application and system failures. A transaction may be used to store multiple data records or to conditionally modify certain data records. A transaction represents an atomic and durable set of data access and data mutation operations.

The transaction()method of the indexedDB object has two arguments: scope and mode, as shown in the following table:

Argument

Description

Examples

scope

The store or stores where the transaction interacts

'books',['contacts', 'invoices']

mode

This states what type of interaction will be done

'readonly', 'readwrite'

When the transaction is created, you can access the stores with the objectStore()method of the transaction object, which returns an object store handler that you can use to add or remove records.

The put() method is used to insert objects into the store; however, the method is asynchronous, which means that the records are not stored immediately like in localStorage. You should register an oncomplete() callback in the transaction object that will be called when the operations are done.

Performing queries

To query the data in an object store, you need to open a readonly transaction:

vartx = db.transaction("books", "readonly");
var store = tx.objectStore("books");

var request = store.openCursor(IDBKeyRange.only(123456));
request.onsuccess = function() {
var cursor = request.result;
  if (cursor) {
    // Called for each matching record.
console.log(cursor.value);
cursor.continue();
} else {
    // No more matching records, cursor === null
console.log('Done!');
  }
};

Queries are to be done by opening cursors with the openCursor() method. The first argument of the openCursor() method is a query that should be an IDBKeyRange object:

  • only(value): It looks for the value, such as an == operation
  • lower(value): It looks for the values lower or equal to the value, such as a <= operation
  • lowerOpen(value): It looks for values lower than the value, such as a < operation
  • upper(value): It looks for values greater than or equal to the value, such as a >= operation
  • upperOpen(value): It looks for values greater than the value, such as a > operation

These are some of the queries that are available; please refer to the IndexedDB specification for a complete list of all the available queries. IndexedDB uses the query to compare the values that are passed as an argument with the objects in the store; however, which attribute in the store is compared? The answer is the key that is specified in keyPath. In our example, the isbn attribute will be used.

The cursor will call the onsuccess() callback repeatedly for every object found, you should call the continue() method on the cursor object to fetch the next object. The result will be null when no more objects are found.

If you want to query the objects by a different attribute, you should create indexes in the store for the attributes that you need. Use a different version number to add new indexes to the object stores:

var request = indexedDB.open("library", 2);

request.onupgradeneeded = function() {
vardb = request.result;
var store = db.createObjectStore("books", {keyPath: "isbn"});
vartitleIndex = store.createIndex("by_title", "title", {
    unique: true
  });
varauthorIndex = store.createIndex("by_author", "author");

  // ...
};

request.onsuccess = function() {
db = request.result;

vartx = db.transaction("books", "readonly");
var store = tx.objectStore("books");
var index = store.index("by_title");

var request = index.get("Bedrock Nights");
request.onsuccess = function() {
    // ...
  };
};

As you can see in the preceding example, you can use an index to query objects. The same onsuccess() method is invoked every time that the index finds a result.

Delete objects in the store

To delete objects, you should call the delete() method in the object store with a query argument for these objects that you want to remove:

vartx = db.transaction("books", "readwrite");
var store = tx.objectStore("books");

store.delete(123456); // deletes book with isbn == 123456
store.delete(IDBKeyRange.lowerBound(456789)); // deletes books with store <= 456789
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.86.211