Swift: long query in Parse (OR/AND condition) - ios

I need to do a compound query with Parse SDK.
My code:
func retrieveObjects() {
let conditions: [String] = .....
let query: PFQuery = self.generateCompoundQueryFromConditions(conditions)
query.findObjectsInBackgroundWithBlock { (objs:[AnyObject]?,error: NSError?) -> Void in
NSLog("\(objs)")
}
}
private func generateCompoundQueryFromConditions(conditions: [String]) -> PFQuery {
var queries: [PFQuery] = []
for condition: String in conditions {
var query = PFQuery(className:"MyClassName")
query.whereKey("objectId", equalTo: condition)
queries.append(query)
}
return PFQuery.orQueryWithSubqueries(queries)
}
Conditions.count -> 24
Code works great, but I get this error:
[Error]: too many $or clauses (Code: 154, Version: 1.7.4)
Is there a way to do a query with many OR conditions?
update
I try also using NSPredicate:
private func generateQueryWithPredicateFromConditions(conditions: [String]) -> PFQuery {
var predicates: [NSPredicate] = []
for condition in conditions {
predicates.append(NSPredicate(format: "objectId = %#", condition))
}
let predicate = NSCompoundPredicate.orPredicateWithSubpredicates(predicates)
return PFQuery(className: "MyClassName", predicate: predicate)
}
Now error is:
'This query is too complex. It had an OR with >4 subpredicates after normalization.'
So, with N-OR condition, I'm going to do N / 4 + N % 4 PFQuery and then merge results. Could be a solution?

I solved by my own in this way, hope can help someone:
let query = PFQuery(className: "MyClassName")
query.whereKey("objectId", containedIn: conditions)
query.findObjectsInBackgroundWithBlock { (objs:[AnyObject]?,error: NSError?) -> Void in
NSLog("\(objs)")
}

Related

orQueryWithSubqueries Counting All Objects

When trying to use a multitude of queries I'm getting all the results, its not actually filtering any data whatsoever. I have tried multiple ways to perform this and some reason I cannot get it to only show what I'm looking for.
func getOpenWorkOrders() {
let query1 = WorkOrders.query()!
query1.whereKey("status", equalTo: "New")
let query2 = WorkOrders.query()!
query2.whereKey("status", equalTo: "In Progress")
let query3 = WorkOrders.query()!
query3.whereKey("status", equalTo: "On Hold")
let query4 = WorkOrders.query()!
query4.whereKey("status", equalTo: "Assigned")
let query5 = WorkOrders.query()!
query4.whereKey("status", equalTo: "Ready To Bill")
let queries = PFQuery.orQueryWithSubqueries([query1, query2, query3, query4, query5])
queries.countObjectsInBackgroundWithBlock { (number : Int32, error : NSError?) in
if error == nil {
self.openWorkOrdersNumber.text = String(number)
}
}
}
is there something I am missing here?
This is the same way I'm doing it in the rest of my app

How do i set value for all objects in background? [parse.com]

Heres my code so far:
var query2 = PFQuery(className: "Post")
query2.whereKey("alias", equalTo: self.currentname)
query2.setValue(self.usernametextfield.text, forKey: "alias")
query2.findObjectsInBackgroundWithBlock { (objects, NSError) -> Void in
for objects in objects {
}
}
}
I want to set objects["alias"] equal to a string for all the objects retrieved from the query. How do I do this?

How can I get more than 1000 results from a Parse query?

I've been trying to chain to no success. After an initial query, I attempt to count the results. Then, I check to see if the results are equal to the limit of the query. If they are the same, I create a new query and repeat.
All I'm getting is the results of the first query, however.
Code is here:
var allObjects = [PFObject]()
var skip = 0
var limit = 10
var downloadCards = PFQuery(className: "Checklist")
downloadCards.whereKey("createdBy", equalTo:PFUser.currentUser()!)
downloadCards.includeKey("card")
downloadCards.orderByAscending("IndexNumber")
downloadCards.limit = limit
downloadCards.skip = skip
downloadCards.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
if let card = object["card"] as? PFObject {
allObjects.append(card)
}
}
}
//1000
if objects!.count == limit {
//Query again until results aren't equal to limit
skip = skip + limit
var downloadCards2 = PFQuery(className: "Checklist")
downloadCards2.whereKey("createdBy", equalTo:PFUser.currentUser()!)
downloadCards2.includeKey("card")
downloadCards2.orderByAscending("IndexNumber")
downloadCards2.limit = limit
downloadCards2.skip = skip
downloadCards2.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
if let card = object["card"] as? PFObject {
allObjects.append(card)
}
}
}
You can set the "skip" variable to 1,000, 2,000, etc. as you get more results up to 10,000 skips. After that, you can follow the directions here: https://parse.com/questions/paging-through-more-than-10000-results
Edit:
Apologies - I might have misread your question.
1) You should rename the variables in your second loop to objects2, error2, card2, etc. similar to downloadCards2.
2) Alternatively, to make your code scalable and DRY, I would make allObjects, skip, and limit properties and simply re-run the same query again.
func runQuery() {
var downloadCards = PFQuery(className: "Checklist")
downloadCards.whereKey("createdBy", equalTo:PFUser.currentUser()!)
downloadCards.includeKey("card")
downloadCards.orderByAscending("IndexNumber")
downloadCards.limit = limit
downloadCards.skip = skip
downloadCards.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects as? [PFObject] {
for object in objects {
if let card = object["card"] as? PFObject {
allObjects.append(card)
}
}
}
if objects!.count == limit {
skip = skip + limit
self.runQuery()
}
}
}
}

Post rating system in iOS with parse.com - upvote / downvote

Simply: I want rating system of the post (or image) that works the same as in Yik Yak or 9gag - user can upvote or downvote only once the post. I dont want to store these data on device but everything on parse.com. I also would like to count the comments. Ideally do everything in only one query to parse.com
I am using currently this code but I dont think this is the good approach how to do it because for every image it will create a new query. Currently I have 4 tables in Parse.com User, Image, Comment, Rating and everything is connected with pointers to the image or user.
func loadData(){
forPaginationStart = 0
numberOfImagesPerPage = 5
imageData.removeAllObjects()
var findImageData: PFQuery = PFQuery(className: "Image")
findImageData.whereKey("deleted", equalTo: 0)
findImageData.orderByDescending("createdAt")
findImageData.skip = forPaginationStart
findImageData.limit = numberOfImagesPerPage
findImageData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]?, error:NSError?)->Void in
if error == nil{
for object in objects! {
let image:PFObject = object as! PFObject
self.imageData.addObject(image)
//comment counts
var countComments = PFQuery(className:"Comment_image")
countComments.whereKey("image_id", equalTo: PFObject(withoutDataWithClassName: "Image", objectId: "\(image.objectId!)"))
countComments.whereKey("deleted", equalTo: 0)
countComments.countObjectsInBackgroundWithBlock {
(count: Int32, error: NSError?) -> Void in
if error == nil {
println("There is \(count) comments for image: \((object.objectId!)!)")
self.commentCount.append(countComments.countObjects())
}
}
}
self.tableView.reloadData()
}
}
//count number of records in DB for pagination end
var totalNumOfRows: PFQuery = PFQuery(className: "Image")
totalNumOfRows.whereKey("deleted", equalTo: 0)
totalNumOfRows.countObjectsInBackgroundWithBlock {
(count: Int32, error: NSError?) -> Void in
if (error == nil) {
self.totalNumRecords = Int(count)
println("total records: \(self.totalNumRecords)")
}
}
}
I had it working with php and SQL but with parse the same approach doesnt work for me... I asked the DB what is the value for that user, if it was 1 the user upvoted and if negative one he downvoted, 0 neither one and I set the button according to the value
SQL query:
IFNULL((SELECT value FROM rating WHERE image.id = rating.image_id AND rating.user_id = (SELECT id FROM user WHERE uuid='$uuid')), 0) as UserRating
- (instancetype)whereKey:(NSString *)key matchesKey:(NSString *)otherKey inQuery:(PFQuery *)query
You can use this method on PFQuery, for example:
var countComments = PFQuery(className:"Comment_image")
var findImageData: PFQuery = PFQuery(className: "Image")
findImageData.whereKey("deleted", equalTo: 0)
findImageData.orderByDescending("createdAt")
findImageData.skip = forPaginationStart
findImageData.limit = numberOfImagesPerPage
countComments.whereKey("image_id", matchesKey:"objectId", inQuery: )
countComments.whereKey("deleted", equalTo: 0)
countComments.countObjectsInBackgroundWithBlock {
(count: Int32, error: NSError?) -> Void in
if error == nil {
println("There is \(count) comments for all images!")
}
}
By setting up the count query like this, you don't have to call count for each image, but I don't know in the background if Parse optimizes to make it faster than your current query.

Use multiple contains functions in if statement (Swift)

Is there a way to use multiple "contains(array, value)" functions in an if statement? I have a query that stores in the results in an array. If the results are nil I perform a set of operations. If, however, the array is not nil I'd like to check to see if certain objects appear in it:
var user: PFUser?
override func viewDidLoad() {
super.viewDidLoad()
//get the two players
if let user = user{
var userQuery = PFQuery(className: "Game")
userQuery.whereKey("user1", equalTo: user)
userQuery.whereKey("isActive", equalTo: true)
var userQuery2 = PFQuery(className: "Game")
userQuery2.whereKey("user2", equalTo: user)
userQuery2.whereKey("isActive", equalTo: true)
var currentUserQuery = PFQuery(className: "Game")
currentUserQuery.whereKey("user1", equalTo: PFUser.currentUser())
currentUserQuery.whereKey("isActive", equalTo: true)
var currentUserQuery2 = PFQuery(className: "Game")
currentUserQuery2.whereKey("user2", equalTo: PFUser.currentUser())
currentUserQuery2.whereKey("isActive", equalTo: true)
var query = PFQuery.orQueryWithSubqueries([userQuery, userQuery2, currentUserQuery, currentUserQuery2])
query.findObjectsInBackgroundWithBlock{
(results: [AnyObject]!, error: NSError!) -> Void in
if error == nil{
//if there are no active games, start new game
if results == nil{
//start game code
} else if contains(results, user) as [AnyObject]! && contains(results, PFUser.currentUser()) as [AnyObject]! {
println(results)
}
}
}
}
}
Using the && operator is returning an error on the line: Cannot invoke '&&' with an argument of type '([AnyObject]!,[AnyObject]!)'. Any ideas on how I can test to see if the user and PFUser.current_user() objects are found in the array?
Thanks!
Problem is that you are trying to cast result of contains func call which is Bool in to array of AnyObjects's.
What you need to do instead is just to compare results of contains(results, user) and contains(results, PFUser.currentUser()) functions calls. But it wont compile.
In order to make your compiler happy you need to cast results array to array of [PFUser]
See code:
if let users = results as? [PFUser] {
if contains(users, user) && contains(users, PFUser.currentUser()) {
println(users)
}
}

Resources