Parse retrieve columns from 2 different objects - ios

I've been using sql forever and i'm now stepping into using parse. I've reach a problem regarding the database structure. i have 2 objects called stores and users. i've added the stores objectId to each user so that i can identify which user is to which store. I do not know if there is another way, but this was what i could think of.
The issue is that i want to retrieve all the users and the store names which is connected to the users. How can achieve this using parse?
My code at the moment.
PFQuery *query = [PFQuery queryWithClassName:#"users"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
for (PFObject *object in objects) {
}
NSLog(#"%#", objects);
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}

It does sound like the aim is to fetch related objects, but my understanding about how to do that differs from #Dima. You do it like this...
PFQuery *query = [PFQuery queryWithClassName:#"users"];
// this tells the query to eagerly fetch the related store object
[query includeKey:#"store"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// handle error, or ...
for (PFObject *object in objects) {
// object is from the users table, and object[#"store"] will contain
// the complete, related store object, eagerly fetched...
NSLog(#"the store object for %# is %#", object, object[#"store"]);
}
}];

If I understand correctly, you just want to fetch a related object? The documentation covers this.
You should be able to fetch a related object like this:
PFObject *store = user[#"store"]; // reference to related object but may need to be fetched
[store fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
// at this point you have fetched the store or you already have it
NSString *name = store[#"name"];
}];

Related

Why is my NSMutable not being populated correctly when inserting objects from another NSArray?

I have a PFQuery that gets the current participants of a particular event:
PFQuery *getcurrentparticipants = [PFQuery queryWithClassName:#"Event"];
[getcurrentparticipants selectKeys:#[#"Participants"]];
[getcurrentparticipants whereKey:#"objectId" equalTo:ObjectID];
[getcurrentparticipants findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSMutableArray *newParticipantsArray = [[NSMutableArray alloc]init];
if([objects[0] valueForKey:#"Participants"] == nil){ // If object retrieved in objects is null. If there are 0 participants
[newParticipantsArray addObject:PFUser.currentUser.username];
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query getObjectInBackgroundWithId:self.ObjectID
block:^(PFObject *Event, NSError *error) {
Event[#"Participants"] = newParticipantsArray;
[Event incrementKey:#"Vacants" byAmount:[NSNumber numberWithInt:-1]];
[Event saveInBackground];
}];
}else{ // STEP 5
for(int i=0;i<objects.count;i++) {
[newParticipantsArray addObject:[[objects objectAtIndex:i] valueForKey:#"Participants"]];
}
[newParticipantsArray addObject:PFUser.currentUser.username];
NSLog(#"Part to upload %#", newParticipantsArray);
PFQuery *query = [PFQuery queryWithClassName:#"Event"];
[query getObjectInBackgroundWithId:self.ObjectID
block:^(PFObject *Event, NSError *error) {
Event[#"Participants"] = newParticipantsArray;
[Event incrementKey:#"Vacants" byAmount:[NSNumber numberWithInt:-1]];
[Event saveInBackground];
}];
}
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
This is how the method works:
Create a PFQuery object
Query the Participants Class for an specific ObjectId
If no error, then we create a NSMutable array
If no participants are in Parse then we insert the current user as participant.
Else, insert all participants in the mutable array and add currentuser at the end of the array.
Then upload it again to Parse
My problem is in step 5:
When I perform the tasks in the else, the column in Parse looks like this :
[["Participant 1"],"Participant 2"]
But I would like to have it like this:
["Participant 1","Participant 2"]
What I have tried:
I tried things like putting the arrays like this. [newParticipantsArray addObject:[[objects objectAtIndex:i] valueForKey:#"Participants"]]; and similar combinations, of course without luck.
It’s hard to say for sure since I can’t see the structure of your data but are you sure the value held in
[[objects objectAtIndex:i] valueForKey: #“Participants”]
Is a single user and not itself an array of users? The plural key “participants” seems to suggest it’s an array of users which would also explain the result you’re getting.
If in fact the value returned for the "Participants" key is an array, you can add the objects in it to your mutable array by doing the following:
NSArray* participants = [[objects objectAtIndex:i] valueForKey:#"Participants"]
[newParticipantsArray addObjectsInArray:participants];
This uses the addObjectsInArray: method of NSMutableArray to add the objects from the old array into the new one.

Accessing Items in NSArray from PFObject

I'm building an application on Parse.com's backend for iOS that has simple follower / following logic to it. I.e. I want the user to be able to find other users and follow their profiles. I can currently retrieve all of the users in the database using a general PFQuery. Tapping on a users profile image "follows" that person. The data is set up so that an array called isFollowing has a unique object added to it with each new person that the user chooses to follow. It's adding it by the users objectId.
However, here's where my bump in the road is; for a separate screen I only want to return the number of people I, as a user, am following. This is the query to access the data:
- (void)viewDidAppear:(BOOL)animated
{
PFQuery *followingQuery = [PFUser query];
[followingQuery whereKey:#"objectId" equalTo:[PFUser currentUser].objectId];
[followingQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
NSLog(#"User test: %#", [objects valueForKey:#"isFollowing"]);
PFQuery *imagesQuery = [PFUser query];
[imagesQuery whereKey:#"objectId" containedIn:[objects valueForKey:#"isFollowing"]];
[imagesQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
if (!error)
{
NSLog(#"Username? %#", [objects valueForKey:#"username"]);
}
else
{
NSLog(#"Something went wrong. %#", error);
}
}];
}
else
{
NSLog(#"Something went wrong: %#", error);
}
}];
[super viewDidAppear:animated];
}
Currently, user test: %# gives the output:
2014-07-28 10:17:58.537 YouStreamSport[11276:60b] User test: (
(
bHul1vkkmB,
brsN8PRUBO
)
)
However, when I run objects.count it's returning one object. Because of this I'm unable to iterate through the array object to find and return only the proper profile images. Since the array object is returning as 1, but there are 2 objects in the array itself, how can I access the items in that array? Any attempts to access using [objectAtIndex: ...] result in a crash.
Thanks for the input in advance.
I wasn't sure how to access a single item in an array within an array, but thanks to the help of #Droppy, I was able to solve the problem. This is the code I am now using:
- (void)viewDidAppear:(BOOL)animated
{
PFQuery *followingQuery = [PFUser query];
[followingQuery whereKey:#"objectId" equalTo:[PFUser currentUser].objectId];
[followingQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error)
{
self.array = [objects valueForKey:#"isFollowing"];
NSLog(#"User test: %#", self.array);
for (int i = 0; i <= self.array.count; i++)
{
NSLog(#"User %d: %#", i, [[[objects valueForKey:#"isFollowing"] objectAtIndex:0] objectAtIndex:i]);
}
}
else
{
NSLog(#"Something went wrong: %#", error);
}
}];
[super viewDidAppear:animated];
}
The resulting output is as follows:
2014-07-28 10:53:16.155 appName[11416:60b] User 0: bHul1vkkmB
2014-07-28 10:53:16.156 appName[11416:60b] User 1: brsN8PRUBO
Thank you!
As written you will only display one objects username. Instead of trying to access them by index try iterating through the results like below. I'd also recommend not using the variable objects for both the inner and outer query. Try this out:
[imagesQuery findObjectsInBackgroundWithBlock:^(NSArray *myObjects, NSError *error)
{
if (!error){
if (myObjects.count) {
for (PFObject *item in myObjects){
NSLog(#"Username: %#", [item valueForKey:#"isFollowing"]);
}
}
}else{
NSLog(#"Something went wrong. %#", error);
}
}];

Download one array from Parse

Quite a simple question really but can't find the answer anywhere.
On the Parse data browser, there are lots of users and they each have an array with a bunch of string values, how do i download just one of these arrays from just one user into an NSArray of string values?
Thanks in advance.
Do you know the objectId of the user? or the username? Say it's the username:
PFQuery *query = [PFQuery queryWithClassName:#"User"];
[query whereKey:#"username" equalTo:#"Vladimir Putin"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
if (objects.count) {
PFUser *vladimir = objects[0];
NSLog(#"Putins strings are %#" [vladimir objectForKey:#"theArrayColumn"]);
} else {
// where are you Vlad?
}
} else {
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
It's easier if you know the object id. Then use getObjectInBackgroundWithId: instead of find. (To be clear, you'll be downloading the object and it's array property. There's no way that I know of to get just a property).

Query on Parse relational data is not coming back ordered using orderedByAscending

I'm querying relation data on parse and I would like the objects to come back ordered by the date they were created. I've had this method work before but haven't been able to get an ordered query using relational data. The query return is in a random order. Thanks in advance! Here's my code:
PFQuery *postQuery = [PFQuery queryWithClassName:#"Post"];
[roomQuery whereKey:#"name" equalTo:self.postName];
NSError *error;
//done on main thread to have data for next query
NSArray *results = [postQuery findObjects:&error];
PFObject *post;
if ([results count]) {
post = [results objectAtIndex:0];
NSLog(#"results were found");
} else {
NSLog(#"results were not found");
}
PFRelation *commentsRelation = [#"Comments"];
[commentsRelation.query orderByAscending:#"createdAt"];
[commentsRelation.query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error) {
NSLog(#"Error Fetching Comments: %#", error);
} else {
NSArray *comments = objects;
}
I'm a little confused by your code,
you create a "postQuery", and call it, but never use any of its data.
There's also a roomQuery that never seems to have been allocated, or used.
You're querying a specific post by its name. Are you controlling its name? If not, you should use id's
what is PFRelation commentsRelation = [#"Comments"];
Probably because it's just a snippet, this stuff is dealt with elsewhere; however, for my answer, I'm assuming that your "comments" field is an array of "Comment" class objects.
Option 1:
PFQuery * postQuery = [PFQuery queryWithClassName:#"Post"];
[postQuery whereKey:#"name" equalTo:self.postName];
// again, possibly an id field would be more reliable
// [postQuery whereKey:#"objectId" equalTo:self.postId];
[postQuery includeKey:#"Comments"];
PFObject * post = [postQuery getFirstObject];// no need to download all if you just want object at [0]
// this will contain your post and all of it's comments with only one api call
// unfortunately, it's not sorted, so you would have to run a sort.
NSArray * comments = [post[#"Comments"] sortedArrayUsingComparator: ^(id obj1, id obj2) {
return [obj1[#"createdAt" compare: obj2[#"createdAt"];
}];
Option 2:
Perhaps a better option is to rework your data structure and instead of associating the comments to the post, you could associate the post to the comments (as in the parse docs)
PFQuery * postQuery = [PFQuery queryWithClassName:#"Post"];
[postQuery whereKey:#"name" equalTo:self.postName];
// again, possibly an id field would be more reliable
// [postQuery whereKey:#"objectId" equalTo:self.postId];
PFQuery * commentQuery = [PFQuery queryWithClassName:#"Comment"];
[commentsQuery whereKey:#"parent" matchesQuery:postQuery]; // when creating a comment, set your post as its parent
[commentsQuery addOrderDescending:#"createdAt"]
[commentQuery findObjectsInBackgroundWithBlock:^(NSArray *comments, NSError *error) {
// comments now contains the comments for myPost
}];
Both of the above solutions avoid making extra unnecessary api calls (parse charges based on calls after all!).

Parse.com Query if String is Contained in Array

Working with recipes and I would like to query (search) based off of items in the recipe rather than the recipe names.
For example, multiple items may contain Chicken. I want to be able to search Chicken and see the Recipe Names that contain Chicken in the recipe.
Here's what I have tried:
- (void)filterResults:(NSString *)searchTerm
{
PFQuery * query = [PFQuery queryWithClassName:self.parseClassName];
NSArray * ingredientArray = [self.profileObject objectForKey:#"ingredients"];
[query whereKey:searchTerm containedIn:ingredientArray];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error)
{
NSLog(#"Error: %# %#", error, [error userInfo]);
}
else
{
[self.searchResults removeAllObjects];
[self.searchResults addObjectsFromArray:objects];
[self.searchDisplayController.searchResultsTableView reloadData];
}
}];
}
This code returns nothing and I get no errors.
Having difficulty figuring out the right way to set up the query.
Should this be solved as a query within a query?
Meaning:
Query through ingredients first, and then query on that to display the recipe name based on the previous query of recipes that contain the searchTerm.
I think you are misusing the [query whereKey:containedIn:] method. This is used to query for all PFObjects where the object for the key you specify is contained in the array you provide. Unless you made a new key for every recipe item, this won't work for your purposes, because none of your objects have a "Chicken" key, for instance.
First, I suggest you use a RecipeIngredient class in Parse with the following fields:
Pointer(Recipe) recipe //pointer to its Recipe object
Number amount //how much of unit
String unit //cup, gram, etc.
String ingredient //milk, flour, etc.
Now, you can simply query the RecipeIngredient class like so:
PFQuery * query = [PFQuery queryWithClassName:"RecipeIngredient"];
[query whereKey:"ingredient" equalTo:searchTerm];
[query includeKey:"recipe"]; //Fetches the Recipe data via the pointer
[query findObjectsInBackgroundWithBlock:^(NSArray *recipeIngredientObjects, NSError *error) {
if (!error) {
NSArray *recipes = [recipeIngredientObjects valueForKey:#"recipe"];
//update table data as needed
} else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
I had this problem last year sometime so I thought I would share the answer.
[query whereKey:#"ingredient" matchesRegex:searchTerm modifiers:#"i"];
That should do it for you. That helps with the case sensitivity.

Resources