Getting a "setVersion" is not a function error when using indexedDB - javascript

I tried to use following code to test the performance of IndexedDB.
The code is modified from http://www.html5rocks.com/en/tutorials/indexeddb/todo/ ,
It works well in chrome, but fails in Firefox 10, saying "db.setVersion is not a function".
I want to know how can I modify the code to make it work in firefox?
var count=0;
var MAX=10;
var times=3;
var allTime;
var stime;
var etime;
var html5rocks = {};
var indexedDB = window.indexedDB || window.webkitIndexedDB ||
window.mozIndexedDB;
if ('webkitIndexedDB' in window) {
window.IDBTransaction = window.webkitIDBTransaction;
window.IDBKeyRange = window.webkitIDBKeyRange;
}
html5rocks.indexedDB = {};
html5rocks.indexedDB.db = null;
html5rocks.indexedDB.onerror = function(e) {
//console.log(e);
alert("Why didn't you allow my web app to use IndexedDB?!");
};
html5rocks.indexedDB.open = function(type) {
var request = indexedDB.open("todos");
request.onsuccess = function(e) {
var v = "1.20";
html5rocks.indexedDB.db = e.target.result;
var db = html5rocks.indexedDB.db;
// We can only create Object stores in a setVersion transaction;
if (v!= db.version) {
var setVrequest = db.setVersion(v);
// onsuccess is the only place we can create Object Stores
setVrequest.onerror = html5rocks.indexedDB.onerror;
setVrequest.onsuccess = function(e) {
if(db.objectStoreNames.contains("todo")) {
db.deleteObjectStore("todo");
}
var store = db.createObjectStore("todo",
{keyPath: "number"});
addTest();
};
}
else addTest();
};
request.onerror = html5rocks.indexedDB.onerror;
}
html5rocks.indexedDB.addTodo = function(todoText,num) {
var db = html5rocks.indexedDB.db;
var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("todo");
var data = {
"text": todoText,
"number": num
};
var request = store.put(data);
request.onsuccess = function(e) {
count++;
if(count>=times*MAX)
{
etime=new Date;
var t=document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML=t+((etime.valueOf()-stime.valueOf())/times)+"<br/>";
allTime=0;stime=new Date;count=0;
getTest();
}
};
request.onerror = function(e) {
console.log("Error Adding: ", e);
};
};
html5rocks.indexedDB.getTodo = function(id) {
var db = html5rocks.indexedDB.db;
var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("todo");
var request = store.get(id);
request.onsuccess = function(e) {
count++;
if(count>=times*MAX)
{
etime=new Date;
var t=document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML=t+((etime.valueOf()-stime.valueOf())/times)+"<br/>";
allTime=0;stime=new Date;count=0;
delTest();
}
};
request.onerror = function(e) {
console.log("Error getting: ", e);
};
};
html5rocks.indexedDB.deleteTodo = function(id) {
var db = html5rocks.indexedDB.db;
var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("todo");
var request = store.delete(id);
request.onsuccess = function(e) {
count++;
if(count>=times*MAX)
{
etime=new Date;
var t=document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML=t+((etime.valueOf()-stime.valueOf())/times)+"<br/>";
allTime=0;stime=new Date;count=0;
dataTest();
}
};
request.onerror = function(e) {
console.log("Error Adding: ", e);
};
};
html5rocks.indexedDB.addData = function(d) {
var db = html5rocks.indexedDB.db;
var trans = db.transaction(["todo"], IDBTransaction.READ_WRITE);
var store = trans.objectStore("todo");
var data={
"text":d,
"number":1
};
var request = store.put(data);
request.onsuccess = function(e) {
etime=new Date;
var t=document.getElementById('result').innerHTML;
document.getElementById('result').innerHTML=t+((etime.valueOf()-stime.valueOf()))+"<br/>";
};
request.onerror = function(e) {
console.log("Error Adding: ", e);
};
};
function addTest() {
for(i=1;i<=times*MAX;i++)
html5rocks.indexedDB.addTodo(' ',i);
}
function getTest() {
for(i=1;i<=times*MAX;i++)
html5rocks.indexedDB.getTodo(Math.round(Math.random()*(MAX*times-1)+1));
}
function delTest() {
for(i=1;i<=times*MAX;i++)
html5rocks.indexedDB.deleteTodo(i);
}
function dataTest() {
data=' ';
for(i=1;i<=21;i++)
data=data+data;
stime=new Date
html5rocks.indexedDB.addData(data);
}
function init() {
stime=new Date;
allTime=0;
html5rocks.indexedDB.open();
}

The spec is not finalized. This is currently shipped as the property mozIndexedDB in Gecko and webkitIndexedDB in Chrome until the standard is finalized. So you have to write for moz also. Now this code is only for webkit.
https://developer.mozilla.org/en/IndexedDB

setVersion() is Deprecated
The new way is to define the version in the IDBDatabase.open() method
firefox from version 10.0 implements open() with the new specification in which an indexeddb database IDBDatabase version is set as the second parameter of the open() method
example
var v = "1.20";
var request = indexedDB.open("todos", v);
html5 indexeddb javascript

The problem here is not the one chosen as the correct answer.
The problem is that the IndexedDB examples on HTML5Rocks were written to the pre-January IndexeDB spec. The working group has since published a breaking change going from the setVersion API to the new onupgradedneeded style.
Here, Firefox is technically correct to fail. Star this issue if you want to see Chrome do the same.

Related

What in this IndexedDB code is causing the device storage to fill up to 1.6GB?

Our application that utilizes IndexedDB to track tallies of data over periods of time. In using the app for a number of months, it seems that there is some sort of storage leak causing a relatively small amount of data to require 1.6 GB of storage on the device?
The app has a feature to download its full data as a ZIP file, and that file is under 50KB. So there must be a long-term leak somewhere. What we're trying now is to download the data, clear storage, and re-import to track down where this leak might be.
However, looking at the following code that interacts with IndexedDB, it's not obvious to me where that leak may be:
// Initializing the Database
if(!dbengine){
dbengine = this;
var request = window.indexedDB.open("DB", 1);
request.onerror = function(event) {
throw new Error("Error opening indexedDb")
};
// This creates the schema the first time around
request.onupgradeneeded = (event) => {
event.target.result.createObjectStore("db", { keyPath: 'id' });
};
request.onsuccess = (event) => {
dbengine.db = event.target.result;
var dbReadyEvent = new CustomEvent('db-ready', {});
document.dispatchEvent(dbReadyEvent);
dbengine.db.onerror = function(event) {
throw new Error("Database error: " + event.target.errorCode);
};
dbengine.db.onabort = function(event) {
throw new Error("Database aborted: " + event.target.error);
// This is the error that is being called for us - QuotaExceededError
};
};
}// if !dbengine
var initialTransaction = dbengine.db.transaction(['db'], 'readwrite');
initialTransaction.onabort = (event) => {
var error = event.target.error; // DOMError
alert(error.name);
} // onabort
initialTransaction.objectStore('db').openCursor().onsuccess = (event) => {
var cursor = event.target.result;
if(cursor) {
this.encryption.loadIdentity((success) => {
if(success) {
this.loadDb(() => {
document.dispatchEvent(new CustomEvent('api-ready', {}));
setInterval(this.syncDb.bind(this), this.syncInterval);
this.syncDb();
});
} else {
document.dispatchEvent(new CustomEvent('api-fail', {}));
}
});
} else {
initialTransaction.objectStore('db').add({
/* [...] */
});
document.dispatchEvent(new CustomEvent('db-created', {}));
} // if/else cursor
}; // objectStore
loadDb(callback) {
this._tallies = [];
this._collections = [];
var dbStore = dbengine.db.transaction(['db'], 'readwrite').objectStore('db');
dbStore.get(1).onsuccess = (event) => {
var result = event.target.result;
var tallies = result.tallies;
if(result.tallies.length !== 0) {
tallies = this._encryption.decrypt(result.tallies);
}
tallies.forEach(((t) => {
/* [...] */
}));
var collections = result.collections;
if(collections.length !== 0) {
collections = this._encryption.decrypt(result.collections);
}
collections.forEach(((c) => {
/* [...] */
}));
callback()
}
} // loadDb
logout() {
var dbStore = dbengine.db.transaction(['db'], 'readwrite').objectStore('db');
var request = dbStore.get(1);
request.onsuccess = function(e) {
delete e.currentTarget.result.identity;
dbStore.put(e.currentTarget.result);
redirectTo('welcome');
};
} // logout
syncDb() {
var tallyDataString = JSON.stringify(this.tallies);
var collectionDataString = JSON.stringify(this.collections);
var encryptedTallyDataString = this._encryption.encrypt(tallyDataString);
var encryptedCollectionDataString = this._encryption.encrypt(collectionDataString);
var dbStore = dbengine.db.transaction(['db'], 'readwrite').objectStore('db');
dbStore.get(1).onsuccess = (event) => {
var data = event.target.result;
if(data.tallies !== encryptedTallyDataString) {
data.tallies = encryptedTallyDataString;
dbStore.put(data);
}
if(data.collections !== encryptedCollectionDataString) {
data.collections = encryptedCollectionDataString;
dbStore.put(data);
}
var syncEvent = new CustomEvent('sync', {});
document.dispatchEvent(syncEvent);
} // onsuccess
dbStore.get(1).onabort = (event) => {
var error = event.target.error; // DOMError
// if (error.name == 'QuotaExceededError') {
// alert('Sync failed. Quota exceeded.')
// }
alert(error.name);
} // onabort
} // syncDb
loadIdentity(callback) {
var dbStore = dbengine.db.transaction(['db'], 'readwrite').objectStore('db');
dbStore.get(1).onsuccess = (event) => {
var result = event.target.result;
if(!result.identity || !result.identity.publicKey || !result.identity.privateKey) {
if(window.location.pathname !== "/app/welcome/") {
redirectTo('welcome');
}
}
this.identity = {
/* [...] */
};
var solid = false;
if(result.identity.publicKey && result.identity.privateKey) {
solid = true;
}
callback(solid);
};
} // loadIdentity
setIdentity(identity, username) {
var dbStore = dbengine.db.transaction(['db'], 'readwrite').objectStore('db');
var getRequest = dbStore.get(1);
getRequest.onsuccess = (event) => {
var result = event.target.result;
result.identity = {
/* [...] */
};
this.identity = identity;
var request = dbStore.put(result);
} // getRequest
} // setIdentity

html5/javascript.Failed to insert data while using indexedDB

Here is my code:
function set() {
var db;
var request;
var store;
var username = document.getElementById("username");
var password = document.getElementById("password");
var newuser = {
"username": username.value,
"password": password.value,
};
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
if ('webkitIndexedDB' in window) {
window.IDBTransaction = window.webkitIDBTransaction;
window.IDBKeyRange = window.webkitIDBKeyRange;
}
request = indexedDB.open("userdb", 1);
request.onerror = function(event) {};
request.onsuccess = function(event) {
db = event.target.result;
var transaction = db.transaction(["user"], "readwrite");
var objectStore = transaction.objectStore("user");
var addrequest = objectStore.add(newuser);
addrequest.onsuccess = function(event) {
alert("Success");
};
};
request.onupgradeneeded = function(event) {
db = event.target.result;
store = db.createObjectStore("user", {
keyPath: "username",
autoIncrement: false
});
};
}
After insert data,I use function get() to get data from indexedDB,but I failed to get that,What is the problem?Thank you!
function get() {
var db;
var request;
var store;
var username = document.getElementById("username");
var password = document.getElementById("password");
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB;
if ('webkitIndexedDB' in window) {
window.IDBTransaction = window.webkitIDBTransaction;
window.IDBKeyRange = window.webkitIDBKeyRange;
}
request = indexedDB.open("userdb", 1);
request.onerror = function(event) {
alert("Error Code:" + event.target.errorCode);
};
request.onsuccess = function(event) {
db = event.target.result;
var transaction = db.transaction("user", IDBTransaction.READ_WRITE);
var objectStore = transaction.objectStore("user");
objectStore.get(username.value);
if (password.value == event.target.result.password)
alert("Success“);
else
alert("Failed");
};
request.onupgradeneeded = function(event) {
alert("Update Success");
};
}
The function works after click another button to get data.But I failed to get data.I have added newuser(username:1 password:1) to the table user.But I cann't get the data.Why?Thank you for your answer.
Your code actually does work, though only once for a given username: after the record has been successfully added to the DB, it persists (which is the point in using IndexedDB in the first place), so subsequent attempts to execute set() result in a fail event on the addrequest object. This never shows up in your code as you haven't defined an onerror handler for said request.
Replace ...
addrequest.onsuccess = function(event) {
alert("Success");
};
... with ...
addrequest.onerror = function(event) {
if (console && console.log) { console.log("add op: Fail\n"); }
};
addrequest.onsuccess = function(event) {
if (console && console.log) { console.log("add op: Success\n"); }
};
... and see it happen (I've taken the liberty to replace alert with console output. Open the console by pressing ctrl-j in the browser window).
Tested on Chrome 50.
Update
Change the get function as follows:
Instead of ...
objectStore.get(username.value);
if (password.value == event.target.result.password)
alert("Success“);
else
alert("Failed");
... write ...
var getrequest = objectStore.get(newuser.username);
getrequest.onerror = function(event) {
if (console && console.log) { console.log("get op: Fail\n"); }
};
getrequest.onsuccess = function(event) {
if (console && console.log) { console.log("get op: Success\n"); }
};
... and it will work.

jQuery.Deferred() is not working properly

I'm trying to implement indexedDB. For that I need to use $.Deferred to get the status of database creation. But the problem is, Differed is not working as expected.
Here is the fiddle, you can find the process in the console.
And here is the code:
$(function($) {
var table = 'price';
$.when(dbConnection('cw', table)).done(function(db) {
console.log(db);
var data = [];
dbInsert(db, data, table);
});
function dbConnection(dbname, table) {
var dfd = $.Deferred();
var request = indexedDB.open(dbname);
request.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
var db = request.result;
var store = db.createObjectStore(table, {
keyPath: "id"
});
var styleCode = store.createIndex("style_code", "style_code");
var colorCode = store.createIndex("color_code", "color_code");
var size = store.createIndex("size", "size");
var price1 = store.createIndex("price1", "price1");
};
request.onsuccess = function() {
db = request.result;
console.log(request.result);
dfd.resolve(db);
return dfd.promise();
};
request.onerror = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
return dfd.promise();
};
request.onabort = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
return dfd.promise();
};
}
function dbInsert(db, data, table) {
var tx = db.transaction(table, "readwrite");
var store = tx.objectStore(table);
$.each(data, function(i, rows) {
var style = rows['style-code'],
color = rows['color-code'],
size = rows['size'],
price = rows['price1'];
store.put({
id: i,
style_code: style,
color_code: color,
size: size,
price1: price
});
});
tx.oncomplete = function() {
console.log('Data inserted successfully.');
};
}
})(jQuery);
Whether I'm doing anything wrong? or am I missing anything in this code?. Can anyone tell me what is wrong in this code.
You are expecting dbConnection to return a promise, but do not return anything from that function. Return the promise immediately (last line below) and not inside all the callbacks:
function dbConnection(dbname, table) {
var dfd = $.Deferred();
var request = indexedDB.open(dbname);
request.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
var db = request.result;
var store = db.createObjectStore(table, {
keyPath: "id"
});
var styleCode = store.createIndex("style_code", "style_code");
var colorCode = store.createIndex("color_code", "color_code");
var size = store.createIndex("size", "size");
var price1 = store.createIndex("price1", "price1");
};
request.onsuccess = function() {
db = request.result;
console.log(request.result);
dfd.resolve(db);
};
request.onerror = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
};
request.onabort = function() {
report(request.error);
console.log(request.error);
dfd.resolve(null);
};
return dfd.promise();
}
Updated JSFiddle: http://jsfiddle.net/TrueBlueAussie/9kjcm49b/2/
Your various callbacks simply resolve or reject the deferred. The readonly promise returned earlier then triggers the next operation.
Note: You should probably use reject for the two error cases (unless you actually want to proceed with a null db value). e.g.
request.onerror = function() {
report(request.error);
console.log(request.error);
dfd.reject("Error occurred");
};
request.onabort = function() {
report(request.error);
console.log(request.error);
dfd.reject("Operation aborted");
};
and use like this:
$.when(dbConnection('cw', table)).done(function(db) {
console.log(db);
var data = [];
dbInsert(db, data, table);
}).fail(function(message){
alert(message);
});

When using IndexedDB, how can I delete multiple records using an index that is not the key?

I have code to create an indexedDB here:
function create_db() {
var indexedDB = window.indexedDB || window.webkitIndexedDB || window.msIndexedDB;
var request = indexedDB.open(“photos”, 2);
request.onupgradeneeded = function(event) {
var db = event.target.result;
// Create photo db
var photo_store = db.createObjectStore("photos", {keyPath: "photo_id"});
var photo_id_index = photo_store.createIndex("by_photo_id", "photo_id", {unique: true});
var dest_id_index = photo_store.createIndex("by_destination_id", "destination_id");
console.log(“store created”);
};
request.onsuccess = function(event) {
console.log(“store opened”);
};
request.onerror = function(event) {
console.log("error: " + event);
};
}
My code to remove entries:
function remove_photos = function (destination_id, db) {
var transaction = db.transaction("photos", "readwrite");
var store = transaction.objectStore("photos");
var index = store.index("by_destination_id");
var request = index.openCursor(IDBKeyRange.only(destination_id));
request.onsuccess = function() {
var cursor = request.result;
if (cursor) {
cursor.delete();
cursor.continue();
}
};
}
How can I delete records using the by_destination_id index so that I can delete all records with a given destination_id, which is an integer?
Thanks for any help.
I found the solution to my issue, the IDBKeyRange.only function doesn't like integers it needs to be a string, so substituting this line in:
var request = index.openCursor(IDBKeyRange.only(destination_id.toString()));
Make the code work.

indexedDB blocked DOM exception after an insert loop

I'm doing few tests with indexedDB and I keep getting chrome blocked during an insert test.
Basically what I'm doing is a simple loop, 100000 times, inserting simple string in the db. It completes correctly but at the end the indexedDB from the inspector is not visible. Not even refreshing the page. If trying to reload the page and reopening the database I get a DOM exception. If closing chrome it hangs and I have to kill it.
Below the code:
var testIndexedDB = {
db : null,
request: null,
openDB : function(){
var self = this;
var request = this.request = indexedDB.open("web", 1);
request.onupgradeneeded = function() {
// The database did not previously exist, so create object stores and indexes.
request.close();
var db = request.result;
var stories = db.createObjectStore("stories", {keyPath: "id"});
};
request.onsuccess = function() {
self.db = request.result;
};
request.onerror = function(e) {
console.log(e);
};
request.onblocked = function(e){
console.log('blocked')
}
},
addItem: function(store, loid, text){
var db = this.db;
var trans = db.transaction(store, "readwrite");
var store = trans.objectStore(store);
var request = store.put({
"id": loid,
"text" : text
});
request.onsuccess = function(e) {
// Re-render all the todo's
};
request.onerror = function(e) {
console.log(e.value);
};
},
getItem: function(store, loid){
var db = this.db;
var trans = db.transaction(store);
var store = trans.objectStore(store);
var request = store.get(loid);
request.onsuccess = function(e) {
console.log(request.result) // Refresh the screen
};
request.onerror = function(e) {
console.log(e);
};
},
removeItem: function(store, loid){
var db = this.db;
var trans = db.transaction(store, "readwrite");
var store = trans.objectStore(store);
var request = store.delete(loid);
request.onsuccess = function(e) {
console.log('el deleted'); // Refresh the screen
};
request.onerror = function(e) {
console.log(e);
};
},
testSize: function(){
var i = 0,
t;
while(i<100000){
t = new Date().getTime();
this.addItem('stories', i, t)
i++;
}
console.log('items added')
}
};
testIndexedDB.openDB();
simply run testIndexedDB.testSize() to notice the issue.
How can I properly test continuos insertion and why is this happening?
thanks
The main problem that you have in the batch insert is the fact that you are opening 100000 transaction and that blocks the database. I've made some optimizations to your code for batch inserting and now the insert time is less than 5s.
Instead of opening separate transaction for every item, I group the items in arrays of 1000 items and then open a transaction for that batch. Now the number of transaction is reduced to 100. Here are all the changes:
First I created a batch insert Function:
addItems: function(store, items){
var db = this.db;
var trans = db.transaction(store, "readwrite"); //uses only one transaction per batch of 1000
trans.oncomplete = function(e){
console.log('batch inserted');
};
var store = trans.objectStore(store);
for(var i=0; i<items.length; i++){
var request = store.put(items[i]);
request.onerror = function(e) {
console.log(e.value);
};
}
},
And then I've changed the inserting function to send the data in batches:
testSize: function(){
var i = 0,
t;
tempList = [];
while(i<100000){
t = new Date().getTime();
tempList.push({
"id": i,
"text" : t
})
if(i> 0 && i% 1000 == 0){ //items are grouped into a array of 1000 items
this.addItems('stories', tempList.slice());
tempList = [];
}
i++;
}
console.log('items added');
}

Categories

Resources