Monday, January 2, 2012

Indexed DB: Reading multiple records

In my previous post I have been talking about reading data. Today, I’ll be talking about reading multiple records at once. Here for we will use a cursor. A cursor is a transient mechanism used to iterate over multiple records in a database. The storage operations of the cursor can be used on the underlying index or an object store.

In the IDBObjectStore interface, we have the openCursor method to create a new cursor for retrieving data. In the IDBIndex interface, we have 2 ways to create a new cursor. These methods are openCursor to retrieve the values from the index and openKeyCursor to retrieve the keys. There are 2 optional parameters that can be provided when calling these methods. The first parameter is an IDBKeyRange, with this we will narrow the result by defining the bounds of the keys we want to retrieve. The second parameter is the direction the cursor must navigate trough the results.

IDBKeyrange

A key range is a continuous interval over some data type used for keys. A key range can have one of the following situations:

  • lower bounded: The keys must have a value smaller than the provided lower bound
  • upper bounded: The keys must have a value larger than the provided upper bound
  • lower and upper bounded: The keys must have a value between the lower and the upper bound
  • unbounded: All keys will be valid
  • Single value: The key must be the provide value

    The upper and lower bound can be open, this means the value of the bound won’t be included, or closed, this means the value of the bound will be included.

    More information about the IDBKeyRange interface can be found here. You will also find some more information about the methods to create a key rage. If you want to use an unbounded key range, you don’t need to provide a key range.

    Retrieving data with a cursor

    As for all actions preformed on the database a transaction is also needed in case of reading data with a cursor.

       1: var txn = dbconnection.transaction([“ObjectStoreName”]);
       2: var objectStore = txn.objectStore(“ObjectStoreName”);
       3: var cursorReq;
       4: // IE 10, Chrome and Firefox implementation
       5: if(window.msIndexedDB || window.mozIndexedDB || window.webkitIndexedDB){
       6:     cursorReq = store.openCursor();
       7: }
       8: // IE Indexed DB Prototype implementation
       9: else{
      10:     cursorReq = store.openCursor(IDBKeyRange.lowerBound(0));
      11: }
      12:  
      13: handleCursor(cursorReq, txn, success, error);

    First things first, we define the cursor. One of the first thing you notice is that the IE Indexed DB prototype requires a key range. This means it won’t be possible to use the unbounded key range. Because I’m using an auto increment key for my object store, I can take use of the lowerbound method to create a key range from 0 to forever. After the cursor is defined, I use a method to handle the reading of the cursor.



  •    1: function handleCursor(cursorReq, txn, success, error){
       2:     cursorReq.onsuccess = function (event) {
       3:         if (event.result) {
       4:             var cur = event.result;
       5:             if (cur) {
       6:                 cursor_get_record(cur);
       7:             }
       8:         }
       9:         else if (cursorReq.result) {
      10:             var cur = cursorReq.result;
      11:             // Present the data
      12:             cur.continue();
      13:         }
      14:     cursorReq.onerror = error
      15: }

    Again the IE Indexed DB prototype has a different implementation for handling a cursor. I’ll start with explaining the correct way. If the request object contains a result. If this is empty, you reached the end of the cursor. If the result is an object, the value of the current record in the cursor can be found in the value field. When you handled the value, you can navigate to the next record in the cursor by calling the continue function on the result object.


    When working with the Indexed DB prototype, you have to make another approach. That’s why I created a recursive function for it. (cursor_get_record). The only parameter I need to pass is the result object I get out of the parameter that is provided when the call for the cursor was successful.



       1: function cursor_get_record(cur){  
       2:     cur.move(); 
       3:     if(cur.value)
       4:     {
       5:         // Present the data
       6:         cursor_get_record(cur);
       7:     }
       8: }

    With the move method we fetch the next record from the cursor. If the cursor contains a value, you can handle it and afterwards you call the current method to fetch the next record of cursor.

    2 comments:

    1. Any idea how to simulate a LIKE query in SQL? Thanks

      ReplyDelete
    2. For now the only way to search is by working with boundaries. If I find something that works like a LIKE in SQL I'll blog about it.

      ReplyDelete