Im trying to figure out the best way to handle incoming saveAll batches from our iOS client. A user can create a few comments in the app and if they leave the app or move away from the comment page it does a batch save of all the comment objects.
In cloud code I need to check the support ticket count for an organization that the comment is associated with, then increment the count on the org object, and save the comment with the ticket number.
So lets say when 2 comments are saved, it gets the ticket number but saves both comments with the same ticket number.
ex: org ticket count 100 2 comments are added from our client with saveAll function.
Cloud code is supposed to get the org ticket count, then save the comment with the ticket count, increment the org ticket count and save the next etc.
here is some example code im playing around with.. everything works but the comments from saveAll are all saved with the same org ticket number.
Parse.Cloud.beforeSave("Comment", function(request, response) {
// when a comment is added we get the current ticketNumber value
// from the org, and assign it to the new comment.
// after that we increment the counter on the org.
console.log('========================')
console.log(request.object.id) // empty
console.log(request.object.get('org'))
console.log('========================')
var orgId = request.object.get("org").id
// get the count from the org
query = new Parse.Query('Organization')
query.equalTo("objectId", orgId)
query.first({ useMasterKey: true }).then(function(object) {
console.log('in query')
// assign it as a variable.
var ticketNumber = object.get('ticketNumber')
console.log(ticketNumber)
// atomic increment ticket number on org
object.increment("ticketNumber")
object.save(null, { useMasterKey: true });
// save the ticketNumber to the comment
request.object.set("ticketNumber", ticketNumber)
response.success();
}, function(error) {
response.error("something happened")
});
});
of course this works if one object comes in.. its just when multiple objects are saved from the client..
Saving and querying for the Org
is going to happen asynchronously, and all your saves are going to happen at the same time, fetching that same org, and assigning the old value before any of the saves go through.
Instead of saveAll from the iOS client, create a cloud code function that takes an array of comments to save. If they all belong to the same Org, you can fetch that once from the first comment, and then iterate through your comments, incrementing the org's ticketNumber and assigning it to each object in turn, then save everything (comments and org) together.
If the comments aren't always the same org, you could first iterate through your comments to get all orgs you need, stick them in an array and do a fetchAll, then iterate through your comments and access the orgs appropriately. This is a good idea so you're fetching two objects once each instead of two objects 10 times each in the case you have 20 tickets, 10 each for 2 different orgs.
If you still need access to the tickets, you can save the org and tickets separately, and return the save result of saveAll on the comments.
Edit:
In cloud code, you could have something like this, assuming all Comments have the same org:
Parse.Cloud.define("saveComments", function(request, response) {
var comments = request.params.comments;
if( !comments || comments.length == 0 ) return response.success(); //Handle case with no comments to save - Job done!
var org = comments[0].get("org");
// Since you don't need to include any Parse Objects, you can use fetch instead of query
org.fetch({useMasterKey:true}).then(
function(organization) {
for( comment in comments ) {
organization.increment("ticketNumber");
comment.set("ticketNumber", organization.get("ticketNumber"));
}
return organization.save(null, {useMasterKey:true}).then(
function(organization) {
return Parse.Object.saveAll(comments, {useMasterKey:true}); //Put this after saving the org, so the return value is the same as your current client save all call
}
);
}
).then(
function(success) { response.success(success); },
function(error) { response.error(error); }
);
});
Then on your iOS client, instead of using saveAll, you would do something like so:
NSDictionary *params = @{"comments":commentsArray};
[PFCloud callFunctionInBackground:@"saveComments" withParams:params...];`