iOS Parse SDK: Multiple query results put into an Array - ios

Scenario = I have an app where users can send each other Messages, Comments, and Pokes that are queried to populate the current user's notificationsTableView. There are 3 queries that must take place, one for Messages, two for Comments, and three for Pokes. The code I'm using is below...
PFQuery *messageQuery = [PFQuery queryWithClassName:#"Message"];
[messageQuery whereKey:#"receiverID" equalTo:[PFUser currentUser][#"userID"]];
[messageQuery orderByDescending:#"createdAt"];
[messageQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
messages = objects;
}];
PFQuery *pokeQuery = [PFQuery queryWithClassName:#"Poke"];
[pokeQuery whereKey:#"receiverID" equalTo:[PFUser currentUser][#"userID"]];
[pokeQuery orderByDescending:#"createdAt"];
[pokeQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
pokes = objects;
}];
PFQuery *commentsQuery = [PFQuery queryWithClassName:#"Comment"];
[commentsQuery whereKey:#"receiverID" equalTo:[PFUser currentUser][#"userID"]];
[commentsQuery orderByDescending:#"createdAt"];
[commentsQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
comments = objects;
}];
What is desired = To consolidate the following arrays: "messages", "pokes", and "comments" into a single array (notificationsArray) that I can sort by "createdAt" and populate my notificationsTableView with notificationsArray objectAtIndexPath:indexPath.row.
Problems I have encountered = (there are two)
(1) When I NSLog the results of any of these queries like so...
PFQuery *messageQuery = [PFQuery queryWithClassName:#"Message"];
[messageQuery whereKey:#"receiverID" equalTo:[PFUser currentUser][#"userID"]];
[messageQuery orderByDescending:#"createdAt"];
[messageQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
messages = objects;
}];
NSLog(#"messages = %#", messages);
It logs "messages = (null)". I can not for the life of me figure out why it is not being set. I know there are messages because when I NSLog the "objects" Array that comes from the query it gives me what I want. It's like the contents of the query will not leave the scope of the query itself. All of the queries above do this. If I can not get the contents of the query out of that block then I can not create an array of all of the arrays to populate the notificationsTableView with, so I'm screwed. Please help.
(2) Even if I do get the results from the queries into individual arrays, I am not sure how to create an array of arrays and order them by a key. Can anyone help me with this? Please.

You are probably looking for the +orQueryWithSubqueries:(NSArray *)queries method, but I don't understand what the return value description is:
a PFQuery that is the or of the passed in PFQueries
I'm thinking this means || (or) ?
You would do it like this:
NSArray *queryArray = [NSArray arrayWithObjects:messageQuery,pokeQuery,commentsQuery,nil];
PFQuery *allQueries = [PFQuery orQueryWithSubqueries:queryArray];
[allQueries findObjects... {
As for the second error, you are right, value is not retained because when the block loses scope all of the local variables inside get destroyed in the autoreleasepool. You need to retain this by using a strong property. self.messages = objects;

(1) You are logging messages outside of the callback function, and the log comes before the callback function returned. Try to log messages into your callback, just after assigning it.
[messageQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
messages = objects;
NSLog(#"messages = %#", messages)
}];
(2) Before sorting, create a NSMutableArray and use the addObjectsFromArray: method with each retrieved array.
To sort notifications, you should use a NSSortDescriptor, which is a mechanism that describes how to sort an array according to the format of contained objects. Here's an example that could match your needs:
NSSortDescriptor *createdAtDescriptor = [[NSSortDescriptor alloc] initWithKey:#"createdAt" ascending:YES];
notificationsArray = [messages sortedArrayUsingDescriptors:#[createdAtDescriptor]];
Hope this help!
EDIT: you can embed your temporary NSMutableArray into an autorelease pool to avoid useless memory leaks, so that the dedicated memory is freed just after you proceed to display.
EDIT: you can use orQueryWithSubqueries Parse method to merge several requests into a single one. It's not annoying in your case cause you're sorting PFObject according to their createdAt key, which is common to every PFObject. In any case, you will have to check PFObject types to display them according to their type. Please see full documentation here. Does not work for queries returning several kind of objects!

Related

sort the query depends on the data in an array

In one condition, sort the query depends on the objectId in the array.If the objectId is in the arraym, i want show it top.My code
PFQuery *query = [PFQuery queryWithClassName:#"Group"];
[query orderByDescending:#"pinUser" ]; //Normal Sort
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
------
---------
}
}];
I want to change the change the normal sort to my requirement.
Apply your sorting on resulted objects in block. u can check the objectId of objects in ur arrayM or not on resulted array of objects , not with PFQuery

Load column form parse in to an Array

I am working on a little app with parse.com. I want to download all objects from a column (Array) called "Firstname". I found some code, but when I log "object" it shows the class completely:
PFQuery *query = [PFQuery queryWithClassName:#"Name"];
[query selectKeys:#[#"Firstname"]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
NSLog("%#", objects);
}];
Edit:
PFQuery *query = [PFQuery queryWithClassName:#"Name"];
[query selectKeys:#[#"Firstname"]];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
self.FirstnameArray = [objects valueForKey:#"Firstname"];
NSLog(#"%#", self.FirstnameArray);
[self.myTableView reloadData];
}];
A query always returns objects of the class associated with the query. Using selectKeys just limits the data that comes back.
You can extract an array of just the values from the returned array with:
NSArray *values = [objects valueForKey:#"Firstname"]
Thats normal, you will get back the whole object - aka a row from that class you are performing the query on. If I recall correct, SelectKey will return any associated object - ie a relation object. So in your case you do not need to use select.

Parse Query Constraint Contained In Not Returning Objects

I am trying to run this query however it is returning no objects. Any help?
for (int arrayIndex=0; arrayIndex<[self.cards count]; arrayIndex++)
{
NSString *senderId = [[deck objectAtIndex:arrayIndex]objectForKey:#"SenderId"];
[list_of_sender_ids addObject:senderId];
}
PFQuery *query = [PFQuery queryWithClassName:#"User"];
[query whereKey:#"facebook_id" containedIn:list_of_sender_ids];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
A couple of things that could be going wrong right off the bat:
First of all, to query the user class, you shouldn't use
PFQuery *query = [PFQuery queryWithClassName:#"User"];
But instead,
PFQuery *query = [PFUser query];
Assuming your User class still returns PFUser objects.
Another thing to make sure is right is the #"Facebook_id" key, and confirm in Parse that this is a top level key that you can see on your Parse User objects.
Lastly, make sure #"SenderId" is also the right key on the objects in deck, since you seem not to be querying based on the same key that could cause the issue.

Create a combined Parse Query?

I have a Parse activity class that has multiple different properties to it. One of the properties happens to hold pointers to photo objects. I am needing to create a query that will give me all of the photos, which are embedded inside activity objects that also need to be queried.
The method – whereKey:matchesKey:inQuery: seems like it should work, but for some reason it's not. Here is what I've tried:
PFQuery *activityQuery = [PFQuery queryWithClassName:#"activityClass"];
[activityQuery whereKeyExists:#"photo"];
PFQuery *photoQuery = [PFQuery queryWithClassName:#"photoClass"];
[photoQuery whereKey:#"objectId" matchesKey:#"photo" inQuery:statisticsQuery];
[photoQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
}
}];
The point is that I need to create a query that will return objects, which are imbedded inside of a different classes objects. When I ran the code above I just didn't get any results in the objects array. There should have definitely been something there as well if it worked properly.
Any idea on how I could accomplish this?
You can include the photo in your first query:
PFQuery *activityQuery = [PFQuery queryWithClassName:#"activityClass"];
[activityQuery whereKeyExists:#"photo"];
[activityQuery includeKey:#"photo"];
[activityQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// You now have the activityClass objects that has photos,
// and the photoClass objects have been fetched as well.
// Iterate through the activityClass objects and get the photos from them
}
}];

parse.com: How to compare pointer-keys with objects?

I am using the backend service parse.com for a iOS app and I have a problem with querying it properly. I need help with the method whereKey:matchesKey:inQuery;
I have this code:
//NOT WORKING
PFQuery *query1 = [PFQuery queryWithClassName:#"Object"];
PFQuery *query2 = [PFQuery queryWithClassName:#"ObjectsRelations"];
[query2 whereKey:#"user" equalTo:[PFUser currentUser]];
[query1 whereKey:#"objectId" matchesKey:#"objectPointer" inQuery:query2];
[query1 findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// No objects
}];
It is not working the way I want. I have tried several ways for it to compare the key "objectPointer" in class "ObjectsRelations" (which is a pointer to an instance of class Object) to the actual Object in query 1. I do not get any objects back, because the comparison does not work as I want, since the key objectId is just a string and the key objectPointer is a pointer to a Object.
When I run this code, I get the intended result, but this requires me to do two api-requests to get the actual objectId as a string!
//WORKING
PFQuery *query = [PFQuery queryWithClassName:#"Object"];
PFQuery *query2 = [PFQuery queryWithClassName:#"ObjectRelations"];
[query2 whereKey:#"user" equalTo:[PFUser currentUser]];
[query2 findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
PFObject *firstObject = [((PFObject*)[objects firstObject]) objectForKey:#"objectPointer"];
[query whereKey:#"objectId" equalTo:firstObject.objectId];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
//Getting the objects correctly from the class Object!
}];
}];
How to do this with a single api-request? How to compare a instance of a Class to a pointer of a class with a query?
Something like this is what i want to do: (Pseudo Code)
[query1 where:SELF matches:#"objectPointer" inQuery:query2];
Any suggestions?
I just searched this exact same problem and there are a number of questions on the Parse forum regarding it:
Trouble with nested query using object_id
Assistance with relational query
Compare string to pointer in query with does not match key in query
The first one explains a hack to include an extra field (in this case) in your ObjectRelations class. This key/field would be a string that would be the objectId of the pointer. It would be in addition to the key/field that holds the pointer.
If you look through the questions you can see that as of now there doesn't seem to be an answer directly from Parse regarding this.
Instead of adding an additional column with type String to contain the objectID it points to, I would suggest to add a column on each object with type Pointer to point to the object itself. This would dramatically reduce the amount of the columns you have to add. The only downside is this must be done on Cloud Code.
At this question's scenario, you'll have two queries like:
//Inner query
//Library containing pointer<Deck> & pointer<User>
PFQuery * subscriptions = [PFQuery queryWithClassName:#"subscription"];
[subscriptions whereKey:#"User" equalTo:[PFUser currentUser]];
//Outer query
//Pull down a list of deckStore objects not included in the subscriptions for current user
PFQuery * decks = [PFQuery queryWithClassName:#"deckStore"];
Then instead of:
[decks whereKey:#"objectId" doesNotMatchKey:#"deckString" inQuery:subscriptions];
You can do this:
[decks whereKey:#"this" doesNotMatchKey:#"deck" inQuery:subscriptions];
Here's a sample of what the Cloud Code should be added:
Parse.Cloud.afterSave("Deck", function(request) {
var deck = request.object;
// To make sure this is the first time of "afterSave" of this object.
if (deck.createdAt.getTime() == deck.updatedAt.getTime()) {
// "this" is the column which contains the pointer of the object itself.
if (deck.get("this") == null) {
deck.set("this", deck);
deck.save();
}
}
}
I have the same problem. This is my solution:
PFQuery *query1 = [PFQuery queryWithClassName:#"tableClass"];
[query1 whereKey:#"objectId" equalTo:[(PFObject *)[object objectForKey:#"pointerField"] objectId]];
PFObject *obj1 = [query1 getObjectWithId:[(PFObject *)[object objectForKey:#"pointerField"] objectId]];
NSString *pointerName = [obj1 objectForKey:#"name"];

Resources