27

I spent hours and hours searching for this one, and just by trial and error was I able to finally find the solution. Logging this in Stack Overflow for future searchers.

Q:

How do I create a composite key in indexeddb?

Keys are created in indexeddb on object stores using the following:

var db;
var openRequest = indexedDB.open('myDB', 1);
openRequest.addEventListener('error', () => {
    console.error('Unable to open db');
});
openRequest.addEventListener('upgradeneeded', function(event){
    var store = db.createObjectStore('myStore', 
        {keyPath: /* composite key */ }
    );
});
openRequest.addEventListener('success', function(){
    db = openRequest.result;
});

I have tried placing objects, defining multiple times, how does one create a composite key, or is this a limitation of the API?

Note: If you are looking for how to query a composite key using a range, please check out this post

Community
  • 1
  • 1
SnareChops
  • 13,175
  • 9
  • 69
  • 91
  • Possible duplicate of [In IndexedDB, is there a way to make a sorted compound query?](http://stackoverflow.com/questions/12084177/in-indexeddb-is-there-a-way-to-make-a-sorted-compound-query) – Josh Nov 22 '15 at 14:50
  • @Josh Though the referenced post does talk about a composite index, it is primarily focused on the IDBKeyRange in selecting a range of elements. The post also does not explain how to create a composite primary key for an Indexeddb object store and therefore I believe this post to not be a duplicate. – SnareChops Nov 22 '15 at 21:51
  • Yes, definitely not a duplicate. This is about _creating_ (and populating) an object store using a compound `keyPath`; the referenced topic is about _extracting_ items from such a store. The two complement each other very effectively. Together, they're very helpful; as SnareChops says, this is not well documented (even four years later!). – Velojet Mar 02 '20 at 06:52

2 Answers2

47

As it turns out, the answer is very simple, but not documented well anywhere I have looked, and not obvious at first glance. Use an array of strings...

var store = db.createObjectStore('myStore', 
    {keyPath: ['id1', 'id2']}
);

Composite indexes can also be created in the same fashion.

For work with composite key data, see the answer below by Malvineous

Raine Revere
  • 30,985
  • 5
  • 40
  • 52
SnareChops
  • 13,175
  • 9
  • 69
  • 91
  • The array elements don't have to be strings; numeric values work just as well. – Velojet Mar 02 '20 at 06:42
  • @Velojet those strings are property names, not the values of the key. However, if you would pass such an array to `put`, then it is the value of the keys. – rudolfbyker Jun 17 '20 at 13:19
  • 1
    Thanks for the clarification, @rudolfbyker. I should make it clear that I'm referring to a composite key array in the context of a `put` operation: `db.put(storename, item, [var1, var2])` where `var1` and `var2` are numeric values. My essential point still stands, that while the composite key itself can be an array, it doesn't have to be an array of _strings_. – Velojet Jun 18 '20 at 23:38
  • @snareChops Thanks for the Q&A, I'm unable to find any example or documentation on how to retrieve data based on this composite key. Like I want to find all records with id1 and extract the last record from the set. Can you please help on this ? I'm adding like row1 => [id1 = abc & id2 = 1], row2 => [id1 = abc & id2 = 2] – Abdul Rehman Aug 20 '20 at 12:44
  • @Bsienn See the answer below that Malvineous just posted – SnareChops Apr 24 '21 at 17:45
  • @SnareChops Love this community, thanks for the update. Though I already had figured it out. cheers – Abdul Rehman Apr 25 '21 at 11:16
5

Just to add to this, to actually use a composite key, it's not that clear either.

Adding an object isn't too bad:

myStore.add({
    id1: 'a',  // first part of key
    id2: 'b',  // second part of key
    etc: 'c',  // other data to store
});

However getting the object again using the composite key isn't all that obvious. You need to pass an array as the key, with the values in the same order as the keyPath array originally passed to createObjectStore().

myStore.get(['a', 'b'])   // look for id1=a + id2=b

Here, the first value in the array (a) matches up with keyPath[0] which in the above answer is set to id1.

Malvineous
  • 25,144
  • 16
  • 116
  • 151