How to loop through indexedDB tables synchronously? - html

I want to write a function in JS where I will loop through a tables in my indexed DB and get the maximum value of last modified of table and return that
function readData(){
var trans = '';
trans = idb.transaction(["tableName"],'readonly'); // Create the transaction
var request = trans.objectStore("tableName").openCursor();
request.onsuccess = function(e) {
var cursor = request.result || e.result;
if(cursor) {
// logic to and find maximum
} else {
return // max last modified
}
cursor.continue();
}
}
IMP--Since onsuccess method is asynchronous how can i make it synchronous? so that my method readData() will return only when max last modified record is found successfully. I can call this method(readData()) synchronously to get last modified record of 2-3 tables if I want.

The sync API is only available in a webworker. So this would be the first requirement. (As far as I know only IE10 supports this at the moment)
An other shot you can give is working with JS 1.7 and use the yield keyword. For more information about it look here
I would sugest to work with a callbakck method that you call when you reached the latest value.
function readData(callback){
var trans = '';
trans = idb.transaction(["tableName"],'readonly'); //Create the transaction
var request = trans.objectStore("tableName").openCursor();
var maxKey;
request.onsuccess = function(e) {
var cursor = request.result || e.result;
if(cursor.value){
//logic to and find maximum
maxKey = cursor.primaryKey
cursor.continue();
}
}
trans.oncomplete = function(e) {
callback(maxKey);
}
}

IndexedDB API in top frame is async. async cannot be synchronous. But you can read all tables in single transaction.

Related

getBulkProperties() Hangs and Errors Out

Our application needs to pull a set of properties from all objects in the model. Our application will concatenate properties from leaf nodes with properties from the parent nodes.
We are calling the getBulkProperties() method with around 20K nodes and around 5 properties. This runs for quite some time and then we receive server errors and the callbacks are never invoked.
Is there a limit we should use? Should we split these calls with a max number X of nodes?
Any help would be appreciated as this is causing our application to hang.
Thanks!
I don't think there is a limit, but you may consider listing properties for a specific group at a time, or just leaf nodes.
This blog post shows how to optimize the search performance, and the code below (from this post) how to integrate with .getBulkProperties:
viewer.search('Steel',
function(dbIds){
viewer.model.getBulkProperties(dbIds, ['Mass'],
function(elements){
var totalMass = 0;
for(var i=0; i<elements.length; i++){
totalMass += elements[i].properties[0].displayValue;
}
console.log(totalMass);
})
}, null, ['Material'])
You may also consider enurating only leaf on the model, as shown at this post and below:
function getAllLeafComponents(viewer, callback) {
var cbCount = 0; // count pending callbacks
var components = []; // store the results
var tree; // the instance tree
function getLeafComponentsRec(parent) {
cbCount++;
if (tree.getChildCount(parent) != 0) {
tree.enumNodeChildren(parent, function (children) {
getLeafComponentsRec(children);
}, false);
} else {
components.push(parent);
}
if (--cbCount == 0) callback(components);
}
viewer.getObjectTree(function (objectTree) {
tree = objectTree;
var allLeafComponents = getLeafComponentsRec(tree.getRootId());
});
}

Promisify a recursive function in node.js

I'm using bluebird for the control flow in my application, I'm trying to implement promisify to extend my recursive function into a promise, but it seems like the "then" method never got executed
I'm doing a mapping from one JSON object to another, the find function looks recursively into the JSON properties and returns the property based on an specific condition.
var promise = require("bluebird");
var mapToModel = function(res){
// res is a json structure
var processes = res.definitions.process;
var diagrams = res.definitions.BPMNDiagram;
var documents = [];
for(var prop in processes) {
if(processes.hasOwnProperty(prop)){
var propertyNames = Object.getOwnPropertyNames(processes[prop]);
for(var property in processes[prop]){
var mapping ={};
if(property==="$"){
//do something with the process
}else{
//shapes
mapping.hash = hash.hashCode(new Date().toString());
mapping.type = property;
mapping.value = processes[prop][property];
var bpmnItem = findPromise(processes[prop][property], function(x) {return x.$.id;}).then(function(value){
//I'm not reaching this point
var bpmnId = value.$.id;
console.log(value);
if(bpmnId!=undefined){
console.log("return:"+ bpmnId);
}
});
documents.push(mapping);
}
}
}
return documents;
}
}
var findPromise = promise.promisify(find);
function find(items,f) {
for(var key in items) {
var elem = items[key];
if (f(elem)) { return elem;}
if(typeof elem === "object") {
find(elem,f); // call recursively
}
}
}
The Bluebird promisify method works on the accepted callback convention for NodeJS - nodebacks.
Nodebacks are in the specific format of someOp(function(err,result){ that is - the first argument is always an error.
In fact, your find method is not even asynchronous, so there is no reason to promisify it to begin with. You can simply call it as it is.
Generally, you should not promisify synchronous functions, you just call them normally. In fact, you don't seem to have any asynchronous operation in your code - so you should not be using promises at all in it.
You can simply do:
mapping.value = processes[prop][property];
var value = find(processes[prop][property], function(x) {return x.$.id;});
var bpmnId = value.$.id;
console.log(value);
if(bpmnId!=undefined){
console.log("return:"+ bpmnId);
}
Remember, Promises are an abstraction over an eventual result. You keep doing everything synchronous just like you did before.

Read records from firebase based on a previously saved value

I took an angularjs + firebase example and modified it for an app where I can register some kids for a small cross-country race.
I'm able to register kids (participants), races, locations, clubs etc. using a basic structure:
FIREBASE_URL/races
FIREBASE_URL/clubs
and so forth. When the active race is selected, I save the raceId and race json-object and can add participants to the active race.
Example:
FIREBASE_URL/active_race/-JI6H9VQewd444na_CQY
FIREBASE_URL/active_race/json-object
What I'd like to do is to get all the participants, if any, based on raceId:
FIREBASE_URL/races/-JI6H9VQewd444na_CQY/participants
I tried the following
'use strict';
app.factory('Race', function ($firebase, FIREBASE_URL, User) {
var ref = new Firebase(FIREBASE_URL + 'races');
var races = $firebase(ref);
var Race = {
all: races,
getParticipantsInRace: function () {
var fb = new Firebase(FIREBASE_URL);
fb.child('active_race/raceId').once('value', function (activeSnap) {
races.$child('/' + activeSnap.val() + '/participants');
});
}
};
return Race;
But I believe I'm doing it wrong. I tried to prepend return before races.$child and fb.child but it did not solve my problem.
I tried to hardcode the following json-array and this is shown on the webpage:
return [{name: 'Claus', born: '1967'}, {name: 'John', born: '1968'}];
How do I get all the participants into $scope.participantsInRace?
I believe I have a solution, but I'm not sure if it's wise to do it this way. But it may be that simple. Prepending $rootScope.participantsInRace = to put it into rootScope:
$rootScope.participantsInRace = races.$child('/' + activeSnap.val() + '/participants');
The code is already synchronizing all data in all races when it declares $firebase(URL+'races');. Additionally, you never assigned your races.$child(...) to anything, so it's not possible to reference that data later.
app.factory('Race', function ($firebase, FIREBASE_URL, User) {
var ref = new Firebase(FIREBASE_URL + 'races');
var races = $firebase(ref);
var Race = {
all: races,
getParticipantsInRace: function (raceId) {
return races[raceId]? races[raceId].participants || {};
}
};
return Race;
});
Keep in mind that the race data won't be available until races.$on('loaded') is invoked (when the data returns from the server).
Thank you for the input. I know a bit more about angularjs and javascript now so I did some refactoring and cleanup. Hardcoding raceId works:
getParticipantsInRace: function () {
return races.$child('-JIecmbdDa4kUT2L51iS').$child('participants');
}
When I wrap it in a call to Firebase I can't seem to return the desired data, probably due to my somewhat limited knowledge of javascript on how to return data. Example:
getParticipantsInRace: function () {
ref.child('activeRace').child('raceId').once('value', function (activeSnap) {
return races.$child(activeSnap.val()).$child('participants');
});
}
My idea is to get the raceId and then return all participants. I tried to prepend return to ref.child() but still no data was returned. So not really an answer.
Regards
Claus
This works. I changed $rootScope.participantsInRace to $scope.participantsInRace and the following:
getParticipantsInRace: function () {
if (User.signedIn()) {
var t = [];
var user = User.getCurrent();
var fb = new Firebase(FIREBASE_URL + 'users');
fb.child(user.username).child('activeRace/raceId').once('value', function (userSnap) {
t = races.$child(userSnap.val()).$child('participants');
});
return t;
}
},

How to retrieve the data from database using Indexed DB

I have an existed database. I'm trying to retrieve the data from database using indexedDB but i'm unable to get the data from database.
var data = [];
// creating or opening the database
var db;
var request = window.indexedDB.open("database");
request.onerror = function(event) {
console.log("error: ");
};
request.onsuccess = function(event) {
db = request.result;
console.log("success: "+ db);
};
request.onupgradeneeded = function(event) {
var db = event.target.result;
var objectStore = db.createObjectStore("Subject", {keyPath: "id"});
for (var i in data) {
objectStore.add(data[i]);
}
}
function readAll() {
var objectStore = db.transaction("Subject").objectStore("Subject");
console.log(objectStore);
objectStore.openCursor().onsuccess = function(event) {
var cursor = event.target.result;
if (cursor) {
alert("Name for id " + cursor.key + " is " + cursor.value.Subject);
cursor.continue();
}
else {
alert("No more entries!");
}
};
}
Thanks in Advance.
You're pretty close.
var data = [];
I'll presume that you actually have some data somewhere, and that it indeed has an id attribute since you're specifying that as your index key e.g.
var data = [{id: 'foo' }, { id: 'bar' } ];
Now here:
var objectStore = db.createObjectStore("Subject", {keyPath: "id"});
for (var i in data) {
objectStore.add(data[i]);
}
(Careful with for..in and arrays)
I don't think you're actually adding any data here, which is one reason why you can't read it. To add data to an object store, try to first create a read/write transaction first and then get your reference to the object store and add your object.
var trans = db.transaction(["Subject"], "readwrite").objectStore("Subject");
Note the usage of an array as the first argument to transaction() and "readwrite" as the second param. (Some examples use the IDBTransaction.READ_WRITE constant but this doesn't seem to work with recent versions of Webkit.)
var objectStore = db.transaction("Subject").objectStore("Subject");
Try this instead:
var trans = db.transaction( [ "Subject" ] );
, objectStore = trans.objectStore( "Subject" );
objectStore.openCursor( IDBKeyRange.lowerBound(0) ).onsuccess = function(event) {..}
I did encountered the same error once. it occurs because at times the onSuccess is executed even before the result data is returned. So you should check if result data is empty.
To solve the issue try using oncomplete instead of onSuccess and also use Jquery indexedDB plugin. The plugin requires certin code changes but has more consistent implementation of indexedDB.
See http://nparashuram.com/jquery-indexeddb/

Query a JSON list of dict

[{"time":136803,"price":"1.4545","amount":"0.0885","ID":"112969"},
{"time":136804,"price":"2.5448","amount":"0.0568","ID":"5468489"},
{"time":136805,"price":"1.8948","amount":"0.0478","ID":"898489"}]
I have a large JSON file like the one above. It is a list of dictionaries. I want to choose a time and find the value assoaciated with that time. I will not know where in my list the time is located only the value for the time. Is there a way I can say, for time 136804, make x = to price? Or should I loop through each value? I also want to use this value (x) in a mathematical function.
My fist idea is to use brute force by going through each item and checking it for a matching time value in a loop.
Is this the best way?
Take a look at SpahQL http://danski.github.io/spahql/ which we use to query JSON in order to select values and subsequently change them as required.
I did something similar to this recently. JSON file I had to query had around 6000 lines and around 500 JSON objects. My query function given below loops through the each object to select the matching objects, but it can fetch any result within few milliseconds.
var data = '[{"time":136803,"price":"1.4545","amount":"0.0885","ID":"112969"},'+ '{"time":136804,"price":"2.5448","amount":"0.0568","ID":"5468489"},'+ '{"time":136805,"price":"1.8948","amount":"0.0478","ID":"898489"}]';
var data = JSON.parse(data);
var query = function(data, select, andwhere) {
var return_array = [];
$.each(data, function (i, obj) {
var temp_obj = {};
var where = true;
if (andwhere) {
$.each(andwhere, function(j, wh) {
if (obj[wh.col] !== wh.val) {
where = false;
}
});
}
if (where === false) {
return;
}
$.each(obj, function (j, elem) {
if (select.indexOf(j.trim())!==-1) {
temp_obj[j] = elem;
}
});
return_array.push(temp_obj);
});
return return_array;
};
var result = query(data, ['price','amount'],[{"col":"time","val":136804}]);
console.log(JSON.stringify(result));
http://jsfiddle.net/bejgy3sn/1/