0
votes

Is it possible to use "NOT IN" in a NSPredicate with a SUBQUERY?

In a 'normal' NSPredicate, this works:

NSPredicate(format: "NOT (uid IN %@)", uids)

However, I'm trying this "NOT IN" syntax in a NSPredicate with SUBQUERY and it doesn't work (but doesn't crash either):

NSPredicate(format:"SUBQUERY(forests.treeFamily, $tree, NOT ($tree._id IN %@)) .@count > 0", arrayOfTreesIds)

While the following "IN" syntax works fine:

NSPredicate(format:"SUBQUERY(forests.treeFamily, $tree, $tree._id IN %@) .@count > 0", arrayOfTreesIds)

Any idea how I could achieve this?


UPDATED

The answer I got from Jay made me think I wasn't clear enough on the schema/class.

So let's make an example with dogs.

We have a ShelterClass, that has a property of Dogs. It's an array of Dog objects, to keep tracks of which dogs are hosted in the shelter.

The Dog class has a "race" property, which is a reference to a Race class.

I want to filter Shelters that DON'T have certain races of dogs. While I would use this to filter Shelters that DO have certain races of dogs:

NSPredicate(format:"SUBQUERY(dogs.race, $race, $race._id IN %@) .@count > 0", arrayOfRaceIds)

I can't find how to filter out / use a "NOT IN" syntax.

1
Thanks for the clarification. If I am reading it correctly, see my updated answer. - Jay

1 Answers

1
votes

Super easy! Here's the format for a regular filter

If you have a DogClass

class DogClass: Object {
   @objc dynamic var name = ""
}

and then want a list of dogs that are not named Dino and Fido, here's the code

 let dogNames = ["Fido", "Dino"]
 let results = realm.objects(DogClass.self).filter("NOT dog_name IN %@", dogNames)

The result of a subquery is going to be dependent on what result you expect and what the subquery is for.

For example, let's say we have a PersonClass that has property of dogs, which is a list of dogs they know. If we want all persons that do not know Fido or Dino, this is the query

let personResults = realm.objects(PersonClass.self).filter("NOT ANY dogs.dog_name in %@", dogNames)

EDIT

Based on an updated question, let's try this. Since I used a PersonClass in the above I will pose this question.

I want a list of all of the people (Shelters) that do not have a breed (race) of Hound. Here's the Breed class to track the breeds

class BreedClass: Object {
    @objc dynamic var breed = ""
}

and the DogClass that has a breed property (like 'Race' in the question)

class DogClass: Object {
    @objc dynamic var dog_id = NSUUID().uuidString
    @objc dynamic var dog_name = ""
    @objc dynamic var dog_breed: BreedClass?

    override static func primaryKey() -> String? {
        return "dog_id"
    }
}

and then finally the Person class that has a List of DogClass objects

class PersonClass: Object {
    @objc dynamic var person_id = UUID().uuidString
    @objc dynamic var first_name = ""

    let dogs = List<DogClass>()

    override static func primaryKey() -> String? {
        return "person_id"
    }
}

Then we have some populated breed objects

let b0 = BreedClass()
b0.breed = "Mut"

let b1 = BreedClass()
b1.breed = "Poodle"

let b2 = BreedClass()
b2.breed = "Hound"

and then add breeds to the dogs and add the dogs to the persons. In this case we're only going to have one dog that's a b2, Hound

let d2 = DogClass()
d2.dog_name = "Sasha"
d2.dog_breed = b2

In this case I added 4 people, Bert, Ernie, Grover and The Count. Ernie was the only person I added the hound to.

Finally a query that will return all people that do NOT have a breed of Hound.

let breed = "Hound"
let personResults = realm.objects(PersonClass.self).filter("NOT ANY dogs.dog_breed.breed == %@", breed)
for person in personResults {
    print(person.first_name)
}

and the output

Bert
Grover
The Count

And Ernie is missing because he has a Hound.