On the HTML5 Indexed DB API - Part 4 of n

So far we have looked at creating databases and adding data to them. Let's next take a look at retrieving records from the database. Surprisingly, this turns out to be a bit complex. The indexed DB way of enumerating records from an object store is to use a "cursor" object. A cursor can iterate over records from an underlying object store or an index. A cursor has the following key properties:

  1. A range of records in either an index or an object store.
  2. A source that references the index or object store that the cursor is iterating over.
  3. A position indicating the current position of the cursor in the given range of records.

While the concept of a cursor is fairly straightforward, writing the code to actually iterate over an object store turns out to be somewhat tricky given the asynchronous nature of all the API calls. Let's implement the listNotes method of our NotesStore object and see what the code looks like.

...
listNotes: function (callback) {
    var self = this,
        txn = self.db.transaction(null, TransactionMode.ReadOnly),
        notes = [],
        store = txn.objectStore(self.store_name);

    Utils.request(store.openCursor(), function (e) {
        var cursor = e.result,
            iterate = function () {
                Utils.request(cursor.move(), function (e2) {
                    // if "result" is true then we have data else
                    // we have reached end of line
                    if (e2.result) {
                        notes.push(cursor.value);

                        // recursively get next record
                        iterate();
                    }
                    else {
                        // we are done retrieving rows; invoke callback
                        txn.commit();
                        callback(notes);
                    }
                });
            };

        // set the ball rolling by calling iterate for the first row
        iterate();
    });
},
...

Let's break this implementation down:

  1. First we acquire a transaction object by calling the database object's transaction method. Note that this time we are indicating that we require a "read-only" transaction.
  2. Next we retrieve a reference to the object store via the objectStore method of the transaction object.
  3. Then we issue an async call to the openCursor API on the object store. The tricky part here is that every single iteration over a record in the cursor is itself an async operation! To prevent the code from drowning in a sea of callbacks we define a local function called iterate to encapsulate the logic of iterating over every record in the cursor.
  4. This iterate function makes an async call to the cursor object's move method and recursively invokes itself again in the callback if it detects that there are more rows to be retrieved. Once all the rows in the cursor have been retrieved we finally invoke the callback method passed by the caller handing in the retrieved data as a parameter.

That's it for today's post. I'll let you mull over this for a bit till the next post when we'll cover some more scenarios.

comments powered by Disqus