I'm developing a app which uses IndexedDB extensively. What I'm trying to do is save data of employees table and company table. Each employee belongs to a company and in employee object I've Company's ID and the objects of both of the entity will look like this.
Company's object:
{"id":1,"name":"ABC"}
Employee's object:
{"id":100,"name":"E1","company_id":1}
I'm saving company's details using auto-incremented key (called it appid), so my final object of company's look like this:
{"id":1,"name":"ABC","appid":1}
Where the appid will get auto-incremented as I insert records of company one by one. Now while inserting employee's object I want to find the localid(appid) of the company and save it in employee's object to make employee's object look like:
{"id":100,"name":"E1","company_id":1,"company_app_id":1}
I'm able to get the localid of the company by calling a method while saving employee's details, like:
var transaction = db.transaction(['employees'], 'readwrite');
var objStore = transaction.objectStore('employees');
var company_id=employeeobject.company_id;
companyDB.getCompanyById(company_id,function(companyObject){
transaction = db.transaction(['employees'], 'readwrite');
objStore = transaction.objectStore('employees');
// If I comment above two lines it throws me exception.
var request=objStore.put(employeeobject);
request.onsuccess = function (e) {
// using it for next insertion.
};
});
Problem with the above code is every time when I want to insert employee's data in table I need to reopen the trascation in callback function because if I don't open the transaction again it throws TransactionInactiveError.
I've searched for specific error on SO and found that Transaction get inactive as soon as it's no more used in current scope.
Above code work perfectly fine when I've couple of employee's objects.
But When I'm trying to execute the same code with ~1K of data it takes
(normal execution time x ~10).
By normal execution time I mean without fetching company's localid and saving employee's details directly.
So my question is, what is the best way I can insert the employee's data including company's localid with least execution time? Or Am I doing something wrong ?
It depends on how you're implementing companyDB.getCompanyById().
As presented, it's an asynchronous black box (maybe it's doing a network request?) And as you've discovered, Indexed DB transactions are only active (1) directly after creation and (2) in callbacks from requests made in that transaction, until "control returns to the event loop". Without further details, your best bet would be to batch the work - do the getCompanyById() lookups for N employees, then write those N records.
But from the description at the top "...data of employees table and company table..." maybe this is all within a single Indexed DB database, in which case just use a single transaction for everything:
var tx = db.transaction(['companies', 'employees'], 'readwrite');
employee_records.forEach(function(record) {
var company_id = record.company_id;
var req = tx.objectStore('companies').get(company_id);
req.onsuccess = function() {
var company = req.result;
record.company_app_id = company.app_id;
tx.objectStore('employees').put(record);
};
});
(I'm mangling your actual data/logic here, this is just to illustrate)
Hmm, maybe something like this helps?
function addCompany(db, company, callback) {
var tx = db.transaction('companies', 'readwrite');
var store = tx.objectStore('companies');
var request = store.add(company);
request.onsuccess = callback;
}
function addEmployee(db, employee, callback) {
var tx = db.transaction('employees', 'readwrite');
var store = tx.objectStore('employees');
var request = store.add(employee);
request.onsuccess = callback;
}
function addCompanyThenEmployee(db, company, employee, callback) {
addCompany(db, company, onAddCompany);
function onAddCompany(event) {
var newAppId = event.target.result;
employee.company_app_id = newAppId;
addEmployee(db, employee, callback);
}
}
var company = {'id': 1, 'name': 'xyz'};
var employee = {'id': 1, 'name': 'xyz'};
var request = indexedDB.open(...);
request.onsuccess = function(event) {
var db = event.target.result;
addCompanyThenEmployee(db, company, employee, onAddCompanyThenEmployee.bind(null, company, employee));
};
function onAddCompanyThenEmployee(company, employee, event) {
console.log('Added company', company, 'and employee', employee);
};
Related
I'm working on restaurant website (client-side project) and facing this problem, I want to make an admin page will show me all the orders placed by the customers and the way I choose that to save the order details in local storage then save it in this indexedDB then display the (the orders) at the admin page so I made this code and it work all good I guess to save the order and all customer details
document.getElementById('submittheorder').onclick = function() {
let i = 0;
const versionDB = 1;
let indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB || window.shimIndexedDB;
var open = indexedDB.open("CustomersOrders", versionDB);
open.onupgradeneeded = function() {
let db = open.result;
let store = db.createObjectStore("OrdersTable", {
keyPath: "id"
});
let index = store.createIndex("CIndex", ["FullName", "Order", "House", "Road", "Block"]);
};
open.onsuccess = function() {
let db = open.result;
let tx = db.transaction("OrdersTable", "readwrite");
let store = tx.objectStore("OrdersTable");
let index = store.index("CIndex");
store.put({
FullName: (sessionStorage.getItem("Cfullname")),
Order: (sessionStorage.getItem("order")),
House: (sessionStorage.getItem("CHouse")),
Road: (sessionStorage.getItem("CRoad")),
Block: (sessionStorage.getItem("CBlock"))
});
tx.oncomplete = function() {
db.close();
location.href = "Thanks.html";
};
}
}
Now the problem is I want to retrieve all the orders and the details for each object to the admin page
the second problem is that i want to check if the database already exist then insert new object not make a new database and save only one object, in a nutshell i want only one database to make and next times save the orders at that database.
Thank you :)
You can place this logic in the function that handles an upgrade event. There are essentially two ways. You can check if object stores and indices exist, using for example db.objectStoreNames.contains(), or you can compare versions by accessing the version properties from the database object or the event object.
For example, you would want to only create an object store if it did not already exist. If it does not already exists, then you know this is when your database is created.
I have the code below:
var FBref = new Firebase('https://employees.firebaseio.com/');
var peopleObject = $firebaseArray(FBref);
var person = {
"name":"John",
"age":"20",
"phone":"123-123-123"
};
peopleObject.$add(person);
I use the code above to add new records of employees successfully.
I need to update the John's phone number to 111-111-111. But the code below doesn't work.
var person = {
"name":"John",
"age":"20",
"phone":"111-111-111" //New phone number
};
peopleObject.$save(person);
Someone advice
Currently you are trying to save whole object, which wouldn't work.
Retrieve old record first by getting record from db first & then update that record. Thereafter save that object using $save method.
//if update action is happening is similar session you can get unique by below code
var someRecordKey;
peopleObject.$add(person).then(function(ref) {
//get record id of new person
someRecordKey = ref.key();
});
// get record by unique key
var person = peopleObject.$getRecord(someRecordKey);
// change a message and save it
item.phone = "111-111-111";
peopleObject.$save(person).then(function() {
// data has been saved to our database
});
Here is a question for parse.com gurus.
I am using Parse Javascript API and trying to execute a query over 2 Pointers and cannot get it to work.
So i have following classes: Posts, Users, Groups. Posts has Pointer to Users. Users has a Pointer to Groups.
I need to get all POSTS, where a USER belongs to GROUP, which name starts with "Admin". Here is my code that doesn't work:
var Posts = Parse.Object.extend("Posts");
var Users = Parse.Object.extend("Users");
var Groups = Parse.Object.extend("Groups");
var query = new Parse.Query(Posts);
var innerQueryUsers = new Parse.Query(Users);
var innerQueryGroups = new Parse.Query(Groups);
innerQueryGroups.startsWith("name", "Admin");
innerQueryUsers.matchesQuery("group", innerQueryGroups);
query.matchesQuery("user", innerQueryUsers);
query.find({
success: function(data) {
},
error: function(error){
// here i get error: {code: 102, message: "bad type for $inQuery"}
}
});
Anybody have an idea how to do it right?
Edit - This can be done in one (untested) query by combining a group query and a post query with the same user query...
function postsInGroup(name) {
var groupQuery = new Parse.Query("Group");
groupQuery.equalTo("name", name);
var userQuery = new Parse.Query(Parse.User);
userQuery.matchesQuery("group", groupQuery);
var postQuery = new Parse.Query("Post");
postQuery.matchesQuery("user", userQuery);
return postQuery.find();
}
Call it like this...
postsInGroup("Admin").then(function(posts) {
console.log(JSON.stringify(posts));
}, function(error) {
console.log(JSON.stringify(error));
});
Its not clear what savings there is between this approach and first querying the group. It's likely that parse.com runs the inner queries much as you would. The difference in readability is a matter of taste.
I'm constructing a Parse.com query inside a Parse query on the browser in a Parse/backbone app.
I have a two stage query (and want the intermediate results) so I construct the second query in a when clause from the first query's fetch:
// changeJob is a backbone event handler triggered from global app state
changeJob: function() {
var taskQuery = new Parse.Query(Task);
taskQuery.equalTo("publicJob", theAppState.get("publicJob"));
taskQuery.equalTo("status", "accepted");
taskQuery.limit(20);
this.tasks.query = taskQuery;
var self = this;
this.tasks.fetch().then(function(foundTasks){
console.log("got " + foundTasks.length + " tasks");
var responsesQuery = new Parse.Query(StepResponse);
responsesQuery.containedIn("task", foundTasks); /// Stack blows here
responsesQuery.include("task");
responsesQuery.include("task.taskLocation");
responsesQuery.limit(1000);
self.responses.query = responsesQuery;
console.log("fetch the step responses");
self.responses.fetch();
});
My collections are model objects are declared with global scope:
var StepResponse = Parse.Collection.extend("StepResponse", {
});
// StepResponse Collection
var StepResponseCollection = Parse.Collection.extend({
model: StepResponse,
});
var Task = Parse.Object.extend("Task", {
});
// Task Collection
var TaskCollection = Parse.Collection.extend({
model: Task,
});
When I try to restrict the inner query based on using the array from the first query, I get a Maximum Stack size exceeded error.
I've seen a few people discuss this on SO but I don't think that they are duplicates - I don't have any bi directional links between these two classes and the stack blows when I'm setting up the query, before I execute it of try to save any objects.
I have tried to get some information from W3C regarding the update of an objectStore item in a indexedDB database, but with not so much susccess.
I found here a way to do it, but it doesn't really work for me.
My implementation is something like this
DBM.activitati.edit = function(id, obj, callback){
var transaction = DBM.db.transaction(["activitati"], IDBTransaction.READ_WRITE);
var objectStore = transaction.objectStore("activitati");
var keyRange = IDBKeyRange.only(id);
objCursor = objectStore.openCursor(keyRange);
objCursor.onsuccess = function(e){
var cursor = e.target.result;
console.log(obj);
var request = cursor.update(obj);
request.onsuccess = function(){
callback();
}
request.onerror = function(e){
conosole.log("DBM.activitati.edit -> error " + e);
}
}
objCursor.onerror = function(e){
conosole.log("DBM.activitati.edit -> error " + e);
}
}
I have all DBM.activitati.(add | remove | getAll | getById | getByIndex) methods working, but I can not resolve this.
If you know how I can manage it, please, do tell!
Thank you!
Check out this jsfiddle for some examples on how to update IDB records. I worked on that with another StackOverflower -- it's a pretty decent standalone example of IndexedDB that uses indexes and does updates.
The method you seem to be looking for is put, which will either insert or update a record if there are unique indexes. In that example fiddle, it's used like this:
phodaDB.indexedDB.addUser = function(userObject){
//console.log('adding entry: '+entryTxt);
var db = phodaDB.indexedDB.db;
var trans = db.transaction(["userData"],IDBTransaction.READ_WRITE);
var store = trans.objectStore("userData");
var request = store.put(userObject);
request.onsuccess = function(e){
phodaDB.indexedDB.getAllEntries();
};
request.onerror = function(e){
console.log('Error adding: '+e);
};
};
For what it's worth, you've got some possible syntax errors, misspelling "console" in console.log as "conosole".
A bit late for an answer, but possible it helps others. I still stumbled -as i guess- over the same problem, but it's very simple:
If you want to INSERT or UPDATE records you use objectStore.put(object) (help)
If you only want to INSERT records you use objectStore.add(object) (help)
So if you use add(object), and a record key still exists in DB, it will not overwritten and fires error 0 "ConstraintError: Key already exists in the object store".
If you use put(object), it will be overwritten.
this is case of update infos of an user object
var transaction = db.transaction(["tab_user"], "readwrite");
var store = transaction.objectStore("tab_user");
var req = store.openCursor();
req.onerror = function(event) {
console.log("case if have an error");
};
req.onsuccess = function(event) {
var cursor = event.target.result;
if(cursor){
if(cursor.value.idUser == users.idUser){//we find by id an user we want to update
var user = {};
user.idUser = users.idUser ;
user.nom = users.nom ;
var res = cursor.update(user);
res.onsuccess = function(e){
console.log("update success!!");
}
res.onerror = function(e){
console.log("update failed!!");
}
}
cursor.continue();
}
else{
console.log("fin mise a jour");
}
}
I'm a couple of years late, but thought it'd be nice to add my two cents in.
First, check out BakedGoods if you don't want to deal with the complex IndexedDB API.
It's a library which establishes a uniform interface that can be used to conduct storage operations in all native, and some non-native client storage facilities. It also maintains the flexibility and options afforded to the user by each. Oh, and it's maintained by yours truly :) .
With it, placing one or more data items in an object store can be as simple as:
bakedGoods.set({
data: [{key: "key1", value: "value1"}, {key: "key2", value: "value2"}),
storageTypes: ["indexedDB"],
complete: function(byStorageTypeResultDataObj, byStorageTypeErrorObj){}
});
Now to answer the actual question...
Lets begin by aggregating the valuable information spread across the existing answers:
IDBObjectStore.put() adds a new record to the store, or updates an existing one
IDBObjectStore.add() adds a new record to the store
IDBCursor.update() updates the record at the current position of the cursor
As one can see, OP is using an appropriate method to update a record. There are, however, several things in his/her code, unrelated to the method, that are incorrect (with respect to the API today at least). I've identified and corrected them below:
var cursorRequest = objectStore.openCursor(keyRange); //Correctly define result as request
cursorRequest.onsuccess = function(e){ //Correctly set onsuccess for request
var objCursor = cursorRequest.result; //Get cursor from request
var obj = objCursor.value; //Get value from existing cursor ref
console.log(obj);
var request = objCursor.update(obj);
request.onsuccess = function(){
callback();
}
request.onerror = function(e){
console.log("DBM.activitati.edit -> error " + e); //Use "console" to log :)
}
}
cursorRequest.onerror = function(e){ //Correctly set onerror for request
console.log("DBM.activitati.edit -> error " + e); //Use "console" to log :)
}