19

I am using IndexedDB and I have two object stores: equip (represents different equipment, primary key tagNo) and equipParts (represents the parts of a piece of equipment and has an index which is based on the tag number/serial number, primary key seqNo, with a field tagNo which represents the equipment that part is part of).

If I delete a record in equip, I want to delete all records in equipParts bearing the tagNo of equip (just like "where equipParts.tagNo = equip.tagNo").

Excerpt from my code:

var tx = db.transaction(["equip", "equipParts"],"readwrite");
var estore = tx.objectStore("equip");
var pstore = tx.objectStore("equipParts");
var tagIndex = pstore.index("by_tagNo");
var pdestroy = tagIndex.openCursor(IDBKeyRange.only(tagno)); //opens all records bearing the selected tag number
pdestroy.onsuccess = function() {
    var cursor = pdestroy.result;
    if (cursor) {
        if (cursor.value.tagNo == tagno) {
            pstore.delete(cursor.value.seqNo); //I guess I'm wrong here
        }
        cursor.continue;
    }
}
pdestroy.onerror = function() {
    alert("Deletion attempt NG");
}
var ereq = estore.delete(tagno);
ereq.onsuccess = function(e) {
    alert("Form deletion OK");
    window.location = "index.html";
}
ereq.onerror = function(e) {
    alert("Form deletion NG");
    window.location = "index.html";
}
db.close();

The problem is that only the record in equip is deleted; the records in equipParts stay there. Is there a way to delete multiple records in an IndexedDB object store based on a non-unique index (which can be the primary key of the parent object store)?

Raine Revere
  • 30,985
  • 5
  • 40
  • 52
user2727708
  • 193
  • 1
  • 1
  • 4

3 Answers3

22

You have to get primary keys to delete the records.

var pdestroy = tagIndex.openKeyCursor(IDBKeyRange.only(tagno)); 
pdestroy.onsuccess = function() {
  var cursor = pdestroy.result;
  if (cursor) {
      pstore.delete(cursor.primaryKey);
      cursor.continue();
  }
}

Alternatively, but not efficient

var pdestroy = tagIndex.openCursor(IDBKeyRange.only(tagno)); 
pdestroy.onsuccess = function() {
  var cursor = pdestroy.result;
  if (cursor) {
      cursor.delete();
      cursor.continue();
  }
}
Raine Revere
  • 30,985
  • 5
  • 40
  • 52
Kyaw Tun
  • 12,447
  • 10
  • 56
  • 83
  • 1
    Why is the second not efficient? – Frank Schwieterman Nov 27 '13 at 05:52
  • 3
    `openCursor` return `IDBCursorWithValue`, which requires reading record value, whereas the first one return `IDBCursor` without record value. – Kyaw Tun Nov 27 '13 at 07:55
  • 1
    Thanks. Is is true though only the later is doing the entire delete within the same transaction? – Frank Schwieterman Nov 27 '13 at 09:57
  • 4
    Deleting records while the cursor is open is buggy on Safari. The cursor would become null even when there're additional matching records. It's more reliable to grab all the primary keys, then call `IDBObjectStore.delete()` on each of them. – cleong May 14 '18 at 14:31
  • 1
    Be aware that you can't call `delete()` (or IDBCursor.update()) on cursors obtained from `IDBIndex.openKeyCursor()`. For such needs, you have to use `IDBIndex.openCursor()` instead. – Ehsan Chavoshi Mar 02 '21 at 09:03
  • i dont get it how the `cursor.continue()` statement works without a loop @KyawTun – Dee Jun 23 '21 at 15:20
  • it seems `cursor.continue()` will trigger request .onsuccess again – Dee Jun 23 '21 at 15:36
  • 1
    @datdinhquoc You are right, it will create a new request, but use [same source](https://w3c.github.io/IndexedDB/#dom-idbcursor-continue), so it will call same `onsuccess` callback where `continue` was called. – Kyaw Tun Jun 24 '21 at 03:53
  • mamma mia, IDBIndex should have `.delete` – Dee Jan 30 '22 at 11:08
2

I deleted multiple records belonging to an index by this way using idb :

var tx = idb.transaction("MyObjectStore", 'readwrite');
var index = tx.store.index('my_relid_idx');
var pdestroy = index.openCursor(RelID);
pdestroy.then(async cursor => {
    while (cursor) {
        cursor.delete();
        cursor = await cursor.continue();
    }
})
Raine Revere
  • 30,985
  • 5
  • 40
  • 52
Ehsan Chavoshi
  • 681
  • 6
  • 10
1

I found out an easiest approach with this

index.iterateCursor(IDBKeyRange, (cursor) => {
  if(cursor) {
    cursor.delete();
    cursor.continue();
  }
});

that way if you have it under an async function you can just use the

await index.iterateCursor...

and wait for the promise on the other side

Raine Revere
  • 30,985
  • 5
  • 40
  • 52
sebas sierra
  • 1,856
  • 1
  • 11
  • 5
  • this works, but why shouldn't be a 'delete' method directly under IDBIndex just as .get and .getAll – Dee Jun 23 '21 at 14:30
  • update: i can't see the method iterateCursor documented in IDBIndex; i've tried again, it's not working @sebas – Dee Jun 23 '21 at 14:42
  • `.iterateCursor` seems an old feature?: https://github.com/jakearchibald/idb/search?q=iterateCursor – Dee Jun 23 '21 at 15:25