Querying an IndexedDB compound index with a shorter array
Asked Answered
P

3

3

IndexedDB allows you to make indexes on multiple properties. Like if you have objects like {a: 0, b: 0} you can make an index on a and b.

The behavior of compound indexes is pretty weird, but apparently it is supposed to be possible to query with an array that is shorter than the compound index. So in my example, I should be able to query on something like [0] and get results for a==0.

But I can't seem to get that to work. Here's an example which you can run on JS Bin:

var db;

request = indexedDB.open("test", 1);
request.onerror = function (event) { console.log(event); };

request.onupgradeneeded = function (event) {
  var db = event.target.result;
  db.onerror = function (event) { console.log(event); };

  var store = db.createObjectStore("store", {keyPath: "id", autoIncrement: true});
  store.createIndex("a, b", ["a", "b"], {unique: true});

  store.add({a: 0, b: 0});
  store.add({a: 0, b: 1});
  store.add({a: 1, b: 0});
  store.add({a: 1, b: 1});
};

request.onsuccess = function (event) {
  db = request.result;
  db.onerror = function (event) { console.log(event); };

  console.log("Only [0, 0]");
  db.transaction("store").objectStore("store").index("a, b").openCursor(IDBKeyRange.only([0, 0])).onsuccess = function (event) {
    var cursor = event.target.result;
    if (cursor) {
      console.log(cursor.value);
      cursor.continue();
    } else {
      console.log("Any [0, x]");
      db.transaction("store").objectStore("store").index("a, b").openCursor(IDBKeyRange.only([0])).onsuccess = function (event) {
        var cursor = event.target.result;
        if (cursor) {
          console.log(cursor.value);
          cursor.continue();
        }
      };
    }
  };
};

Here is the JS Bin link again.

The output I see is:

Only [0, 0]
Object {a: 0, b: 0, id: 1}
Any [0, x]

But I was hoping to see:

Only [0, 0]
Object {a: 0, b: 0, id: 1}
Any [0, x]
Object {a: 0, b: 0, id: 1}
Object {a: 0, b: 1, id: 2}

Where am I going wrong?

Puppy answered 5/10, 2014 at 13:38 Comment(0)
D
5

You should use key range IDBKeyRange.bound([0], [0, '']), so that all keys started with [0] included.

Deach answered 6/10, 2014 at 14:16 Comment(1)
That does work, but I don't think I understand why. Isn't bound supposed to compare as >= and <=?Puppy
D
6

A slightly more general version of Kyaw Tun's answer: if all keys are known to be arrays with two non-array elements, and you want the ones matching [x, <any>], use IDBKeyRange.bound([x], [x, []]).

Dunbarton answered 7/9, 2017 at 14:11 Comment(1)
cheers @Ivan! that's exactly what was looking forChaparro
D
5

You should use key range IDBKeyRange.bound([0], [0, '']), so that all keys started with [0] included.

Deach answered 6/10, 2014 at 14:16 Comment(1)
That does work, but I don't think I understand why. Isn't bound supposed to compare as >= and <=?Puppy
P
1

Just to explain why Ivan's (and Kyaw's too) answer does work: values of different type can be compared.

Only if you're sure that you are comparing numbers you can use IDBKeyRange.bound([x], [x, MAX_NUMBER]) (if b<MAX_NUMBER) or IDBKeyRange.bound([x], [x, '']) but in general case (assumed without array subkeys) you have to put an empty array ([]) as upper bound for the b subkey.

From W3C specification :

Number keys are less than date keys. Date keys are less than string keys. String keys are less than binary keys. Binary keys are less than array keys

Papuan answered 14/1, 2023 at 13:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.