How to create IndexedDb stores in a transaction? - javascript

I am creating a local IndexedDB for the first time and the browser fires onupgradeneeded request in response to window.indexedDB.open method.
I would like to create multiple tables (e.g. stores) in the onupgradeneeded event, but I'd like to do it in a transaction.
I see that transaction object supports `.objectStore', but that implies already having created the store/table.
How do I create multiple stores and wrap it in a transaction?

To create multiple object stores within onupgradeneeded:
var request = indexedDB.open(...);
request.onupgradeneeded = function(event) {
// side note: this === request === event.target === event.currentTarget
var db = this.result;
// Create 0 or more object stores here using this one instance of IDBDatabase.
db.createObjectStore(...);
db.createObjectStore(...);
...
};
The onupgradeneeded event creates an implicit transaction within the IDBRequest object that is of type VERSION_CHANGE. The transaction applies to all calls within the onupgradeneeded callback. Each of the createObjectStore calls above implicitly use the same transaction.
You can, if you want, get a reference to this transaction use this.transaction within this function. Here you are accessing the implicitly-generated transaction property of the open request, which references an IDBTransaction object that was created for you (with type set to VERSION_CHANGE), which is notably different than creating a transaction explicitly using the IDBDatabase.prototype.transaction method.

You can create multiple object store in onupgradeneeded event handler. It is already in transaction. In fact, it is global exclusive transaction on the database.
After you created required object stores and their indexes, you can create transaction on the database connection. You just need to pass list of object stores in db.transaction.
You can use the transaction onupgradeneeded, but better use only for creating object stores there. Create another transaction for reading and writing after finishing onupgradeneeded event.

Run this example of IndexedDB transaction in the console of your browser
let db;
dbName = "Jokes";
dbVersion = 5;
const request = indexedDB.open(dbName, dbVersion);
request.onupgradeneeded = e => {
db = e.target.result
console.log(db);
const jstore = db.createObjectStore("JokeStore", { keyPath: "title" });
const mstore = db.createObjectStore("MockStore", { keyPath: "title" });
alert("upgrade");
}
request.onsuccess = e => {
db = e.target.result
console.log(db);
alert("success");
}
request.onerror = e => {
alert("error" + e.target.error);
}
const tx = db.transaction("JokeStore", "readwrite");
tx.onerror = e => alert(e.target.error);
const jstoretx = tx.objectStore("JokeStore");
jstoretx.add({ title: "Knock Knock", text: "Who's there? There is a place." });
It creates an entry in the database store.

Related

Uncaught DOMException: Failed to execute 'put' on 'IDBObjectStore': Evaluating the object store's key path did not yield a value at request.onsuccess

I'm trying to Store some application data using indexedDB
Here is my code
function _getLocalApplicationCache(_, payload) {
const indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.shimIndexedDB;
if (!indexedDB) {
if (__DEV__) {
console.error("IndexedDB could not found in this browser.");
}
}
const request = indexedDB.open("ApplicationCache", 1);
request.onerror = event => {
if (__DEV__) {
console.error("An error occurred with IndexedDB.");
console.error(event);
}
return;
};
request.onupgradeneeded = function () {
const db = request.result;
const store = db.createObjectStore("swimlane", {keyPath: "id", autoIncrement: true});
store.createIndex("keyData", ["name"], {unique: false});
};
request.onsuccess = () => {
// creating the transition
const db = request.result;
const transition = db.transaction("swimlane", "readwrite");
// Reference to our object store that holds the swimlane data;
const store = transition.objectStore("swimlane");
const swimlaneData = store.index("keyData");
payload = JSON.parse(JSON.stringify(payload));
store.put(payload);
const Query = swimlaneData.getAll(["keyData"]);
Query.onsuccess = () => {
if (__DEV__) {
console.log("Application Cache is loaded", Query.result);
}
};
transition.oncomplete = () => {
db.close();
};
};
}
If I do use different version then 1 here --> indexedDB.open("ApplicationCache", 1);
I'm getting a error like they keyPath is already exist. And other than than for version 1 I'm getting this error.
Can someone please help me where i'm doing wrong.
Review the introductory materials on using indexedDB.
If you did something like connect and create a database without a schema, or created an object store without an explicit key path, and then you stored some objects, and then you edited the upgradeneeded callback to specify the keypath, and then never triggered the upgradeneeded callback to run because you continue to use current version number instead of a newer version number, it would be one possible explanation for this error.
The upgradeneeded callback needs to have logic that checks for whether the object stores and indices already exist, and only create them if they do not exist. If the store does not exist, create it and its indices. If the store exists and the indices do not, add indices to the store. If the store exists and the indices exist, do nothing.
You need to trigger the upgradeneeded callback to run after changing your database schema by connecting with a higher version number. If you do not connect with a higher version number, the callback never runs, so you will end up connecting to the older version where your schema changes have not taken place.

Create multiple Schema in indexed DB

I am trying to create mutiple schema with indexed db , the scripts that implemented is
const openDB = () => {
var indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
// Create multiple schema
// Create a dynamic Schema to append data
var open = indexedDB.open("ExcelExtension", 1);
open.onupgradeneeded = function () {
var db = open.result;
var store_sheet = db.createObjectStore("schema_1", { keyPath: "id" });
var index = store_sheet.createIndex("NameIndex", ["name.last", "name.first"]);
};
open.onupgradeneeded = function () {
var db2 = open.result;
var store_sheet2 = db2.createObjectStore("schema_2", { keyPath: "id" });
var index = store_sheet2.createIndex("NameIndex2", ["name.last", "name.first"]);
};
}
//html
<button onlick="openDB()> Create DB
what did i do wrong ? there is no error in script but its only reflecting one schema in db
When i check on my database on browser, i can see only one schema was created
Review how event listeners/handlers work in JavaScript. There are generally two ways to register an event handler:
// way 1
thing.onsomeevent = myEventHandler;
// way 2
thing.addEventListener('someevent', myEventHandler)
Most of the time it does not matter which syntax you use because most of the time you are only registering one event handler.
However, sometimes there is a big difference. The difference between the two methods is that when you have multiple event handlers, in the property assignment approach it overwrites all event handlers registered with one event handler. In the second way, it does not overwrite.
You can only register multiple event handlers using the add event listener syntax.

how fire upgradeneeded event with out upgrade version of the indedexdb

i have a question about the event "upgradeneeded".
i need to check the data base every time the user reload the page, but how to fire it with out upgrade the version of the indexeddb, or it's the unique solution ?
request.addEventListener('upgradeneeded', event => {
var db = event.target.result;
var planningObjectStore = db.transaction("planningSave", "read").objectStore("planningSave");
});
"upgradeneeded" is only fired when you need to change the schema, which you signal by changing the version number. If you're not modifying the schema - e.g. you're just reading/writing to existing object stores - use the "success" event instead. Also, there's an implicit transaction within the "upgradeneeded" event, so no need to call transaction() there.
var request = indexedDB.open("mydb", 1); // version 1
// only fires for newly created databases, before "success"
request.addEventListener("upgradeneeded", event => {
var db = event.target.result;
var planningObjectStore = db.createObjectStore("planningSave");
// write initial data into the store
});
// fires after any successful open of the database
request.addEventListener("success", event => {
var db = event.target.result;
var tx = db.transaction("planningSave");
var planningObjectStore = tx.objectStore("planningSave");
// read data within the new transaction
});

Create index on already existing objectStore

As an example on basic setup one index is created.
db.onupgradeneeded = function(event) {
var db = event.target.result;
var store = db.createObjectStore('name', { keyPath: 'id' });
store.createIndex('by name', 'name', { unique: false });
};
Question:
Is it possible to create/append more indexes to the same objectStore on the future versionupdate? Since if I try:
db.onupgradeneeded = function(event) {
var db = event.target.result;
var store = db.createObjectStore('name', { keyPath: 'id' });
store.createIndex('by newName', 'newName', { unique: false });
};
It throws an error that current objectStore does already exist. An if I try to create store reference using transaction:
db.onupgradeneeded = function(event) {
var db = event.target.result;
var store = db.transaction('name', 'readwrite').objectStore('name');
store.createIndex('by newName', 'newName', { unique: false });
};
It throws that version change transaction is currently running
Yes it is possible. It can be a bit confusing at first. You want to get the existing object store via the implicit transaction created for you within onupgradeneeded. This is a transaction of type versionchange which is basically like a readwrite transaction but specific to the onupgradeneeded handler function.
Something like this:
var request = indexedDB.open(name, oldVersionPlusOne);
request.onupgradeneeded = myOnUpgradeNeeded;
function myOnUpgradeNeeded(event) {
// Get a reference to the request related to this event
// #type IDBOpenRequest (a specialized type of IDBRequest)
var request = event.target;
// Get a reference to the IDBDatabase object for this request
// #type IDBDatabase
var db = request.result;
// Get a reference to the implicit transaction for this request
// #type IDBTransaction
var txn = request.transaction;
// Now, get a reference to the existing object store
// #type IDBObjectStore
var store = txn.objectStore('myStore');
// Now, optionally inspect index names, or create a new index
console.log('existing index names in store', store.indexNames);
// Add a new index to the existing object store
store.createIndex(...);
}
You also will want to take care to increment the version so as to guarantee the onupgradeneeded handler function is called, and to represent that your schema (basically the set of tables and indices and properties of things) has changed in the new version.
You will also need to rewrite the function so that you only create or make changes based on the version. You can use event.oldVersion to help with this, or things like db.objectStoreNames.contains.
Something like this:
function myOnUpgradeNeeded(event) {
var is_new_db = isNaN(event.oldVersion) || event.oldVersion === 0;
if(is_new_db) {
var db = event.target.result;
var store = db.createObjectStore(...);
store.createIndex('my-initial-index');
// Now that you decided you want a second index, you also need
// to do this for brand new databases
store.createIndex('my-second-new-index');
}
// But if the database already exists, we are not creating things,
// instead we are modifying the existing things to get into the
// new state of things we want
var is_old_db_not_yet_current_version = !isNaN(event.oldVersion) && event.oldVersion < 2;
if(is_old_db_not_yet_current_version) {
var txn = event.target.transaction;
var store = txn.objectStore('store');
store.createIndex('my-second-new-index');
}
}
Pay close attention to the fact that I used event.target.transaction instead of db.transaction(...). These are not at all the same thing. One references an existing transaction, and one creates a new one.
Finally, and in addition, a personal rule of mine and not a formal coding requirement, you should never be using db.transaction() from within onupgradeneeded. Stick to modifying the schema when doing upgrades, and do all data changes outside of it.

Why is this call to IDBObjectStore.get() is resulting in confusing behavior?

I am trying to get the hang of using indexedDB to store data client side.
consider the following code:
function queryURL(message, sender)
{
chrome.contextMenus.removeAll();
var openRequest = indexedDB.open("Tags",1);
openRequest.onsuccess = function(event){
var queryURL = message['host'];
var db = event.target.result;
var objectStore = db.transaction("domains").objectStore("domains");
var query = objectStore.get(queryURL);
query.onsuccess = function(event){
alert(query.result);
delete query.result["domain"];
createMenuItems(query.result);
available_commands=request.result;
};
db.onerror = function(event){
console.log("an error bubbled up during a transaction.");
};
};
openRequest.onerror = function(event){
console.log("error opening DB");
};
}
I do not fully understand what should be happening in the query.
The result is the same whether or not the key that is queried for is in the database:
query.onsuccess() runs and query.result is undefined so the
code errors and exits as soon as I try to delete a key from
query.result.
If the key is not found, query.onsuccess() should not be
running, correct?
If the key is found, query.result should hold the object that
corresponds to that key, correct?
In case it helps, here is the code that I used to initialize the database:
const db_name="Tags";
var request = window.indexedDB.open(db_name, 1);
var tags = [
//codes: 0 - markdown wrap tag
// 1 - HTML wrap tag
// 2 - single tag
{ domain: "www.youtube.com",
bold:["*",0],
strikethrough:["-",0],
italic:["_",0]
},
{ domain: "www.stackoverflow.com",
bold:["<strong>",1],
italic:["<em>",1],
strikethrough:["<del>",1],
superscript:["<sup>",1],
subscript:["<sub>",1],
heading1:["<h1>",1],
heading2:["<h2>",1],
heading3:["<h3>",1],
blockquote:["<blockquote>",1],
code:["<code>",1],
newline:["<br>",2],
horizontal:["<hr>",2]
}
];
request.onerror = function(event) {
alert("Error opening the database");
};
request.onupgradeneeded = function(event) {
var db = event.target.result;
alert("I'm doing stuff!");
var objectStore = db.createObjectStore("domains", {keyPath: "domain" });
objectStore.createIndex("domain", "domain", { unique: true });
objectStore.transaction.onComplete = function(event) {
var domanStore=db.transaction("domains","readwrite").objectStore("domains");
for(var i in tags)
{
domainStore.add(tags[i]);
}
}
};
Here are some links to the resources I am using:
Using IndexedDB
IDBObjectStore
IDBRequest
Finding out that the result is empty or undefined is a successful query. So yes, you get onsuccess call with result === undefined.
onerror is only reserved for when something breaks, e.g. you supplied an invalid key.
From IDBObjectStore.get docs:
Note: This method produces the same result for: a) a record that doesn't exist in the database and b) a record that has an undefined value. To tell these situations apart, call the openCursor() method with the same key. That method provides a cursor if the record exists, and no cursor if it does not.
Yes. It is even more confusing when delete method return success, even if no record is deleted.
Since request error event is cancellable bubbling event, it is not feasible to invoke to error callback even if no record is found. If request is on error and error is not prevented, its transaction will be aborted and indexedDB.onerror will be called as well. So invoking success with undefined result is still better than invoking error.

Categories

Resources