11

I have a composite index of two properties on an indexeddb objectstore and wish to retrieve a cursor based on the range of both of these properties.

Here's an example object in the store :

{species: 'Oak',xcoord: 123456, ycoord: 654321}

and index :

treeStore.createIndex("treelocation", ["xcoord","ycoord"], { unique: false });

The index creation is succesful and I can see it in the Chrome developer tools, however now I'd like to open a cursor with a keyrange on both the x and y co-ordinates (which will be the extent of a map).

Searching online I can't see how to do this, and opening an index with an array of key ranges doesn't seem to work.

Cœur
  • 37,241
  • 25
  • 195
  • 267
kes
  • 320
  • 3
  • 10

3 Answers3

8

The range suggestion is part of the answer, but even with array keys it really is just a 1-dimensional range, not a 2- (or N-) dimensional selection of the data. With your current schema you'll need to do something like this:

index.openCursor([lowX, lowY], [highX, highY]).onsuccess = function(e) {
    var cursor = e.target.result;
    if (!cursor) return; // done!

    var x = cursor.key[0], y = cursor.key[1];
    // assert(lowX <= x && x <= highX);
    if (y < lowY) {
        cursor.continue([x, lowY]);
    } else if (y > highY) {
        cursor.continue([x + 1, lowY]);
    } else {
        processRecord(cursor.value); // we got one!
        cursor.continue();
    }
};

(if coordinates are not integral, replace + 1 with an appropriate epsilon)

I've posted an example of a generalized solution to:

https://gist.github.com/inexorabletash/704e9688f99ac12dd336

Joshua Bell
  • 7,727
  • 27
  • 30
  • 1
    Is this still the best approach? Trying to find items in a plane that are inside of a bounding box, starts to get slow when # items is 100k+. Does adding more than 1 to `x` work? Something like `continue([x + boundsDiff, lowY])`? (sorry for necro) – IrkenInvader Mar 26 '19 at 17:35
  • 1
    As noted, the key space is one dimensional. Calling `continue([x+n, /*anything*/])` will skip over any items with a first array entry less than `x+n` that haven't been visited yet – Joshua Bell Apr 01 '19 at 22:46
6

I have been told the solution is indeed IDBKeyRange.bound([lowX,lowY],[highX,highY]).

Myrne Stol
  • 11,222
  • 4
  • 40
  • 48
  • 12
    This will not behave as you might expect. The range `IDBKeyRange.bound([2,2], [4,4])` includes values like [3,0] and [3,5] as [2,2] < [3,0] < [3,5] < [4,4] per the definition of keys (https://w3c.github.io/IndexedDB/#key-construct) Which is to say, you will need to do additional filtering, as this solution on its own is insufficient. e.g. if the key [x,y_less_than_min] is seen, ignore it and advance the cursor with `continue([x,lowY])`; if the key [x,y_greater_than_max] is seen, ignore it and advance the cursor with `continue([x+1, lowY])` – Joshua Bell Oct 06 '15 at 17:37
4

The index you created is a composite index. It is query like this:

index = objectStore.index('treelocation');
index.get([123456, 654321]); 

Alternatively, you can use two indexes for each coorrd. In my opinion it is better.

x_index = objectStore.index('xcoord'); 
y_index = objectStore.index('ycoord');
x_species = x_index.get(IDBKeyRange.only(123456)) 
y_species = y_index.get(IDBKeyRange.only(654321)) 
species = x_species.intersect(y_species); // this is result
Kyaw Tun
  • 12,447
  • 10
  • 56
  • 83
  • 1
    Thanks Kyaw - this would be a great solution if I only wanted to retrieve records at one location, however the x and y are geographic co-ordinates and I'm looking to retrieve a range of both as one get, hence the composite index. – kes May 14 '13 at 08:45