Two constraints on one key in a Parse Query - ios

I'm using Parse and my app is written in Swift. I have a golf app that allows a user to friend other users. The users can log their golf scores and then see their golf scores and their friend's scores in a leaderboard style tableViewController.
The problem I'm having is that Parse doesn't support two constraints on the same key in a query. I have a function that queries the users and their scores and stores them in an array of tuples (leadeboardData). I'm trying to querying the current user's PFRelation as well as the current user (variable "friendsRelation" and constant "friendQuery"). The class I query is the "GolfScorecard", which is where the scores are stored on Parse. I then called the "whereKey" method on the "golfer" key, which is where my user is stored under on Parse. I call "whereKey matchesQuery" for my friendsRelation query and then "whereKey equalTo" to try and get my current user. I then "includeKey" "golfer" so I can get the user info along with the score info. Any suggestions on how to go about this? I'm trying to do it all in one query but when the "whereKey" method is called on the same key ("golfer") the last one overrides the first one, which makes it only possible to get the friend info or the current user info but not both. I'm stumped how to include both. Thanks in advance.
Here is my function I call to make the query:
func loadLeaderboardData() {
leaderboardData.removeAll()
friendsRelation = PFUser.currentUser()?.objectForKey("friendsRelation") as? PFRelation
friendsRelation = PFUser.currentUser()?.objectForKey("friendsRelation") as? PFRelation
let friendQuery = friendsRelation?.query()
let query = PFQuery(className: "GolfScorecard")
query.whereKey("golfer", matchesQuery: friendQuery!)
query.whereKey("golfer", equalTo: PFUser.currentUser()!)
query.includeKey("golfer")
query.orderByAscending("score")
query.findObjectsInBackgroundWithBlock { (scoreCards: [PFObject]?, error: NSError?) -> Void in
if error == nil {
for object:PFObject in scoreCards! {
let golfer:PFObject = object["golfer"] as! PFObject
self.leaderboardData.append(object,golfer)
dispatch_async(dispatch_get_main_queue()) {
self.leaderboardTableView.reloadData()
}
}
} else {
print(error)
}
}
}

Thanks Paulw11 the orQueryWithSubQueries worked. The only issue it ended up presenting was my app was crashing with an error stating 'Key "username" has no data. Call fetchIfNeeded before getting its value.'...I don't think the Parse Query was coming back in time to load in the tableView that I was putting the data in. I ended up using the 'fetchIfNeededInBackgroundWithBlock' method inside of the cellForRowAtIndexPath call around the code where I was calling the 'username' data at. This allowed it to grab the data in the background if it didn't come back in time. This seemed to due the trick.
-Also I subclassed my queries, which is where the GolfScorecard & GolferProfile is coming from inside of my for-in loop.
-This is my new compound Query:
func loadLeaderboardData() {
leaderboardData.removeAll()
friendsRelation = PFUser.currentUser()?.objectForKey("friendsRelation") as? PFRelation
let friendQuery = friendsRelation?.query()
let friendScorecardQuery = PFQuery(className: "GolfScorecard")
friendScorecardQuery.whereKey("golfer", matchesQuery: friendQuery!)
let currentUserScorecardQuery = PFQuery(className: "GolfScorecard")
currentUserScorecardQuery.whereKey("golfer", equalTo: PFUser.currentUser()!)
let subQuery = PFQuery.orQueryWithSubqueries([friendScorecardQuery, currentUserScorecardQuery])
subQuery.orderByAscending("score")
subQuery.findObjectsInBackgroundWithBlock { (scoreCards: [PFObject]?, error: NSError?) -> Void in
if error == nil {
for object:PFObject in scoreCards! {
if let object = object as? GolfScorecard {
let golfer = object["golfer"] as! GolferProfile
self.leaderboardData.append(object,golfer)
dispatch_async(dispatch_get_main_queue()) {
self.leaderboardTableView.reloadData()
}
}
}
} else {
print(error)
}
}
}

Related

using a friend system in ios parse swift

I created a query with subquery
var userInitiated = PFQuery(className: "friends")
userInitiated.whereKey("friender", equalTo: PFUser.currentUser()!.username!)
var friendInitiated = PFQuery(className: "friends")
friendInitiated.whereKey("friendee", equalTo: PFUser.currentUser()!.username!)
// find friends of user
let friendQuery = PFQuery.orQueryWithSubqueries([userInitiated, friendInitiated])
friendQuery.findObjectsInBackgroundWithBlock ({ (objects:[PFObject]?, error:NSError?) in
if error == nil {
// cleanup
self.friendArray.removeAll(keepCapacity: false)
// STEP 2 Hold Recieved Data
// find objects that you queried for
for object in objects! {
self.friendArray.append(object.valueForKey("-----") as! String)
}
}
})
In the for object in objects part - I want to append the usernames that I got from the query but I only want to add the ones that aren't the current users username, how would I do that?
A is current User, B is one of A's friends. So what the (friender, friendee) pair in your "friends" class?
Both (A,B), (B,A)
one of (A,B) and (B,A)
In first case, you can just query friender equalTo currentUser
In second case, your query seems ok.

Parse PFQuery bug with geoPoint constraint

I am doing a pretty straightforward PFQuery. I want to get any objects where the specific key I'm entering exists, and the object is within 2 km of user location. However, this query returns all objects within 2 km, but doesn't filter out anything to only return objects where that key exists. I checked and the key ("T(objectIDs[0])") contains the information I think it does. Additionally, I have tried this in a compound query where I am doing a different key in each query. This query works as expected and returns objects within 2 km AND only where the key exists. Have any of you seen behavior like this? Any insight is greatly appreciated, thanks!
let singleQuery = PFQuery(className: locationTagsClassNameConstant)
singleQuery.whereKeyExists("T\(objectIDs[0])")
singleQuery.whereKey(geoPointColumnNameConstant, nearGeoPoint: userGeo, withinKilometers: 2)
singleQuery.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if error == nil {
var dataToExtract:Array<AnyObject> = []
if let objects = objects {
for activity in objects {
dataToExtract.append(activity)
}
complete(result: dataToExtract)
}
} else {
// Log details of the failure
print("Error++++: \(error!)")
}
})

Swift - load from Parse table based on array values

I have the following flow in my app:
User selects to view his contacts list
I go and load from the FriendsConnection table the array of friends (usernames) that match the current user logged (basically friends of current user). Store in array
Another database query to the Users class in Parse to get the information of related to each username in the array stored locally. Store this locally
Populate Table View with contact list and once clicked show details.
For step 2 above, I have:
var friendsUsernamesListArray: [String] = []
func loadFriendsUsernames()
{
let query = PFQuery(className:"FriendsConnections")
query.whereKey("appUsername", equalTo:PFUser.currentUser()!["appUsername"])
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil{
//print("Successful")
//print(objects)
self.friendsUsernamesListArray = objects![0]["friendsList"] as! NSArray as! [String]
}else{
print("Failure")
}
}
}
and now the values of friendsUsernamesListArray are:
["username1", "username2", "username3"]
I am not sure how to do the third part to get one major object in return with one request (to avoid a single request for each username in the array above). What I want is probably to get something like this:
User1 -> [name: XXXXX, age: 20, number:019282992] User2 -> [name:
YYYY, age: 22, number:045527229]
Thanks for your help in advance
Found the answer and sharing it for future reference for similar issues:
The solution is to use containedIn keyword as provided by Parse PFQuery.
func loadFriendsInfo()
{
let query : PFQuery = PFUser.query()!
query.whereKey("appUsername", containedIn: self.friendsUsernamesListArray)
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil{
print(objects)
}else{
print("Getting Usernames Info Failure")
print(error)
}
}
}
The above code works and happy to answer any questions related to it.
You could also try this (slightly similar to the previous answer)
let query = PFQuery(className: "Users")
query.findObjectsInBackgroundWithBlock{(objects, error) -> Void in
if objects.count > 0 {
for object in objects {
for friend in self.friendsUsernamesListArray {
if object["username"] == friend {
//get objects info and add it to other things. E. g. object["email"]
}
}
}
}
}
Let me know if this works. You may need to add some question/ exclamation marks for optionals. Hope this helps.

Parse backend: key won't increment?

I'm trying to increment a number in my parse table under the column "votes". Here's my code:
func upVote() {
var reviewQuery: PFQuery = PFQuery(className: "reviews")
reviewQuery.whereKey("content", equalTo: reviewTextView.text)
reviewQuery.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]!, error:NSError!)->Void in
if error == nil{
for object in objects{
println(object)
let review:PFObject = object as! PFObject
review.incrementKey("votes", byAmount: 1)
}
}
}
}
When I print the object in the console I can see that it is the correct object that I'm looking for. It looks like this:
<reviews:ZqgSVL1Tsd:(null)> {
content = "njk\n";
reviewer = "<PFUser:6387CJtYI1>";
votes = 1;}
But when I look at my parse end, the number of votes has not changed. What am I doing wrong?
Save the object with
review.saveInBackground()
after incrementing the key.
After you modify an object, however small the modification, you must save it after. You are not saving your changes to the object review.
You have several options for saving, including save(), saveInBackground(), saveEventually(), and more. See the documentation for PFObject for more information:
https://www.parse.com/docs/ios/api/Classes/PFObject.html#//api/name/save
For example, you could save the object synchronously with
review.save() and you could save the object asynchronously with review.saveInBackground().

Querying the User table with a users objectId queried from another table

I'm trying to query my User table with the Users objectId queried from another table.
Here's my code:
func queryFriendsTable() {
var queryFriends = PFQuery(className: "Activity")
queryFriends.whereKey("type", equalTo: "friend")
queryFriends.whereKey("fromUser", equalTo: PFUser.currentUser()!)
queryFriends.includeKey("toUser")
var queryUserTable = PFUser.query()
queryUserTable!.whereKey("objectId", matchesKey: "toUser", inQuery: queryFriends)
queryUserTable!.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
self.friendNamesArray.addObject(object["username"]!)
println(self.friendNamesArray)
}
}
}
}
}
Nothing is being returned when I run this query. I think the problem has to do with the fact that the toUser objectId in the Activity table is a pointer and not a string. (The toUser value I try to use in the matchesKey spot is a pointer)
So how can I get the objectId as a string from a pointer object using the inQuery method?
Your suspicions are correct. Parse won't find pointers when looking through the User's class. Instead what you'll need to do is create a string variable and set it equal to the result of queryFriends. So what that means is you'll have to run that query first, get the pointer back, and access it's objectId field as a string in order to use it in your following query.

Resources