Having some trouble trying to figure out some logic for the following situation.
I have built a friends system for my mobile app using parse. Simply put, when a user "follows" something they are put into a relationship. That relationship contains all of the people that the individual user has folowed.
User
Relationship - Friends (contains all of the users that that overall user has followed)
I can query who an individual user is following fairly easily:
PFQuery *query = [PFUser query];
[query whereKey:#"username" equalTo:#"asg"];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
for (PFObject *object in objects) {
PFRelation *friendsRelation = [object objectForKey:#"Friends"];
PFQuery *query = [friendsRelation query];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"%#", objects);
} else {
}
}];
}
}];
How would I query who is following a certain user, though? So, a users followers.
If you already know the user, use whereKey:equalTo::
PFObject *user = ...;
PFQuery *query = [PFUser query];
[query whereKey:#"Friends" equalTo:user];
[query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
If you need to do a query to get the user then instead combine the requests with whereKey:matchesQuery::
[query whereKey:#"Friends" matchesQuery:userQuery];
I actually did a bunch of blog posts talking about implementing a friends list that might help you:
Overview
JavaScript part 1
JavaScript part 2
JavaScript part 3
The code samples are in JavaScript, but shouldn't be too hard to convert to iOS.
It covers the relationships, querying, updating etc.
The short answer to your question it to use a Join Table instead of a Relation so you can easily query from both sides of the relationship.
You can find a lot of information in the relations guide on the Parse site:
https://parse.com/docs/relations_guide#manytomany-jointables
I think what you are asking for is the PFUser *user = [PFUser currentUser] method. If you are trying to retrieve friends for the current user logged in. You could use the same for retrieving the users that the current user logged in has followed.
Related
I am using parse to develop an iOS app.
For the database, I have three tables, named user, UserAndCourse, and course.
Where UserAndCar stores a pointer to user and course.
the tables looks like:
My problem is:
How can I query the courses that belongs to the current user on iOS. i.e that look into UserAndCourse to find the rows that user column is current user and query for the course object that is pointed to by the course column.
Can I do this in a single relational query? or I have to query the UserAndCourse table rows first and then query the course it pointed to.
I suggest to use PFRelation, that way you don't need to create new table for relations like userAndCourse. Parse automatically will link through the relation. You just need to create relation between them and do what you want.
Saving relation;
PFUser *user = [PFUser currentUser];
PFRelation *relation = [user relationForKey:#"course"];
[relation addObject:coursePFObject];
[user saveInBackground];
And query when you need to reach them;
PFRelation *relation = [user relationForKey:#"course"];
PFQuery *query = [relation query];
[query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
// results contains all the people who subscribe to course
}];
Moreover you can specify your course by adding more queries on it;
PFQuery *query = [relation query];
[query whereKey:#"name" equalTo:#"Math"];
[query findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
// results contains all the people who subscribe Math course
}];
I think it's better way to shape your tables in parse. Otherwise you need to create table for each relation.
Hope it helps also if you need more detail this can be helpful.
I have two classes User and Post. The User class has a userType field and I want to retrieve all of the posts from a given userType lets call them group x. In the Post class I have a pointer to the User class.
I was trying to do something like, first retrieve all user Ids for the type of user I want:
PFQuery *queryUser = [PFQuery queryWithClassName:kFTUserClassKey];
[queryUser whereKey:kFTUserTypeKey equalTo:kFTUserTypeX];
[queryUser whereKey:kFTUserLocationKey nearGeoPoint:nearGeoPoint withinMiles:miles];
[queryUser findObjectsInBackgroundWithBlock:^(NSArray *usersTypeX, NSError *error) {
if (!error) {
NSMutableArray *objectIds = [[NSMutableArray alloc] init];
// Add ambassador ids into query
for (PFObject *userX in usersTypeX) {
[objectIds addObject:[PFObject objectWithoutDataWithClassName:kFTUserClassName objectId: userX.objectId]];
}
}
}];
And then I wanted to query based on these objectIds but I am not sure how to query on this array or if this is even the correct way to do this. How can this be done?
Parse provides a matchesQuery method on query, so ...
PFQuery *innerQuery = [PFQuery queryWithClassName:#"User"];
[innerQuery whereKey:#"userType" equalTo:#"X"]; // fix with your real user type
PFQuery *query = [PFQuery queryWithClassName:#"Post"];
[query whereKey:#"user" matchesQuery:innerQuery];
[query findObjectsInBackgroundWithBlock:^(NSArray *posts, NSError *error) {
// posts are posts where post.user.userType == X
}];
I am writing an iOS app and I am using Parse to store data on the server side.
I have Users and each user can have a Car.
I am trying to figure out how to write a query that allows me to get all users that have a car with year less than 2000 and with a certain color (lets say red).
Each car has a relationship to the user and each user also has a relationship to their car.
User <-> Car (one to one)
I started using the PFQuery:
PFQuery * userQuery = [PFQuery queryWithClassName:#"User"];
I am not sure how to handle the relationship in the query. So, I'm pretty much not sure how to get this done.
Any suggestion?
First off, the User class is a special case, when using it in a query you need to do this:
PFQuery *query = [PFUser query];
Next, the way you construct the query you want depends where the pointer is. If the User has a car property that is a pointer to the Car then the query would be as follows:
PFQuery *userQuery = [PFUser query];
PFQuery *carQuery = [PFQuery queryWithClassName:#"Car"];
[carQuery whereKey:#"year" lessThan:#(2000)];
[carQuery whereKey:#"color" equalTo:#"red"];
[userQuery whereKey:#"car" matchesQuery:carQuery];
[userQuery includeKey:#"car"]
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error) {
for (PFObject *user in users) {
PFObject *car = user[#"car"];
// read user/car properties as needed
}
}];
If instead the Car class has a user property you just do a normal query and add the following line to let you access the full User object:
[carQuery includeKey:#"user"];
What does your table look like? If you have User as a column in your Car table, you can just query the car table for cars of year less than 2000 and then you would just access the User property of that query. It would look something like this:
PFQuery *carQuery = [PFQuery queryWithClassName:#"Car"];
[carQuery whereKey:#"year" lessThan:#(2000)];
[carQuery includeKey:#"user"];
[carQuery findObjectsInBackgroundWithBlock:^(NSArray *cars, NSError *error) {
if (!error) {
for (Car *car in cars) {
User *user = car#["user"];
}
}
}];
I have two parse tables POST and COMMENT.
I want to get all post where there are new comments. I am making checks based on viewedDate field in post table and createdAt field in Comment Table.
SELECT *
FROM POST p
INNER JOIN COMMENT c
ON p.objectId==c.pId and c.createdAt > p.viewedDate;
PFQuery *post = [ParsePost query];
[post findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
PFQuery *post = [ParsePost query];
PFQuery *comment = [ParseComment query];
//how add greater than for keys from other table?
//line below crashes
[comment whereKey:#"createdAt" greaterThan:[objects valueForKey:#"viewedDate"]];
[comment whereKey:#"post" matchesKey:#"objectId" inQuery:post];
[post whereKey:#"objectId" matchesKey:#"post" inQuery:comment];
[post findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
}];
}];
UPDATE
*This is how i do it currently*
PFQuery *postView = [ParsePost query];
[postView whereKey:#"author" equalTo:[ParseLigoUser currentUser]];
[postView findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
for (ParsePost *post in objects) {
PFQuery *comment = [ParseComment query];
[comment orderByDescending:#"createdAt"];
if (post.viewedDate) {
[comment whereKey:#"createdAt" greaterThan:post.viewedDate];
}else {
continue;
}
[comment whereKey:#"post" equalTo:post];
[comment countObjectsInBackgroundWithBlock:^(int number, NSError *error) {
if (number>0) {
if (!self.newposts) {
self.newposts = [NSMutableDictionary dictionary];
}
[self.newposts setObject:#"NOTSEEN" forKey:[post.objectId stringByAppendingString:#"Comment"]];
self.postCount += 1;
}
}];
}
}];
UPDATE 2 with relations
PFQuery *postView = [ParsePost query];
[postView whereKey:#"author" equalTo:[ParseLigoUser currentUser]];
[postView setLimit:100];
[postView findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
for (ParsePost *post in objects) {
PFRelation *rel = [post relationForKey:#"hasComment"];
PFQuery *query = [rel query];
[query orderByDescending:#"createdAt"];
if (post.viewedDate) {
[query whereKey:#"createdAt" greaterThan:post.viewedDate];
}else {
continue;
}
[[rel query] countObjectsInBackgroundWithBlock:^(int number, NSError *error) {
if (number>0) {
if (!self.newposts) {
self.newposts = [NSMutableDictionary dictionary];
}
[self.newposts setObject:#"NOTSEEN" forKey:[post.objectId stringByAppendingString:#"Comment"]];
self.postCount += 1;
}
}];
}
}];
Your problem isn't really the lack of join table queries on parse, but rather that you designed your database with a sql mindset instead of a NoSQL mindset (which of course is common for all who comes from the relation db world).
When designing your database "schema" for parse (or any other NoSQL database) you need to free yourself from thinking in relations and normalization etc to thinking in data access. How will you access your data? Start by thinking on how you will query your data, and then design your db to optimize for these queries. The most important for your mobile app is to minimize the number of connections to a remote server, and to minimize client side handling.
There are several ways to fix solve your current problem. You can, of course, create some queries that will work around the lack of join table queries, and handle some of this on the client. That could probably work short-term if this feature needs to be implemented quickly.
A long-term approach would be to redesign your schema to meet your requirement of easily retrieving posts that have new comments.
One solution (of several possible):
Create a new property in the Post class: “newcomments” that is a boolean.
Create a cloud code snippet that updates the newcomments property in Post whenever a new comment is created (sets it to TRUE).
This snippet should be run in the afterSave hook for Comment (https://parse.com/docs/cloud_code_guide#functions-aftersave)
Then, whenever you open a post to see the new comments, you reset this field to FALSE in the background.
Now you can query for posts where newcomments equalTo false
Or, instead of newcomments being a boolean, you could just as well use it for storing an array of pointers to the actual new comments (the afterSave hook updates this array with the pointer to the new comment). This way, you don’t need a second query for getting the new comments once you open the post. Here, you clear the newcomments property as soon as you’ve read the comments (or opened the post and have an array of the comments).
This storing of an array probably strikes a bad note in your SQL mindset, but this is one of many differences between SQL and NoSQL, since the latter is more focused on query efficiency than storage and consistency.
Or, if you don’t want to store this in the Post object, you could create a new PostTracker (or whatever) class to handle this. Maybe there are other things you would want to track (certainly, there might be in the future, even if you don’t have an idea for that at the moment).
I am using the Parse framework and have it set up where I can login and access the user who logs in PFUser object. I want to be able to visit a friends page and to do this i need to query and retrieve data (names and other pieces of information) of a PFUser object which is not the on that is logged in. I cannot find any queries that let me access data from the User class. I hope this makes sense,
Thanks
It really depends on what sort of criteria you want to search on, but you can query PFUsers just like you would any other PFObject within Parse. The only difference is that you create your query with the +[PFUser query] method. Here's an example from Parse's documentation:
PFQuery *query = [PFUser query];
[query whereKey:#"gender" equalTo:#"female"]; // find all the women
NSArray *girls = [query findObjects];
You can also query where a user is an associated object on some other class in Parse, and you can also handle results asynchronously:
PFQuery *query = [PFQuery queryWithClassName:#"Post"];
[query whereKey:#"user" equalTo:user];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
// do something with results
}];
Check out the query documentation for other examples of the sorts of things you can do with queries.