Question

Is there a way to do an OR or IN query on the same property in IndexedDB?

Another word, how do I get the results for,

SELECT * FROM MyTable WHERE columnA IN ('ABC','DFT') AND columnB = '123thd'

Thanks!

Was it helpful?

Solution

There's no way to do a OR or IN-like query out of the box without during multiple requests. Usually the pattern for problems like this is "over reach, pulling more entries then you need and filtering those down to exact matches."

The Chrome team has proposed a openKeyCursor() method as a solution for this kind of query (thanks to the suggestion of Kyaw Tun, a fellow SO participant and author of ydn), but it's not standard nor adopted in other browsers.

The best solution I can think of is similar to Comment #4 of the w3 thread:

  • Open exact key cursor on columnB using value '123thd'
  • Cursor across all those records, matching objects for columnA values 'ABC' and 'DFT'

If generally you expect the number of results where columnB is '123thd' to be quite large, and most don't have values 'ABC' or 'DFT', then it may behoove you to open two cursors instead - one on columnA w/value 'ABC' and another with value 'DFT'. It's a more complex approach because you'll have to merge the two results sets manually.

OTHER TIPS

Solution with 2 successive queries using a compound index:

  1. Create a compound index on columnA and columnB. See this post for an example.
  2. Query with range parameter ['ABC','123thd'] and iterate over its results.
  3. Query with range parameter ['DFT','123thd'] and iterate over its results.

Solution with 1 query using a derived property:

The drawback of this approach is you need to know the queries you plan to execute and have to adapt this method for each query. This is something you should be doing even with SQL. Unfortunately, because many developers do not bother with proper query planning, this is not so obvious, so it is like an additional requirement to learn and start using before using indexedDB. However, proper query planning does yield better performance.

// In onupgradeneeded
function myOnUpgradeNeeded(event) {
  var db = event.target.result;
  var store = db.createObjectStore('MyTable');

  // Create a compound index on the derived property of columnA
  // and the normal property of columnB
  store.createIndex('A_ABC_OR_DFT_AND_B', ['ABC_OR_DFT_FLAG','columnB']);
}


function wrappedPut(db, obj, callback) {
  // Set the flag property
  // indexedDB cannot index booleans, so we use simple integer representation
  // where 1 represents true, 0 represents false. But we can go further since
  // we know that undefined properties dont appear in the index.

 if(obj.columnA=='ABC'||obj.columnA=='DFT') {
   obj.ABC_OR_DFT_FLAG = 1;
 } else {
   delete obj.ABC_OR_DFT_FLAG;
 }

 put(db, obj, callback);
}

// In your add/update function
function put(db, obj, callback) {
 db.transaction('MyTable','readwrite').objectStore('myTable').put(obj).onsuccess = callback;
}

// Voila, a single efficient high speed query over all items 
// in MyTable where columnA is ABC or DFT and where columnB 
// is 123thd, using a single index, without using a 3rd party
// library, and without doing some horrible double cursor stuff
// in javascript, and just using the indexedDB API.
function runTheABCDFT123Query(db, onSuccess, onComplete) {
  var tx = db.transaction('MyTable');
  tx.onComplete = onComplete;
  tx.objectStore('MyTable').index('A_ABC_OR_DFT_AND_B').openCursor(
    IDBKeyRange.only([1,'123thd'])).onsuccess = function(event) {
    if(event.target) {
      onSuccess(event.target.value);
      event.target.continue();
    }
  };
}

This functionality isn't present in the indexedDB API. You are only allowed to filter on a single index. You can try one of the libraries that are written around the indexedDB API. Some of them provide extra filter functionality.

This is a simple query equivalent to your sql query using JsStore Library

var Connection = new JsStore.Instance("MyDatabase");
Connection.select({
    From: "MyTable",
    Where:{
        columnA : {In:['ABC','DFT')]},
        columnB : '123thd'
    },
    OnSuccess:function (results){
        console.log(results);
    },
    OnError:function (error) {
        console.log(error);
    }
});

Hope this will save your time and code:).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top