IndexedDB is a non-relational database, and I want to use a sentence of
developer.mozilla.org : "IndexedDB is a way for you to persistently store data inside a user's browser. Because it lets you create web applications with rich query abilities regardless of network availability, these applications can work both online and offline."
Ok, first of all, let's talk about some classic steps when using IndexedDB.
(1) Open database
(2) Build and update the version of database
(3) Use "createObjectStore()" and "createIndex" methods to create storage space
(4) Use transaction("objectStore" method) to add, delete, change, get data
(5) Use cursor(set search range)
(6) Use index
(7) Concurrency
(8) Security(same origin policy)
And we should care about the compatibility:
window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange
// (Mozilla has never prefixed these objects, so we don't need window.mozIDB*)
1. Open database
The IndexedDB object only has one method —— open(), this method has two parameters: database name and version. When opening a non-exist database, the IndexedDB will creat one. And the version should bigger than "1", because when a database is first created, its version is the integer 1. Each database has one version at a time; a database can't exist in multiple versions at once. The only way to change the version is by opening it with a greater version than the current one. But if a database is exist, and we don't want to change the content of it, maybe we can omit the second parameter.
var request = window.indexedDB.open("Mydb", 2);
var db = null;
request.onerror = function(event) {
alert("Database error: " + event.target.errorCode);
// Do something with request.errorCode!
};
request.onsuccess = function(event) {
db = request.result;
// Do something with request.result!
};
Because IndexedDB is fully asynchronous execution, so most of the operations will use "onerror" and "onsuccess" events. And we should know that "request" equals to "event.target".
2. Build and update the version of database
We will use "onupgradeneeded" event to update the version of database, and create some parts of database by using "createIndex" method.
// our data
const user = [
{ id: 1, name: "Bill", tel: 123456},
{ id: 2, name: "Dylan", tel: 456789}
];
var request = indexedDB.open("Mydb", 2);
request.onerror = function(event) {};
request.onupgradeneeded = function(event) {
var db = event.target.result;
// create a object store space to save our data
// we will use "id" as the keyPath, because it is unique
var objectStore = db.createObjectStore("users", { keyPath: "id" });
// create a index of "name" so that we can search users by "name"
// "name" is not a unique parameter
objectStore.createIndex("name", "name", { unique: false });
// create a index of "tel" so that we can search users by "tel"
// we want "tel" to be a unique parameter
objectStore.createIndex("tel", "tel", { unique: true });
// save data
for (var i in user) {
objectStore.add(user[i]);
}
};
Just remember that "onupgradeneeded" is the only place that we can change the database structure.
3. Use transaction
If we want to add, get, delete, put data, we should use transaction.
var transaction = db.transaction(["users"], "readwrite");
transaction.oncomplete = function(event) { alert("All done!"); };
transaction.onerror = function(event) {};
// add data
var objectStore = transaction.objectStore("users"); //load a storage space
for (var i in user) {
var request = objectStore.add(user[i]);
request.onsuccess = function(event) {
// event.target.result == user[i].id
};
}
// delete data
var request = db.transaction(["users"], "readwrite").objectStore("users").delete("1");
request.onsuccess = function(event) {};
// get data
var getdata = db.transaction("users").objectStore("users").get("1");
getdata.onsuccess = function(event) {
alert("Name for id = 1 is " + event.target.result.name);
};
4. Use cursor
If we want to get data from storage space, we can use "get()" method, but if we want to get all values, just use "openCursor()" function, it has two parameters(of course you can also leave it empty), one is the range of cursor, and the other is direction of traversal.
var objectStore = db.transaction("users").objectStore("users");
objectStore.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
alert("Name for id " + cursor.key + " is " + cursor.value.name);
cursor.continue(); // must!!
}
else {
alert("No more entries!");
}
};
We can also set a range for cursor
// only find "Danna"
var singleKeyRange = IDBKeyRange.only("Donna");
// find all results before "Bill"(include)
var lowerBoundKeyRange = IDBKeyRange.lowerBound("Bill");
// find all results before "Bill"(not include)
var lowerBoundOpenKeyRange = IDBKeyRange.lowerBound("Bill", true);
// find all results after "Donna"(not include)
var upperBoundOpenKeyRange = IDBKeyRange.upperBound("Donna", true);
// Match anything between "Bill" and "Donna", but not including "Donna"
var boundKeyRange = IDBKeyRange.bound("Bill", "Donna", false, true);
index.openCursor(boundKeyRange).onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
// Do something with the matches.
cursor.continue();
}
};
We can also change the cursor's direction by setting the second parameter, and "IDBCursor.prev" means that the last one will be searched first.
objectStore.openCursor(null, IDBCursor.prev).onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
// Do something with the entries.
cursor.continue();
}
};
5. Use index
In the second part, we just use "creatIndex" method to creat some index for our storage space, such as "name" and "tel" index. So in this part, we can use index to find data, because maybe sometimes the index is not unique (ex: "name"), but the index function only return one result which has minimum keys.
var index = objectStore.index("name");
index.get("Donna").onsuccess = function(event) {
alert("Donna's id is " + event.target.result.id);
};
6. Concurrency
We should care about the version of our database in the different webpages at the same time, if the version has been updated in one page, other pages' version must be changed at the same time.
var openReq = mozIndexedDB.open("MyTestDatabase", 2);
openReq.onblocked = function(event) {
// if it has been loaded in other place, we can't use it.
alert("Please close all other tabs with this site open!");
};
openReq.onupgradeneeded = function(event) {
db.createObjectStore(/* ... */);
useDatabase(db);
}
openReq.onsuccess = function(event) {
var db = event.target.result;
useDatabase(db);
return;
}
function useDatabase(db) {
// if we want to update the database, we have to close it.
db.onversionchange = function(event) {
db.close();
alert("A new version of this page is ready. Please reload!");
};
// other operation
}