how fire upgradeneeded event with out upgrade version of the indedexdb - javascript

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
});

Related

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.

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.

Safari: IndexedDB: Cannot create object stores in separate transactions

It appears that the Safari and iPhone web browsers are incapable of allowing the user to create different object stores from separate transactions. This is even the case when the user closes the database, increments the version number and then uses createObjectStore() within the onupgradedneeded callback.
Is there a workaround?
For example, visit http://bl.ocks.org/redgeoff/1dea140c52397d963377 in Safari and you'll get an alert with the "AbortError" when Safari attempts to create the 2nd object store.
For convenience, here is the same snippet of code:
var idb = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB
|| window.msIndexedDB;
// Generate a unique db name as IndexedDB is very delicate and we want our test
// to focus on a new DB
var dbName = 'mydb' + '_' + (new Date()).getTime() + '_'
+ Math.round(1000000*Math.random());
var db = null;
var version = 1;
var open = function (version, onSuccess, onUpgradeNeeded) {
var request = null;
if (version) {
request = idb.open(dbName, version);
} else { // 1st time opening?
request = idb.open(dbName);
}
request.onupgradeneeded = function () {
if (onUpgradeNeeded) {
onUpgradeNeeded(request);
}
};
request.onsuccess = function () {
db = request.result;
if (onSuccess) {
onSuccess(request);
}
};
request.onerror = function () {
console.log('error=', request.error);
alert('error=' + JSON.stringify(request.error));
};
};
var createObjectStore = function (name, callback) {
db.close(); // synchronous
version++; // increment version to trigger onupgradeneeded
open(version, callback, function (request) {
request.result.createObjectStore(name, {
keyPath: 'id'
});
});
};
// NOTE: we could create the first store when opening the DB for the first time, but we'll keep
// things simple and reuse our createObjectStore code for both object stores
open(null, function () {
createObjectStore('store1', function () {
createObjectStore('store2', function () {
console.log('done creating both stores');
});
});
});
I tried using a sleep of 2 secs after the DB is closed and reopened and that doesn't appear to work. If there is no workaround then this essentially means that you cannot use the IndexedDB implementation in Safari to dynamically create object stores, which means that you need to know all your object stores before creating a DB.
Unless I am mistaken and someone has a workaround, the best way to dynamically add object stores is to implement a db-per-object-store design. In other words, you should create a new database whenever you need to create a new object store.
Another good option is to use https://github.com/axemclion/IndexedDBShim to emulate IndexedDB with WebSQL.

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.

How to create IndexedDb stores in a transaction?

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.

Categories

Resources