Reputation: 341
I have the following function below which I am using to determine a users rank in a scoreboard.
Parse.Cloud.define("getUserGlobalRank", function(request, response)
{
var usernameString = request.params.username;
var scoreAmount = request.params.score;
var globalRankQuery = new Parse.Query("scoreDB");
globalRankQuery.greaterThanOrEqualTo("score",scoreAmount);
globalRankQuery.descending("score");
globalRankQuery.limit("1000");
globalRankQuery.count(
{
success: function(count);
{
response.success(count);
},
.........
However this will not give an accurate response if there are more than 1000 entries that meet that criteria. I would like to chain together a set of .find methods that I can subsequently perform a .count on. Does anyone have any idea how I can achieve this? If you could provide a code example that would be great!
Many thanks, James
Upvotes: 5
Views: 7443
Reputation: 2577
Beware when using count. For classes with over 1000 objects, count operations are limited by timeouts. They may routinely yield timeout errors or return results that are only approximately correct. Thus, it is preferable to architect your application to avoid this sort of count operation. I typically use the skip parameter like so:
Parse.Cloud.define('getUserGlobalRank', function(request, response) {
var output = [];
var usernameString = request.params.username;
var scoreAmount = request.params.score;
var skip = (request.params['skip']) ? request.params.skip : 0;
if (skip == 0 || !window['results']) { window['results'] = []; }
var qry = new Parse.Query('scoreDB');
qry.greaterThanOrEqualTo('score', scoreAmount);
qry.descending('score');
qry.limit(1000);
qry.skip(skip);
qry.find({
success: function (results) {
if (results.length > 0) { // got results..
output = output.concat(results);
// Call the function again
var params = request.params;
params['skip'] = results.length;
request.params = params;
Parse.Cloud.run('getUserGlobalRank', request, response);
} else { // we're done here
response.success(output);
}
},
error: function (err) {
}
});
});
Upvotes: 3
Reputation: 141
Reckon this should work as far as implementing promises. Also, pretty sure skip is only supported up to 10,000 unfortunately, but I've been wrong before. Full disclosure, I've not tested this code.
Parse.Cloud.job("checkUsers", function(request, response) {
// {} - empty params for first iteration
getAllUsers({}).then(function(users) {
// Do stuff with users
response.success("Loaded " + users.length + " users");
},
function(error) {
response.error(error);
});
});
function getAllUsers(request) {
//
var users = (request["users"]) ? request["users"] : [];
var skip = users.length;
var userQuery = new Parse.Query(Parse.User);
userQuery.limit(1000);
userQuery.skip(skip);
Parse.Promise.as().then(function() {
//
return userQuery.find().then(null, function(error) {
//
return Parse.Promise.error(error);
});
}).then(function(userObjects) {
//
if (userObjects.length > 0) {
var allUsers = users.concat(userObjects);
var params = {"users": allUsers};
getAllUsers(params);
} else {
//
return Parse.Promise.resolve(allUsers);
}
},
function(error) {
//
return Parse.Promise.reject(error);
});
}
Upvotes: 0
Reputation: 1816
My goal was to iterate through all users on Parse. Here's my answer based on keither04's answer that is based on Cam Tullos's answer =]
Parse.Cloud.job("checkUsers", function (request, response)
{
// {} - empty params for first iteration
getAllUsers({}, {
success: function (users) {
// Do stuff with users
response.success("Loaded " + users.length + " users");
},
error: function (error) {
response.error(error);
}
});
});
function getAllUsers (request, response)
{
var users = (request["users"]) ? request["users"] : [];
var skip = users.length;
var userQuery = new Parse.Query(Parse.User);
userQuery.limit(1000);
userQuery.skip(skip);
userQuery.find({
success: function (results) {
if (results.length > 0) {
var allUsers = users.concat(results);
var params = {"users": allUsers};
getAllUsers(params, response);
} else {
response.success(users);
}
},
error: function (error) {
response.error(error);
}
});
}
P.S.: I'm curious if it's possible to do using promises.
Upvotes: 4
Reputation: 321
The following answer will work in Cloud Code as it keeps track of the count by using a function parameter in successive calls to getUserGlobalRank(). I have used this architecture in similar cases successfully.
As Gene Z. Ragan mentioned in his comment, the currently accepted answer will not work in Cloud Code because there is no 'window' object.
The reason that recursion has been offloaded to a regular Javascript function is because Parse has a very low recursion limit for Cloud Code functions (I have verified that >4 recursive calls to a Cloud Code function will result in an error). By implementing the recursion in a Javascript function, you can bypass Parse's recursive limit, and continue making recursive calls as long as the code executes in the permitted time (about 15 seconds).
Parse.Cloud.define('getUserGlobalRank', function(request, response) {
getUserGlobalRank({'username':request.params.username, 'score':request.params.score}, {
success: function(count) {
response.success(count);
},
error: function(error) {
response.error(error);
}
});
});
function getUserGlobalRank(request, response) {
var usernameString = request['username'];
var scoreAmount = request['score'];
var count = (request['count'])? request['count'] : 0;
var globalRankQuery = new Parse.Query("scoreDB");
globalRankQuery.greaterThanOrEqualTo("score", scoreAmount);
globalRankQuery.descending("score");
globalRankQuery.limit(1000);
globalRankQuery.skip(count);
globalRankQuery.find({
success: function(results) {
if (results.length > 0) {
count = count + results.length;
getUserGlobalRank({'count':count, 'username':usernameString, 'score':scoreAmount}, response);
}
else { // found count of users with higher ranks
response.success(count);
}
},
error: function(error) { // query error
response.error(error);
}
});
}
Upvotes: 5
Reputation: 788
Parse.Query.count() is not affected by the Parse.com 1000 query limit.
So you will get a correct count returned no matter how many entries matched by the query.
eg:
var userQ = new Parse.Query('_User');
userQ.count({success:function(count){console.log(count)}});
> 1512
var userQ = new Parse.Query('_User');
userQ.limit(100);
userQ.count({success:function(count){console.log(count)}});
> 1512
Bonus points - .limit()
takes a number as its parameter, not a string as in your code.
https://www.parse.com/docs/js/symbols/Parse.Query.html#limit
hth
Upvotes: 3