How to get count of relation field in parse - javascript

GameScore object have one Relation field named Badges.
How I can get count of all objects in this relation in query:
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("playerName", "Dan Stemkoski");
query.find({
success: function(results) {
alert("Successfully retrieved " + results.length + " scores.");
// Do something with the returned Parse.Object values
for (var i = 0; i < results.length; i++) {
var object = results[i];
alert(object.id + ' - ' + object.get('playerName'));
}
},
error: function(error) {
alert("Error: " + error.code + " " + error.message);
}
});
I need something like:
object.Badges.count
or
object.Badges.length

A Parse.Relation object is actually a query description that will return the objects in that relation, so for this case you'd need to run another query for each GameScore:
query.find().then(function (gameScores) {
alert("Successfully retrieved " + gameScores.length + " scores.");
var countPromises = gameScores.map(function (gs) {
// the following executes a count() query, which is severely penalized by Parse
return gs.get('Badges').query().count()
.then(function (count) {
// this extra step is to add the retrieved count as an extra property to the GameSccore object,
// instead of returning only the counts
gs.count = count;
return gs;
});
});
return Parse.Promise.when(countPromises);
}).then(function () {
var gameScoresWithBadgeCount = Array.prototype.slice.call(arguments);
}).fail(function(error) {
alert("Error: " + error.code + " " + error.message);
});
This causes a lot of extra round trips (I assume you're on a browser environment because of alert()), and calls count() queries which are additionally limited by Parse.
What I can recommend you is to keep a count cache as an extra field on the GameScore class, and update it accordingly through CloudCode hooks. Alternatively, you can try to avoid the Relation and make the equivalent using an Array field if possible, through which you can always include the related Badges if needed or get their count without querying for them at all!

Sadly, tougher than one would hope. It requires another asynch trip through the data, so your callback form can do it, but isn't really up to the job. Here it is with promises...
// return a promise that's fulfilled with a badges count for the passed GameScore
function badgeCountOfGameScore(gameScore) {
var relation = gameScore.get("Badges"); // it must have a relation col called Badges for this to work
return relation.query.find().then(function(results) {
return results.length;
});
}
Now your original function redone with promises (and underscore to better handle arrays)...
var _ = require("underscore");
function bunchOfBadgeCounts() {
var GameScore = Parse.Object.extend("GameScore");
var query = new Parse.Query(GameScore);
query.equalTo("playerName", "Dan Stemkoski");
return query.find().then(function(results) {
alert("Successfully retrieved " + results.length + " scores.");
var promises = _.map(results, function(object) {
return badgeCountOfGameScore(object);
});
return Parse.Promise.when(promises);
}).then(function() {
alert("Counts = " + JSON.stringify(_.toArray(arguments)));
}, function (error) {
alert("Error = " + JSON.stringify(error));
});
}

Related

Firebase cloud functions: "Unhandled error RangeError: Maximum call stack size exceeded"

I have a cloud function that uses firebase and after I call it from my angular app I get the mentioned above error:
Unhandled error RangeError: Maximum call stack size exceeded
at baseKeys (/workspace/node_modules/lodash/lodash.js:3483:12)
at keys (/workspace/node_modules/lodash/lodash.js:13333:60)
at /workspace/node_modules/lodash/lodash.js:4920:21
at baseForOwn (/workspace/node_modules/lodash/lodash.js:2990:24)
at Function.mapValues (/workspace/node_modules/lodash/lodash.js:13426:7)
at encode (/workspace/node_modules/firebase-functions/lib/providers/https.js:184:18)
at /workspace/node_modules/lodash/lodash.js:13427:38
at /workspace/node_modules/lodash/lodash.js:4925:15
at baseForOwn (/workspace/node_modules/lodash/lodash.js:2990:24)
at Function.mapValues (/workspace/node_modules/lodash/lodash.js:13426:7
I've searched stack to find solutions - but in most cases there was serialization problem, that I believe DOES NOT happen here.
Here is my function:
exports.createCase = functions.region('europe-west2').https.onCall((data, context) => {
console.log("creating new case");
if (!context.auth) {
throw new functions.https.HttpsError('failed-precondition', 'This function must be called ' +
'while authenticated.');
}
const caseName = data.caseName;
// Authentication / user information is automatically added to the request.
const uid = context.auth.uid;
const name = context.auth.token.name || null;
const picture = context.auth.token.picture || null;
const email = context.auth.token.email || null;
console.log("caseName=" + caseName + " uid=" + uid + " name=" + name + " picture=" +
picture + " email=" + email);
var operationResult = new Promise ((resolve, reject) => {
var accessData : any = {};
var accessId = admin.database().ref('/access/').push();
var operationId = admin.database().ref('/operationslog/' + accessId.key + '/').push();
console.log("accessId created=" + accessId + ' || ' + accessId.key + ' operations id=' +
operationId + ' || ' + operationId.key);
let now: number = Date.now();
accessData[`/access/` + accessId.key] = new Access(caseName, uid, email);
accessData[`/operationslog/` + accessId.key + `/` + operationId.key] = {
date: now,
performedByUser: uid,
performedByMail: email,
performedByImg: picture,
performedBySystem: false,
operationType: 'CREATE',
order: (REVERSE_ORDER_MAX - now),
details: {creator: uid, name: caseName}
};
console.log('commiting data');
admin.database().ref().update(accessData).then( (value: void) => {
console.log("returning ok result");
resolve({
status: "Ok",
accessId: accessId,
description: 'Case created'
});
}, err => {
console.log("Error while trying to create case: " + err);
reject("CASE NOT CREATED");
}
).catch(exception => {
console.log("Error while trying to create case: " + exception);
reject("CASE NOT CREATED");
}
);
}
);
return operationResult;
});
and the call from Angular app:
let createCaseCall = functions.httpsCallable('createCase');
createCaseCall({caseName: value.caseName}).then(result => {
// Read result of the Cloud Function.
console.log("got result: " + result);
if (result.data.status == 'Ok') {
this.showSuccessMessage('Case created.');
}
}).catch(err => {
console.log("Error while calling cloud functions: " + err);
this.showErrorMessage('Error while creating the case.');
});
Now, the important information is that, the data in firebase realtime database IS CREATED when this function is called and console log does contain "returning ok result" line...
It is still a serialization problem.
Here's what you're trying to send back to the client:
resolve({
status: "Ok",
accessId: accessId,
description: 'Case created'
});
accessId is the result of a push operation:
var accessId = admin.database().ref('/access/').push();
That means it's a DatabaseReference object, which contains circular references that can't be serialized. It's not a simple data type, like a string.
You'll want to think more carefully about what you want exactly you want to send back to the client app. Maybe you wanted to send back the name or path of the child key that was created by push()?
Also, you'll probably want to remove the whole new Promise() thing, as that's an anti-pattern here. There is no need to create a new promise when you have promises from all the other database operations available to work with.

Using promises in Geofire

The Geofire documentation says that Geofire returns promises when reading and writing data but I cannot seem to find any examples of this. I was wondering how I could use this in javascript to attach a function onto the end of the listener that fired when the geofire promise is fulfilled. My code is:
var onKeyEnteredRegistration = this.geoQuery.on("key_entered", function(key, location, distance) {
console.log(key + " entered query at " + location + " (" + distance + " km from center)");
}).then(function(result){
console.log("promise resolved with:" + result);
}, function(error){
console.error(error)
});
But this is outputting an error saying 'undefined is not a function' referencing the chained function. Does anyone know how to use promises in Geofire?
Thanks
So only the get, set and remove functions on the GeoFire class return promises. Like so:
geoFire.get("some_key").then(function(location) {
if (location === null) {
console.log("Provided key is not in GeoFire");
}
else {
console.log("Provided key has a location of " + location);
}
}, function(error) {
console.log("Error: " + error);
});
However the on function instead returns a GeoQuery whose on function returns a GeoCallbackRegistration which is used to kill the query with the cancel function (which does not return anything.)
var onKeyEnteredRegistration = this.geoQuery.on("key_entered", function(key, location, distance) {
console.log(key + " entered query at " + location + " (" + distance + " km from center)");
});
onKeyEnteredRegistration.cancel(); // the "key_entered" event will stop firing

Parse.com query.equalTo error processing

I'm trying to query a Parse.com database to see if a certain field is available. I am then looking to do certain actions depending on whether or not the variable is present in the database.
Here is the full production function:
function getProduct() {
var Products = Parse.Object.extend("Products");
ProductEAN = 76130347394081;
output = "";
var query = new Parse.Query(Products);
query.equalTo("EANBarcode", ProductEAN );
query.find({
success: function(results) {
var no = results[0].get("EANBarcode");
var title = results[0].get("Name");
output += "[" + no + "] " + title;
console.log( "Output is: " + output );
},
error: function(error) {
alert(error.message);
}
});
}
If the ProductEAN is present, then the success function works as intended. If it is not present, rather than the error function running, I get a console output saying the following:
"Uncaught TypeError: Cannot read property 'get' of undefined"
I am following this guide: https://www.youtube.com/watch?v=2TVmgEJfbno
I am at a bit of a loss as to why this would not work. Does anyone have any experience with this?
Error callback of find() method gets called only when there's an error in execution. In the case when ProductEAN is not present, it is still a successful query but with zero matching records. Which means it calls your success callback with results being an empty array. Hence results[0] is undefined , which explains the console error output.
You might want to change your success callback to verify for the results.length and make decision appropriately. Something like this :
query.find({
success: function(results) {
if(results.length > 0){
var no = results[0].get("EANBarcode");
var title = results[0].get("Name");
output += "[" + no + "] " + title;
console.log( "Output is: " + output );
}
else{
console.log('No matching records');
}
},
error: function(error) {
alert(error.message);
}
});

How to return the response of multiple asynchronous calls?

Similar to this question : Return response from async call
Except that the call is within a loop that calls multiple time the asynchronous function.
Specifically, how can the value of s be returned? This code returns undefined. This function is called within a for loop. The library used for ORM is Bookshelfjs. Thanks for the help.
function getUsernameFromDBAsync(userId) {
var s = "moo";
new Model.Users({
idUser: userId
})
.fetch()
.then(function(u) {
var prenom = u.get('firstName');
var nom = u.get('familyName');
s = prenom + " " + nom;
return s;
});
}
Your aren't really showing how you're doing the loop which makes it a little harder to guess what to recommend. But assuming .fetch().then() returns a promise, here's a general idea with standard ES6 promises built into node.js:
function getUsernameFromDBAsync(userId) {
var s = "moo";
return new Model.Users({
idUser: userId
}).fetch().then(function (u) {
var prenom = u.get('firstName');
var nom = u.get('familyName');
s = prenom + " " + nom;
return s;
});
}
var userIds = [...];
var promises = [];
for (var i = 0; i < userIds.length; i++) {
promises.push(getUsernameFromDBAsync(userIds[i]));
}
// now set up a .then() handler for when all the promises are done
Promise.all(promises).then(function(names) {
// process names array here
}, function(err) {
// process error here
});
If you are using the Bluebird promise library, you could do it a little bit more simply like this:
function getUsernameFromDBAsync(userId) {
var s = "moo";
return new Model.Users({
idUser: userId
}).fetch().then(function (u) {
var prenom = u.get('firstName');
var nom = u.get('familyName');
s = prenom + " " + nom;
return s;
});
}
var userIds = [...];
Promise.map(userIds, getUsernameFromDbAsync).then(function(names) {
// process names array here
}, function(err) {
// process error here
});
Var s is unnecessary. Simply return the "prénom nom" string from then's success callback.
function getUsernameFromDBAsync(userId) {
return new Model.Users({
idUser: userId
}).fetch().then(function (u) {
return u.get('firstName') + ' ' + u.get('familyName');
});
}
Your "loop" can be achieved with Array.prototype.map() to generate an array of promises, followed by Promise.all(promises).then(...), to receive and handle an array of names when all the promises have resolved.
var promises = userIds.map(function(userId) {
return getUsernameFromDBAsync(userId);
});
Promise.all(promises).then(function(names) {
// do something with `names`, which is an array of `prénom nom` strings.
}, function(err) {
// handler error, eg ...
console.error(error);
});
Or more succinctly :
Promise.all(userIds.map(getUsernameFromDBAsync)).then(function(names) {
// do something with `names`, which is an array of `prénom nom` strings.
}, function(err) {
// handler error, eg ...
console.error(error);
});

Javascript Function Not Returning Data - Node-MySQL

Using NodeJS, Express, Socket.io & node-mysql on a server at the moment. Currently having a problem with one of the functions on the server not returning any data at all. I'm at my ends wit trying to figure this out.
Function Code;
It is supposed to return "c", but is not working. The console.log is actually showing the ID and username.
function LOGIN_USER(a,b) {
// a = username
// b = password
var c = [];
connection.query("SELECT ID FROM GAME_PLAYER WHERE USR = '" + a + "' AND PWD = '" + b + "'", function(err,rows,field) {
if (err) throw err;
for (var i in rows) {
c.ID = rows[i].ID;
c.USR = a;
}
console.log(c.ID + " " + c.USR);
return c;
});
}
Other code.
socket.on('login user', function(data) {
var c;
c = LOGIN_USER(data.username,data.password);
console.log(c.ID,c.USR);
});
After this console.log is where my nodeJS server crashes. Saying it can't display undefined etc.. etc.
Can't for the life of me figure this out, any help is most appreciated!! :)
The MySQL query is asynchronous, so you cannot use a return value in it. In asynchronous programming, you must use a callback, because a return statement will stop execution:
function LOGIN_USER(a, b, callback) {
// a = username
// b = password
var c = [];
connection.query("SELECT ID FROM GAME_PLAYER WHERE USR = '" + a + "' AND PWD = '" + b + "'", function (err, rows, field) {
if (err) throw err;
for (var i in rows) {
c.ID = rows[i].ID;
c.USR = a;
}
console.log(c.ID + " " + c.USR);
callback(c);
});
}
And then this is how you'd use it:
socket.on('login user', function(data) {
LOGIN_USER(data.username, data.password, function(c) {
console.log(c.ID, c.USR);
});
});
When you use a return statement in a callback function, it acts as if you used return;, which in turn just stops the execution of a function. This is how the callback is working:
You are passing values to a function, as well as another function:
var func = function() {
// do something
};
LOGIN_USER(a, b, func);
When the user login has completed, the login function will call the function that was passed to it:
function LOGIN_USER(a, b, callback) {
// do some work
callback();
};
So you passed func() to LOGIN_USER(), and LOGIN_USER() called func() when it completed.

Categories

Resources