In IndexedDB, what is the difference between IDBObjectStore.put and IDBCursor.update?

In IndexedDB, there are two ways to update an object already in the database. You can call IDBCursor.update or IDBObjectStore.put.

Both accept the updated object as a parameter.

  • InvalidStateError when opening IndexedDB in Firefox 21
  • Javascript: Searching indexeddb using multiple indexes
  • How do I get the results of an indexedDb request outside the scope of its callback
  • How to persist data in a Service Worker
  • IndexedDB: Retrieve item with max value
  • “Cannot call method 'open' of undefined” error when using indexedDB.open
  • IDBCursor.update requires you to open a cursor first, but you basically have to do that with IDBObjectStore.put too to retrieve the prior value.

    IDBObjectStore.put will create a new object if it can’t find one to update, but since it has to check for an update first, I don’t know if this would actually create a performance difference.

    So what is the difference between these methods? Is there anything I’m missing? I tried to make a test case to investigate performance differences:

    var db;
    
    function runTest(N, cb) {
      console.log("N = " + N);
    
      // Add some fake data to object store
      var tx = db.transaction("store", "readwrite");
      tx.objectStore("store").clear();
      for (var i = 0; i < N; i++) {
        tx.objectStore("store").add({"id": i, "index":0,"guid":"21310c91-ff31-4cb9-ae68-16d48cbbd84a","isActive":false,"balance":"$1,840.25","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":"Witt Fletcher","gender":"male","company":"QUILM","email":"wittfletcher@quilm.com","phone":"+1 (851) 485-2174","address":"729 Conover Street, Marenisco, Virginia, 7219","about":"Incididunt do deserunt ut quis. Exercitation et ut ad aliqua ut do sint Lorem. Aliquip sit aliquip nulla excepteur pariatur ut laborum ea dolor. Consectetur incididunt et et esse commodo id eu dolor in. Nostrud sit mollit occaecat ullamco commodo aute anim duis enim et aliqua. Aute duis nostrud do minim labore sunt mollit in voluptate aliquip sit. Aliqua aliquip non ipsum exercitation cillum irure in.\r\n","registered":"2014-07-02T03:42:57 +04:00","latitude":-65.942119,"longitude":-129.471674,"tags":["reprehenderit","nostrud","velit","exercitation","nulla","nulla","est"],"friends":[{"id":0,"name":"Kristine Francis"},{"id":1,"name":"Lizzie Ruiz"},{"id":2,"name":"Bobbie Underwood"}],"greeting":"Hello, Witt Fletcher! You have 7 unread messages.","favoriteFruit":"apple"});
      }
      tx.oncomplete = function () {
        // Update with cursor.update
        var tStart = (new Date()).getTime();
        tx = db.transaction("store", "readwrite");
        var store = tx.objectStore("store");
        for (var i = 0; i < N; i++) {
          store.openCursor(i).onsuccess = function (event) {
            var cursor = event.target.result;
            cursor.value.age = 34;
            cursor.update(cursor.value);
          };
        }
        tx.oncomplete = function () {
          var tEnd = (new Date()).getTime();
          console.log("cursor.update - " + (tEnd - tStart) + " milliseconds");
    
          // Update with put
          tStart = (new Date()).getTime();
          tx = db.transaction("store", "readwrite");
          store = tx.objectStore("store");
          for (var i = 0; i < N; i++) {
            store.openCursor(i).onsuccess = function (event) {
              var cursor = event.target.result;
              cursor.value.age = 34;
              store.put(cursor.value);
            };
          }
          tx.oncomplete = function () {
            tEnd = (new Date()).getTime();
            console.log("put - " + (tEnd - tStart) + " milliseconds");
    
            if (cb !== undefined) {
              cb();
            }
          };
        };
      };
    }
    
    request = indexedDB.open("yes5ytrye", 1);
    request.onerror = function (event) { console.log(event); };
    
    request.onupgradeneeded = function (event) {
      var db = event.target.result;
      db.onerror = function (event) { console.log(event); };
    
      db.createObjectStore("store", {keyPath: "id"});
    };
    
    request.onsuccess = function (event) {
      db = request.result;
      db.onerror = function (event) { console.log(event); };
    
      runTest(100, function () {
        runTest(1000, function () {
          runTest(10000, function () {
            console.log("Done");
          });
        });
      });
    };
    

    You can try it here.

    In Firefox, I get output like:

    N = 100
    cursor.update - 39 milliseconds
    put - 40 milliseconds
    N = 1000
    cursor.update - 229 milliseconds
    put - 256 milliseconds
    N = 10000
    cursor.update - 2194 milliseconds
    put - 2096 milliseconds
    Done
    

    Basically no difference in performance. The results are a little different in Chrome when N is large:

    N = 100
    cursor.update - 51 milliseconds
    put - 44 milliseconds
    N = 1000
    cursor.update - 414 milliseconds
    put - 447 milliseconds
    N = 10000
    cursor.update - 13506 milliseconds
    put - 22783 milliseconds
    Done
    

    But like I said above, I’m not even sure if there should be a difference between these two methods, because it seems like they have to do exactly the same thing.

  • Can I use indexeddb across subdomains?
  • The mysterious case of disappearing blobs in Chrome (in IndexedDB)
  • Add Index to Pre-Existing ObjectStore In IndexedDB Using Javascript
  • How is indexedDB conceptually different from HTML5 local storage?
  • Preventing UI flicker when loading async data in React.js
  • Getting a “setVersion” is not a function error when using indexedDB
  • 2 Solutions collect form web for “In IndexedDB, what is the difference between IDBObjectStore.put and IDBCursor.update?”

    The main difference between update cursor and put, is the fact that you need to retrieve the element you want to update with cursor; on the other hand when using the put statement, you only need to know the id of the element you are updating and you just execute the put function which is defined on the store level. However this speed up will only work in cases where you have the complete object stored in memory.

    I updated your code a bit and got the speed up:

    var db;
    
    function runTest(N, cb) {
      console.log("N = " + N);
    
      // Add some fake data to object store
      var tx = db.transaction("store", "readwrite");
      tx.objectStore("store").clear();
      for (var i = 0; i < N; i++) {
        tx.objectStore("store").add({"id": i, "index":0,"guid":"21310c91-ff31-4cb9-ae68-16d48cbbd84a","isActive":false,"balance":"$1,840.25","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":"Witt Fletcher","gender":"male","company":"QUILM","email":"wittfletcher@quilm.com","phone":"+1 (851) 485-2174","address":"729 Conover Street, Marenisco, Virginia, 7219","about":"Incididunt do deserunt ut quis. Exercitation et ut ad aliqua ut do sint Lorem. Aliquip sit aliquip nulla excepteur pariatur ut laborum ea dolor. Consectetur incididunt et et esse commodo id eu dolor in. Nostrud sit mollit occaecat ullamco commodo aute anim duis enim et aliqua. Aute duis nostrud do minim labore sunt mollit in voluptate aliquip sit. Aliqua aliquip non ipsum exercitation cillum irure in.\r\n","registered":"2014-07-02T03:42:57 +04:00","latitude":-65.942119,"longitude":-129.471674,"tags":["reprehenderit","nostrud","velit","exercitation","nulla","nulla","est"],"friends":[{"id":0,"name":"Kristine Francis"},{"id":1,"name":"Lizzie Ruiz"},{"id":2,"name":"Bobbie Underwood"}],"greeting":"Hello, Witt Fletcher! You have 7 unread messages.","favoriteFruit":"apple"});
      }
      tx.oncomplete = function () {
        // Update with cursor.update
        var tStart = (new Date()).getTime();
        tx = db.transaction("store", "readwrite");
        var store = tx.objectStore("store");
        for (var i = 0; i < N; i++) {
          store.openCursor(i).onsuccess = function (event) {
            var cursor = event.target.result;
            cursor.value.age = 34;
            cursor.update(cursor.value);
          };
        }
        tx.oncomplete = function () {
          var tEnd = (new Date()).getTime();
          console.log("cursor.update - " + (tEnd - tStart) + " milliseconds");
    
          // Update with put
          tStart = (new Date()).getTime();
          tx = db.transaction("store", "readwrite");
          store = tx.objectStore("store");
          for (var i = 0; i < N; i++) {
              //you don't need the element just update
            store.put({"id": i, "index":0,"guid":"21310c91-ff31-4cb9-ae68-16d48cbbd84a","isActive":false,"balance":"$1,840.25","picture":"http://placehold.it/32x32","age":33,"eyeColor":"brown","name":"Witt Fletcher","gender":"male","company":"QUILM","email":"wittfletcher@quilm.com","phone":"+1 (851) 485-2174","address":"729 Conover Street, Marenisco, Virginia, 7219","about":"Incididunt do deserunt ut quis. Exercitation et ut ad aliqua ut do sint Lorem. Aliquip sit aliquip nulla excepteur pariatur ut laborum ea dolor. Consectetur incididunt et et esse commodo id eu dolor in. Nostrud sit mollit occaecat ullamco commodo aute anim duis enim et aliqua. Aute duis nostrud do minim labore sunt mollit in voluptate aliquip sit. Aliqua aliquip non ipsum exercitation cillum irure in.\r\n","registered":"2014-07-02T03:42:57 +04:00","latitude":-65.942119,"longitude":-129.471674,"tags":["reprehenderit","nostrud","velit","exercitation","nulla","nulla","est"],"friends":[{"id":0,"name":"Kristine Francis"},{"id":1,"name":"Lizzie Ruiz"},{"id":2,"name":"Bobbie Underwood"}],"greeting":"Hello, Witt Fletcher! You have 7 unread messages.","favoriteFruit":"apple"});
          }
          tx.oncomplete = function () {
            tEnd = (new Date()).getTime();
            console.log("put - " + (tEnd - tStart) + " milliseconds");
    
            if (cb !== undefined) {
              cb();
            }
          };
        };
      };
    }
    
    request = indexedDB.open("yes5ytrye", 1);
    request.onerror = function (event) { console.log(event); };
    
    request.onupgradeneeded = function (event) {
      var db = event.target.result;
      db.onerror = function (event) { console.log(event); };
    
      db.createObjectStore("store", {keyPath: "id"});
    };
    
    request.onsuccess = function (event) {
      db = request.result;
      db.onerror = function (event) { console.log(event); };
    
      runTest(100, function () {
        runTest(1000, function () {
          runTest(10000, function () {
            console.log("Done");
          });
        });
      });
    };

    Yes, they are same in the sense that both of them use to update a record value. Use simple put if you already know the key and value, otherewise a bit convoluted cursor update is only option.

    Performance should be same for updating a record.