11

Adding an object to an IndexedDB objectStore will fail if the key already exists. How can I check for the existence of an object with a given key – preferably synchronously (no reason for another layer of callbacks) and without pulling the object.

I know how to do get requests asynchronously via transactions, but it seems a bit of an ordeal to go through every time I want to add an object.

note Solution only has to work in Chrome (if that helps)

Chris
  • 11,819
  • 19
  • 91
  • 145

4 Answers4

18

The best way to check existence of a key is objectStore.count(key). Which is async.

In your case, the best option is openCursor of your key. If exists, cursor will come up.

var req = objectStore.openCursor(key);
req.onsuccess = function(e) {
  var cursor = e.target.result; 
  if (cursor) { // key already exist
     cursor.update(obj);
  } else { // key not exist
     objectStore.add(obj)
  }
};
Ben
  • 54,723
  • 49
  • 178
  • 224
Kyaw Tun
  • 12,447
  • 10
  • 56
  • 83
9

A bit late for an answer, but possible it helps others. I still stumbled -as i guess- over the same problem, but it's very simple:

If you want to INSERT or UPDATE records you use objectStore.put(object) (help)
If you only want to INSERT records you use objectStore.add(object) (help)

So if you use add(object), and a record key still exists in DB, it will not overwritten and fires error 0 "ConstraintError: Key already exists in the object store".

If you use put(object), it will be overwritten.

Lutz
  • 770
  • 1
  • 8
  • 19
  • Hey @Lutz. Do you happen to know how `add` behaves when you have passed in an array as the `keyPath` value? Like this: ```js db.createObjectStore('users', { keyPath: ['email', 'password'] }); ``` I have tried the above but it doesn't fire the ConstraintError as expect. It is adding with differing emails but same password. – mjfneto Oct 29 '20 at 15:19
  • This is exactly what I came here for. Thank you! – LOAS Aug 25 '21 at 08:34
8

So far none of the browsers have the sync API implemented so you're going to have to do it async. An objectStore exposes a get method which you provide it with a key and it'll return you the object (or null) that matches the key.

There's a really good tutorial on MDN that covers using IDB, and getting a record is covered too, but the inlined code is:

db.transaction("customers").objectStore("customers").get("444-44-4444").onsuccess = function(event) {
  alert("Name for SSN 444-44-4444 is " + event.target.result.name);
};

If you don't want retrieve the record then you can always used the count method on an index as explained here in the spec. Based on the result of that you can either use add or put to modify the record, it'll save extracting the record if you don't need to.

Aaron Powell
  • 24,927
  • 18
  • 98
  • 150
  • Ideally I wouldn't pull out and initialize a record (some of the objects have a lot of deeply-nested data). Looking for something akin to SQL's EXISTS. I know indexedDB isn't SQL-based, but other key-value stores like Redis support checking the existence of a key – Chris Mar 12 '13 at 16:40
  • @Chris - nah there's no method like that, I've added another alternative to working it out if you don't want to hydrate the object – Aaron Powell Mar 12 '13 at 22:36
  • Well that answers it, though not the way I'd hoped haha. Thank you! – Chris Mar 12 '13 at 22:46
  • Some options: 1. Do a count that is restricted with IDBKeyRange.only(id), which avoids deserialization. 2. Use openKeyCursor with IDBKeyRange.only(id) which also avoids deserialization. 3. Use onerror in addition to onsuccess. onerror is fired when store.add fails with your key set (there is a prop defined in your object that corresponds to the store's key that you tried to insert). This way you do not need to do a pre-insert count check (your logic reacts to store.add instead of preempting it). – Josh Apr 11 '13 at 15:30
1

The question is why do you want to know this? Maybe you should use another approach?

You can use auto increment, this way you don't need to check if a key exists, you will always get a unique one.

You can also use the put method instead of the add method. With the put the data will be updated if the key exists and if the key doesn't exist the data is added.

Everything depends on the reason why you want to check if something exists.

Kristof Degrave
  • 4,142
  • 22
  • 32
  • Because I'm building an application that, on install, loaded JSON in as demo data. In testing – and in some cases in the wild – the installation callback will fire multiple times so the demo data actually already exists (as indexedDB isn't wiped). – Chris Mar 12 '13 at 16:36
  • So in that case you could use the put method. Existing data will be overwritten and new data will be added – Kristof Degrave Mar 12 '13 at 17:42
  • 2
    Okay that works in this very specific case, but that isn't the same as checking for existence. That is just forcing data into the DB. There are other cases where I can imagine someone wanting to check for a record's existence without pulling out the data or force updating it. – Chris Mar 12 '13 at 17:44
  • I can imagine that, but less on the key it self. I think you will do this more on an index (for example check if a name exist) than for a key. When you check an index, you can make use of the getKey method to only retrieve the key in that case. Or as Kyaw mentioned, use the count method. Either way you can only do it async. – Kristof Degrave Mar 13 '13 at 08:57