Thursday, January 06, 2011

Testing IndexedDB with the Trial Tool Web App and Mozilla Firefox 4 Beta 8

• Updated 1/6/2011: Narasimhan Parashuram (@nparashuram, a.k.a. axemclion) fixed the problem reported with creating an Object Store (see end of post). Also added a link to Seth Ladd’s 2010-12-13 Google HTML5 Tech Talk (HD).

Updated 12/27/2010: Narasimhan Parashuram responded to my message to Babu Srithar regarding problems with creating an Object Store (see end of post).

Note: This post is under construction as of 12/25/2010.

Table of Contents:

About the Indexed Database API W3C Working Draft

The Indexed Database API W3C Working Draft of 19 August 2010 “… defines APIs for a database of records holding simple values and hierarchical objects. Each record consists of a key and some value. Moreover, the database maintains indexes over records it stores. An application developer directly uses an API to locate records either by their key or by using an index. A query language can be layered on this API. An indexed database can be implemented using a persistent B-tree data structure.

From the Introduction:

User agents need to store large numbers of objects locally in order to satisfy off-line data requirements of Web applications. [WEBSTORAGE] is useful for storing pairs of keys and their corresponding values. However, it does not provide in-order retrieval of keys, efficient searching over values, or storage of duplicate values for a key.

This specification provides a concrete API to perform advanced key-value data management that is at the heart of most sophisticated query processors. It does so by using transactional databases to store keys and their corresponding values (one or more per key), and providing a means of traversing keys in a deterministic order. This is often implemented through the use of persistent B-tree data structures that are considered efficient for insertion and deletion as well as in-order traversal of very large numbers of data records.

Example

A script can efficiently find records in an object store that come closest to the required value provided the value is stored in either a primary or a secondary key. In the following example, the 'books' object store holds data about books which are stored by their 'isbn' attribute. Additionally, an index is maintained on the 'author' attribute of the objects stored in the object store. This index can be used to look up books for a given author. If an exact match is not found, the operation raises an exception.

ECMAScript

var db = indexedDB.open('books', 'Book store', false);
if (db.version !== '1.0') {
  var olddb = indexedDB.open('books', 'Book store');
  olddb.createObjectStore('books', 'isbn');
  olddb.createIndex('BookAuthor', 'books', 'author', false);
  olddb.setVersion("1.0");  
}
// db.version === "1.0";  
var index = db.openIndex('BookAuthor');
var matching = index.get('fred');
if (matching)
  report(matching.isbn, matching.name, matching.author);
else
  report(null);

The next example performs the exact same logic as above asynchronously.

ECMAScript


  function findFred() {
    var store = db.objectStore('books');
    var index = store.index('BookAuthor');
    var req = index.get('fred');
    req.onsuccess = function(event) {
      var matching = event.result;
      report(matching.isbn, matching.name, matching.author);
    }
    req.onerror = function(event) {
      report(null);
    }
  }

  var db;
  var dbRequest = indexedDB.open('books', 'Book store');
  dbRequest.onsuccess = function(event) {
    db = event.result;
    if (db.version != "1.0") {
      var versionRequest = db.setVersion("1.0");
      versionRequest.ontimeout = function(event) {
        throw new Error("timeout trying to set version to 1.0");
      }
      versionRequest.onsuccess = function(event) {
        var store = db.createObjectStore('books', 'isbn');
        store.createIndex('BookAuthor', 'books', 'author', false);
        event.transaction.onabort = function(event) {
          throw new Error("error while trying to set version to 1.0");
        }
        event.transaction.oncomplete = function(event) {
          findFred(db);
        }
      }
    } else {
      findFred(db);
    }
  }
   

Support for IndexedDB by Mozilla Firefox 4 Beta

image Mozilla Firefox 4 beta versions support the draft HTML5 specification’s current IndexedDB API working draft without add-ins. (Microsoft Internet Explorer 8 and IE9 beta version currently require downloading and registering a SqlCeJsE40.dll COM library. See Testing IndexedDB with the SqlCeJsE40.dll COM Server and Microsoft Internet Explorer 8 or 9 Beta of 12/25/2010.)

Arun Ranganathan and Shawn Wilsher posted Firefox 4: An early walk-through of IndexedDB to hacks.mozilla.org blog on 6/1/2010:

image Web developers already have localStorage, which is used for client side storage of simple key-value pairs. This alone doesn’t address the needs of many web applications for structured storage and indexed data. Mozilla is working on a structured storage API with indexing support called IndexedDB, and we will have some test builds in the next few weeks. This can be compared to the WebDatabase API implemented by several browsers that uses a subset of the allowable language of SQLite. Mozilla has chosen to not implement WebDatabase for various reasons discussed in this post.

In order to compare IndexedDB and WebDatabase, we are going to show four examples that use most parts of the asynchronous APIs of each specification. The differences between SQL storage with tables (WebDatabase) and JavaScript object storage with indexes (IndexedDB) becomes pretty clear after reading the examples. The synchronous versions of these APIs are only available on worker threads. Since not all browsers currently implement worker threads, the synchronous APIs will not be discussed at this time. The IndexedDB code is based off a proposal that Mozilla has submitted to the W3C WebApps working group that has gotten positive feedback so far. The code for both APIs does not include any error handling (for brevity), but production code should always have it!

These examples are for the storage of a candy store’s sale of candy to customers, which we’ll refer to as kids. Each entry in candySales represents a sale of a specified amount of candy to a kid, specified by an entry in candy and kids respectively.

Example 1 – Opening and Setting Up a Database

This first example demonstrates how to open a database connection and create the tables or object stores if the version number is not correct. Upon opening the database, both examples check the version and create the necessary tables or object stores and then set the correct version number. WebDatabase is a bit stricter in how it handles versions by giving an error if the database version is not what the caller expects (this is specified by the second argument to openDatabase). IndexedDB simply lets the caller handle versioning as they see fit. Note that there is active discussion about how IndexedDB should handle version changes in the working group.

WebDatabase

var db = window.openDatabase("CandyDB", "",
                             "My candy store database",
                             1024);
if (db.version != "1") {
  db.changeVersion(db.version, "1", function(tx) {
    // User's first visit.  Initialize database.
    var tables = [
      { name: "kids", columns: ["id INTEGER PRIMARY KEY",
                                "name TEXT"]},
      { name: "candy", columns: ["id INTEGER PRIMARY KEY",
                                 "name TEXT"]},
      { name: "candySales", columns: ["kidId INTEGER",
                                      "candyId INTEGER",
                                      "date TEXT"]}
    ];   for (var index = 0; index < tables.length; index++) {
      var table = tables[index];
      tx.executeSql("CREATE TABLE " + table.name + "(" +
                    table.columns.join(", ") + ");");
    }
  }, null, function() { loadData(db); });
}
else {
  // User has been here before, no initialization required.
  loadData(db);
}

IndexedDB

var request = window.indexedDB.open("CandyDB",
                                    "My candy store database");
request.onsuccess = function(event) {
  var db = event.result;
  if (db.version != "1") {
    // User's first visit, initialize database.
    var createdObjectStoreCount = 0;
    var objectStores = [
      { name: "kids", keyPath: "id", autoIncrement: true },
      { name: "candy", keyPath: "id", autoIncrement: true },
      { name: "candySales", keyPath: "", autoIncrement: true }
    ];   function objectStoreCreated(event) {
      if (++createdObjectStoreCount == objectStores.length) {
        db.setVersion("1").onsuccess = function(event) {
          loadData(db);
        };
      }
    }   for (var index = 0; index < objectStores.length; index++) {
      var params = objectStores[index];
      request = db.createObjectStore(params.name, params.keyPath,
                                     params.autoIncrement);
      request.onsuccess = objectStoreCreated;
    }
  }
  else {
    // User has been here before, no initialization required.
    loadData(db);
  }
};

The post continues with the following comparative examples:

    • Example 2 – Storing Kids in the Database
    • Example 3 – List All Kids
    • Example 4 – List Kids Who Bought Candy

and concludes:

IndexedDB generally simplifies the programming model for interacting with databases, and allows for a wide number of use cases. The working group is designing this API so it could be wrapped by JavaScript libraries; for instance, there’s plenty of room for a CouchDB-style API on top of our IndexedDB implementation. It would also be very possible to build a SQL-based API on top of IndexedDB (such as WebDatabase). Mozilla is eager to get developer feedback about IndexedDB, particularly since the specification has not been finalized yet. Feel free to leave a comment here expressing your thoughts or leave anonymous feedback through Rypple.

Narasimhan Parashuram’s Trial Tool for APIs

Narasimhan Parashuram describes his Trial Tool Web app as follows:

An API Playground to illustrate the functionality of APIs using examples.

Site http://axemclion.github.com/trialtool/index.html
Source Code http://blog.nparashuram.com/search/label/trialtool
Project News http://github.com/axemclion/trialtool/

Parashuram NarasimhanTrialTool is an API viewer that lets you see the functionality of examples by category, modify them and execute them inline to see the results.
You can also create your own examples using the "fork" option in the URL parameter. Some APIs illustrated using Trial Tool

Note: Opening Firefox 4 Beta IndexedDB in IE8 or IE9 launches Trial Tool with a set of simple examples:

image

Babu Srithar’s Firefox 4 Beta IndexDB Fork

Babu Srithar posted Firefox 4 Beta - IndexedDB API on 8/2/2010:

image A few days ago, I had written about the hack that won us some cool prizes at the Yahoo Hack day India. Apart from FlickSubz, we also submitted a few more hacks. One hack that I started working on, but was unable to complete was trying to enable Indexed Database in YUI Storage.

Firefox 4 Beta 1 has a lot of interesting features for developers and one of the interesting ones was IndexedDB. Unfortunately, there is not much written about it on Firefox's blog. I could find just these articles.

I started to play around with IndexedDB and thought that I could convert the code I wrote to examples for TrialTool. The example is located at http://pastebin.com/2ZiDfezt and it has been pulled into TrialTool example list.
To see it working, download Firefox 4 Beta and hit http://tinyurl.com/ff-idxdb in Firefox 4. [This shortened URL doesn’t work for me; use the Firefox 4 Beta IndexedDB link.]

The examples are self explanatory and with the examples, you can look at the API of IndexedDB that is supported by Firefox.

If you want to create your own examples, use this link.

Opening the Trial Tool viewer with the Firefox 4 Beta IndexedDB URL displays the default page for testing the IndexedDB API:

image 

Do the following to initialize an IndexedDB instance and open a BookShop database:

  1. Click the lower (console) pane’s Output tab to enable console output.
  2. Click the Open Database link to add code to open the database.
  3. Click Load Pre Pre-requisites to add code to initialize the window.IndexedDB object.
  4. Click Run to execute the code added in steps 1 and 2.
  5. Click the [Object] link in the console’s Database Opened item to display the empty database properties, as shown here:

image

An ObjectStore is IndexedDB’s equivalent of a Windows Azure table. To (attempt to) create a BookList ObjectStore after opening the database with the preceding example, do the following:

  1. Open the Create Object Store example.
  2. Click Load Pre Pre-requisites to create the instance and open the Bookshop database.
  3. Add a document.write(“Attempting to create a ‘BookList’ ObjectStore …”); instruction after the ConsoleHelper.waitFor() … line to verify that the code block executes (see below).
  4. Click Run to execute the code.

Problem: Executing the Create Object Store example fails to create the BookList data store.  The DAO.db.createObjectStore() method fails silently; neither the success or error message appears in the console: See fix below.

image 

Executing the Get All Object Stores example returns an empty list.

The current Firebug version doesn’t support Firefox 4.0 Beta 8.

Update 12/27/2010: Narasimhan Parashuram responded as follows to my message to Babu Srithar regarding problems with creating an Object Store:

image Looks like the API has changed. The application was working on the first version for Firefox, but the API has changed. I am working on getting the correct API in the examples. 

Update 1/6/2011: Narasimhan Parashuram (@nparashuram, a.k.a. axemclion) fixed the problem I reported with creating an Object Store by adding logic to TrialTool’s ConsoleHelper.WaitFor function in nextFunc (see js/ConsoleHelper.js commit on github). Examples that require creating the Object Store now work. For example, here’s the result of running the Get All Data example with the prerequisite code added:

image

Parashuram also announced in a comment of 1/6/2011 to my Testing IndexedDB with the SqlCeJsE40.dll COM Server and Microsoft Internet Explorer 8 or 9 Beta post:

Here is a showcase of the IndexedDB API for IE - http://blog.nparashuram.com/2011/01/indexeddb-for-internet-explorer.html.

Stay tuned for a post that describes his new IE 8/9 version in detail.

Gtugs posted to YouTube on 1/4/2011 a 00:59:37 2010-12-13 Google HTML5 Tech Talk (HD) video presentation about IndexedDB by Seth Ladd:

image

Speaker: Seth Ladd is a Developer Advocate for Google Chrome. He has been developing web applications for over 14 years, and still has a blast doing it. After trying them all, he believes the most productive and fun web framework is Ruby on Rails. He has a wide array of skills and experience, including all aspects of web development, engineering, and deployment. Seth organized and produced Aloha on Rails, the Hawaii Ruby on Rails Conference.

Abstract: HTML5 gives us a number of different techniques for storing information on the client machine: AppCache for storing local copies of assets, localStorage for quick and simple key/value data, WebSQL and IndexDB for full SQL-like queries, and the FileSystem APIs for reading/writing files to a persistent sandbox. We'll spend time discussing all of these solutions and when you would want to use each.

Related OakLeaf Posts


0 comments: