Indexeddb

Time:2021-1-27

brief introduction

Indexeddb is a way to store data in the browser. Since it is called dB, because it enriches the query mode of the client, and because it is local storage, it can effectively reduce the impact of the network on the page data.

With indexeddb, the browser can store more data, thus enriching the application types of the browser.

Introduction to indexeddb

Different from traditional relational data, indexeddb is a key value database.

Value can be a complex structure object, and key can be some attribute values of the object, or other objects (including binary objects). You can use any attribute in the object as an index to speed up the search.

Indexeddb comes with transaction. All database operations are bound to specific transactions, and these transactions are submitted automatically. Indexeddb does not support manual transaction submission.

Most indexeddb APIs are asynchronous. When using asynchronous methods, the API does not immediately return the data to be queried, but returns a callback.

The essence of asynchronous API is to send an operation request to the database. When the operation is completed, we will receive a DOM event. Through this event, we will know whether the operation is successful and get the result of the operation.

Indexeddb is a NoSQL database. Different from relational database, indexeddb is object-oriented and stores JavaScript objects.

Another important feature of indexeddb is its homology policy. Each source is associated with a different database collection. Different sources are not allowed to access the databases of other sources, thus ensuring the security of indexeddb.

Use of indexeddb

In this section, we will explain how to use indexeddb with specific examples.

Browser support for indexeddb

Different browsers have different implementations for indexeddb. Normally, we can use indexeddb window.indexedDB To get the indexeddb object of the browser. But for some browsers, there is no standard window.indexedDB Instead, use the prefixed implementation.

Therefore, we usually need to make judgment and conversion in the process of use

// In the following line, you should include the prefixes of implementations you want to test.
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
// DON'T use "var indexedDB = ..." if you're not in a function.
// Moreover, you may need references to some window.IDB* objects:
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction || {READ_WRITE: "readwrite"}; // This line should only be needed if it is needed to support the object's constants for older browsers
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)

Above, we get indexeddb, idbtransaction and idbkeyrange from window.

The indexeddb represents the connection of the database. Idbtransaction represents transaction, while idbkeyrange is used to retrieve data from a specific key range in the database.

However, generally speaking, the implementation with prefix is unstable, so we usually do not recommend using it in a formal environment. Therefore, if you do not support standard expressions, you need to report an error directly

if (!window.indexedDB) {
    console.log("Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.");
}

Create indexeddb

To use indexeddb, we first need to open it:

// Let us open our database
var request = window.indexedDB.open("MyTestDatabase", 3);

The open method returns an idbopendbrequest object. At the same time, it is an asynchronous operation. The open operation does not immediately open the database or open the transaction. We can process the request by listening to the request event.

The open method passes in two parameters, the first is the name of the database, and the second is the version number of the database.

When you create a new database or upgrade an existing database version, an onupgradeneeded event will be triggered, and idbversion change event will be passed in the event. We can event.target.result To get the idbdatabase object, and then use this object to upgrade the database version. As follows:

// This event is only implemented in recent browsers   
request.onupgradeneeded = function(event) { 
  // Save the IDBDatabase interface 
  var db = event.target.result;

  // Create an objectStore for this database
  var objectStore = db.createObjectStore("name", { keyPath: "myKey" });
};

Note that the version number here is an integer. If you pass in a float, the float will be rounded.

With request, we can process it by listening to onerror or onsuccess events.

var db;
var request = indexedDB.open("MyTestDatabase");
request.onerror = function(event) {
  console.log("Why didn't you allow my web app to use IndexedDB?!");
};
request.onsuccess = function(event) {
  db = event.target.result;
};

After getting the DB object, we can set global exception handling

db.onerror = function(event) {
  // Generic error handler for all errors targeted at this database's
  // requests!
  console.error("Database error: " + event.target.errorCode);
};

The table in indexeddb is called object stores. Just like the table in relational database, every object in object stores is associated with a key. There are two concepts related to key, key path and key generator

If you are storing a JavaScript object, you can specify an attribute in the object as the key path, and this attribute will be used as the key.

If no key path is specified, the stored object can be any object, or even the basic type, such as number and string.

The key generator is the key generator.

If we want to store such data:

// This is what our customer data looks like.
const customerData = [
  { ssn: "444-44-4444", name: "Bill", age: 35, email: "[email protected]" },
  { ssn: "555-55-5555", name: "Donna", age: 32, email: "[email protected]" }
];

Take a look at the corresponding database operation:

const dbName = "the_name";

var request = indexedDB.open(dbName, 2);

request.onerror = function(event) {
  // Handle errors.
};
request.onupgradeneeded = function(event) {
  var db = event.target.result;

  // Create an objectStore to hold information about our customers. We're
  // going to use "ssn" as our key path because it's guaranteed to be
  // unique - or at least that's what I was told during the kickoff meeting.
  var objectStore = db.createObjectStore("customers", { keyPath: "ssn" });

  // Create an index to search customers by name. We may have duplicates
  // so we can't use a unique index.
  objectStore.createIndex("name", "name", { unique: false });

  // Create an index to search customers by email. We want to ensure that
  // no two customers have the same email, so use a unique index.
  objectStore.createIndex("email", "email", { unique: true });

  // Use transaction oncomplete to make sure the objectStore creation is 
  // finished before adding data into it.
  objectStore.transaction.oncomplete = function(event) {
    // Store values in the newly created objectStore.
    var customerObjectStore = db.transaction("customers", "readwrite").objectStore("customers");
    customerData.forEach(function(customer) {
      customerObjectStore.add(customer);
    });
  };
};

We need to handle all schema related operations in the onupgradeneeded event.

use first db.createObjectStore Create an objectstore for customers, and use the keypath of the object as the key.

In addition to the key, we created two indexes to improve the query speed.

Finally, we monitor transaction.oncomplete Event, and add the operation to store the object.

In the above code, we use keypath as the key.

Here is an example of using the key generator:

 var objStore = db.createObjectStore("names", { autoIncrement : true });

Curd in indexdb

All operations of indexeddb need to be in a transaction. Let’s look at an operation to open a transaction

var transaction = db.transaction(["customers"], "readwrite");

In the above example, readwrite is used to operate the customers objectstore.

Transaction receives two parameters. The first parameter is an array, in which are the objectstores to be processed in the transaction, and the second parameter is the processing mode.

With transaction, we can listen to the complete and error operations of the transaction, and then we can perform the add operation

// Do something when all the data is added to the database.
transaction.oncomplete = function(event) {
  console.log("All done!");
};

transaction.onerror = function(event) {
  // Don't forget to handle errors!
};

var objectStore = transaction.objectStore("customers");
customerData.forEach(function(customer) {
  var request = objectStore.add(customer);
  request.onsuccess = function(event) {
    // event.target.result === customer.ssn;
  };
});

In the above example, we used the add method. The premise of add is that there are no objects with the same key in the database. In addition to the add method, we can also use the put method, which is mainly used for update operations.

Let’s look at a deletion operation

var request = db.transaction(["customers"], "readwrite")
                .objectStore("customers")
                .delete("444-44-4444");
request.onsuccess = function(event) {
  // It's gone!
};

Now that we have data in our database, let’s see how to query:

var transaction = db.transaction(["customers"]);
var objectStore = transaction.objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) {
  // Handle errors!
};
request.onsuccess = function(event) {
  // Do something with the request.result!
  console.log("Name for SSN 444-44-4444 is " + request.result.name);

Here, we use it directly db.transaction , readonly mode by default.

Here is an example of an update:

var objectStore = db.transaction(["customers"], "readwrite").objectStore("customers");
var request = objectStore.get("444-44-4444");
request.onerror = function(event) {
  // Handle errors!
};
request.onsuccess = function(event) {
  // Get the old value that we want to update
  var data = event.target.result;
  
  // update the value(s) in the object that you want to change
  data.age = 42;

  // Put this updated object back into the database.
  var requestUpdate = objectStore.put(data);
   requestUpdate.onerror = function(event) {
     // Do something with the error
   };
   requestUpdate.onsuccess = function(event) {
     // Success - the data is updated!
   };
};

To update, we use the put method.

Using cursor

Indexeddb supports cursor operation. We can use cursor to traverse the data of objectstore

var objectStore = db.transaction("customers").objectStore("customers");

objectStore.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    console.log("Name for SSN " + cursor.key + " is " + cursor.value.name);
    cursor.continue();
  }
  else {
    console.log("No more entries!");
  }
};

Opencursor can accept multiple parameters. The first parameter can accept the query range of key, and the second parameter is used to specify the traversal direction. If both parameters are empty, the default is to traverse all data in ascending order.

If you want to traverse the next cursor, you can call cursor.continue .

Let’s look at the cursor usage of two parameters:

// Only match "Donna"
var singleKeyRange = IDBKeyRange.only("Donna");

// Match anything past "Bill", including "Bill"
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");

// Match anything past "Bill", but don't include "Bill"
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);

// Match anything up to, but not including, "Donna"
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);

// Match anything between "Bill" and "Donna", but not including "Donna"
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);

// To use one of the key ranges, pass it in as the first argument of openCursor()/openKeyCursor()
index.openCursor(boundKeyRange, "prev").onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // Do something with the matches.
    cursor.continue();
  }
};

In addition to opencursor, we can also traverse keycursor by using openkeycursor

// Using a normal cursor to grab whole customer record objects
index.openCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.key is a name, like "Bill", and cursor.value is the whole object.
    console.log("Name: " + cursor.key + ", SSN: " + cursor.value.ssn + ", email: " + cursor.value.email);
    cursor.continue();
  }
};

// Using a key cursor to grab customer record object keys
index.openKeyCursor().onsuccess = function(event) {
  var cursor = event.target.result;
  if (cursor) {
    // cursor.key is a name, like "Bill", and cursor.value is the SSN.
    // No way to directly get the rest of the stored object.
    console.log("Name: " + cursor.key + ", SSN: " + cursor.primaryKey);
    cursor.continue();
  }
};

In addition, we can query directly through index

var index = objectStore.index("name");

index.get("Donna").onsuccess = function(event) {
  console.log("Donna's SSN is " + event.target.result.ssn);
};

To use index, you need to request.onupgradeneeded Create index in.

Author: what about the flydean program

Link to this article:http://www.flydean.com/indexeddb-kickoff/

Source: flydean’s blog

Welcome to my official account: the most popular interpretation of “those things”, the most profound dry cargo, the most concise tutorial, and many small tricks you don’t know, etc. you’ll find them!

Recommended Today

Multi version management of golang in Windows

Multi version management of golang in Windows currentgolangThere are still some incompatibilities among different versions. Recently, we encountered some problemsgo-microThe framework can only run in go 1.13 ~ 1.14, but I installed it under windows1.15So you need to install other versions of golang, so many versionsgolangHow to useWindowsIt’s coexistence on the Internet. If you find […]