NSPredicate: search for an array of values in relation, NOT - ios

I'm a bit lost, and I hope you can help me.
I have two arrays
let stars = ["Paul", "Ringo"]
let visitors = ["bob", "mary"]
Then I have Core Data entities Rockstar and Person. And a one-to-many relation fans between the two.
Now I want to find a couple of specific Rockstars, and make sure that they don't have visitors as fans.
I try to do that with a compound predicate, roughly like this:
let starsPredicate = NSPredicate(format: "id IN %#", stars)
let fansPredicate = NSPredicate(format: "NOT (fans.personid CONTAINS %#)", visitors)
and finally
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [starsPredicate, fansPredicate])
I'm afraid this results in two questions:
What is the correct syntax for the fansPredicate? It works fine with one value, but it crashes on an array
Is this possible with a compound predicate at all? I think if the Person entity is empty, I get zero records from the compoundPredicate.

This can be achieved with a “SUBQUERY”:
let fansPredicate = NSPredicate(format: "SUBQUERY(fans, $f, $f.personid IN %#).#count = 0",
visitors)
The predicate is true for all Rockstar objects which have no related Person object whose personId is in the given list.

Related

NSPredicate for Nested Relationships (many to many) iOS

I've been struggling for several hours and thought I'd reach out to SO. In my app's data model, I have an entity, SubCategory, which has many CreatedTask, and each CreatedTask can have many User:
Say I have an User entity variable, called userObject.
I want to fetch all SubCategories, where tasks.assignees has userObject. How do I do this?
Here is what I've tried so far:
let userFetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "SubCategory")
let predicate = NSPredicate(format: "%# IN tasks.assignees", userObject)
fetchedResultsController.fetchRequest.predicate = predicate
However I am generating a nasty error:
unimplemented SQL generation for predicate.
Any help would be greatly appreciated.
Full Error Output:
Thanks to yan's help I was able to figure it out, just had to place the user object into an array.
let users: [User] = [userObject]
let predicate = NSPredicate(format: "SUBQUERY(tasks, $m, ANY $m.assignees IN %#).#count > 0", users)

NSPredicate matching string for Cloudkit

I need to pull the record from cloudkit matching a string.
I have a User record type, with an email field. I have multiple records with the same email, but I can't get the predicate to get me the records.
I've tried all of these:
let predicate = NSPredicate(format: "email = 'julio_ukohgsp_chevez#tfbnw.net'")
NSPredicate(format: "email == %#", argumentArray: [email])
NSPredicate(format: "email IN %#", [email])
NSPredicate(format: "email contains %#", email)
NSPredicate(format: "email = %#", email)
NSPredicate(format: "email == %#", email)
My query specifies the record type:
let query = CKQuery(recordType: "User", predicate: NSPredicate(format: "email == %#", email))
When I do a predicate with value: true, I get all records including the one I want.. I know for sure I have a User with that email, multiple in fact..
What am I missing?
Edit ..
let query = CKQuery(recordType: "User", predicate: NSPredicate(format: "email BEGINSWITH %#", email))
Does work and brings back records, but I want an exact match!
I had same issue and was able to resolve it by adding Queryable index on user field in my case. Note, after index was added I needed to write data again to CloudKit to be able to perform query.
Code for creation of predicate:
let user = "test-user"
let userPredicate = NSPredicate(format: "user == %#", user)
I've been having this same issue with one of my apps. In my case, I needed to subscribe to a list of predicates where a lot of search criteria needed to be met but the search or filter predicate gave me the most trouble. I also tried:
let word1 = "Mac"
let predicate = NSPredicate(format: "Title CONTAINS %#", word1)
This worked but only when 'word1' was a single word with no spaces and 'Title' was a 'String List' in the cloud.
The clarification that I have come across in all my research is:
You need to use the term 'self' when you create a predicate and what that does is it searches all the fields that are a searchable string. Here is the information I got from Apple's Website.
Predicates support the following variable substitution strings:
Use %# for value objects such as strings, numbers, and dates.
Use %K for the name of a field. This substitution variable indicates that the substituted string should be used to look up a field name.
With one exception, the CONTAINS operator can be used only to test list membership. The exception is when you use it to perform full-text searches in conjunction with the self key path. The self key path causes the server to look in searchable string-based fields for the specified token string. For example, a predicate string of #"self contains 'blue'" searches for the word “blue” in all fields marked for inclusion in full-text searches. You cannot use the self key path to search in fields whose type is not a string.
What I ended up doing, which there has to be a better way, is my 'Title' property is still a 'String List' in the cloud and in my code I made an array of all the words in my searchTerm and with each word in the searchTerm I made a separate NSPredicate and I used those as part of a NSCompoundPredicate. I then added the NSCompoundPredicate to my subscription and it worked. Here is my example:
var searchCriteria: [NSPredicate] = []
for word in searchTerm {
let predicate = NSPredicate(format: "self CONTAINS %#", word)
searchCriteria.append(predicate)
}
let p2 = NSPredicate(format: "Price > %d", minPrice)
let p3 = NSPredicate(format: "Price < %d", maxPrice)
let p4 = NSPredicate(format: "Category == %d", category)
searchCriteria.append(p2)
searchCriteria.append(p3)
searchCriteria.append(p4)
let compoundPredicate = NSCompoundPredicate(andPredicateWithSubpredicates: searchCriteria)
I hope this helps. There has to be a better way to do this and if anyone knows, please let me know cause I understand there isn't a lot of good explanations on how to do things with CloudKit.

Realm subquery with List count in Swift

I have
class Car : Object
{
let queries : List<QueryModel> = List<QueryModel>()
}
and
class QueryModel : Object
{
let location : String = ""
}
Now how do I write that NSPredicate to fetch just that cars which have queries with location equal to 'London'?
p.s. It's a known fact that things like #count do not work on Swift List class...
p.p.s. I need it as a predicate because this is the only way RBQFetchedResultsController knows how to do it's job.
How I thought it could've been written was something like NSPredicate(format: "SUBQUERY(queries, $query, $query.location == %#)", 'London'). But nothing like this ever passes Realm's predicate validation :)
You can write
NSPredicate(format: "ANY queries.location = %#", locationString)
If you want to add other filtering, you can create other NSPredicate and then use
NSCompoundPredicate(andPredicateWithSubpredicates: [pred1, pred2])
to combine the filters.
Now, if you want to filter cars, you have to prepare a
let request = FetchRequest<Car>(realm: realm, predicate: predicate)
then you can use RBQFetchedResultsController to execute the request.

CloudKit - NSPredicate for finding all records that contains multiple CKReferences in a reference list

I'm working on Joke app that uses CloudKit
Each joke has a reference list to some categories/tags.
I'm trying to query all jokes that has some specific tags. For instance I want to find all jokes that is in the categories of Animal and Doctor.
Right now I have tried with the following code
let tagRecords = tags.map { CKReference(record: $0.record, action: .None) }
let predicate = NSPredicate(format: "tags CONTAINS %#", tagRecords)
let query = CKQuery(recordType: "Jokes", predicate: predicate)
Basically what the above does is first of all creating an array of references and then make a predicate to find the tags the contains those references
Unfortunately this doesn't work
I get the following error
server message = "Internal server error""
So the question is: How do you find all records that contains all references in a reference list?
Assuming that tags is a relationship or an array of strings, you could try with the following predicate:
let predicate = NSPredicate(format: "ANY tags IN %#", tagRecords)
Hope this helps.

Realm query to exclude results from relations

I have
class Person: Object {
let friends = List<Person>()
let family = List<Person>()
}
I have person instance, which includes links to some other persons in person.friends list.
And I want to query all other Person objects, not including person.friends and person.
I can make two for in loops to check if the query doesn't contain persons from the list, but it seems like not the best way to do that.
P.S. In CoreData I did it with predicate:
let predicate = NSPredicate(format: "SELF != %# AND NOT SELF IN %#",person, person.friends),
But Realm gives me an error:
Predicate expressions must compare a keypath and another keypath or a
constant value
.
Unfortunately, this predicate is currently unsupported in Realm -- you can follow https://github.com/realm/realm-cocoa/issues/1328 for updates.

Resources