Realm + swift sorting - iphone

Wondering if anyone have faced & solved this issue:
Description
I have 2 Classes called Person & Dog where a person can have more than one dog.
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
class Person: Object {
dynamic var name = ""
dynamic var picture: NSData? = nil // optionals supported
let dogs = List<Dog>()
}
According to my requirement, I need to fetch all objects from person.name == "ABC" & apply sort all dogs by Name.
Snippet:
let realm = try! Realm()
let result = realm.objects(Person.self).filter("name == %#","ABC").sorted(property : "name")
From the above snippet, sorting is done based on the person Name not on the dog's Name.
How to create a filter & sort the list by dog name?

realm.objects(Person.self) returns Results<Person>. Calling sorted() to Results<Person> means sort Person. If you'd like to sort dogs, you can retrieve element of person first, (e.g. let person = result[0]), then retrieve dogs property, call sorted() method to it.
let people = realm.objects(Person.self).filter("name == %#","ABC")
let person = people.first!
let sortedDogs = person.dogs.sorted(property : "name")

Related

Swift NSPredicate SUBQUERY with NOT IN?

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.
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.

Filter querying multiple objects from Realm using List of Primary Keys

I'm trying to query multiple objects from Realm using a List of Primary Key Strings. I know I can do this using a for..in loop but I'd rather use a filter if possible.
primaryKeyArray contains a number of Strings
class Item : Object {
#objc dynamic var itemKey = NSUUID().uuidString
}
var primaryKeyArray : List<String>?
//Assume Realm DB already contains multiple Item Objects
//primaryKeyArray contains "key1", "key2", "key3", etc..
let predicate = NSPredicate(format: "itemKey == %#", primaryKeyArray)
let items = realm.objects(Item.self).filter(predicate)
I know the problem is with my predicate format. Not sure whether to use some form of CONTAINS or what? Any help with the predicate syntax would be greatly appreciated!
I think you are asking how to query Realm for items that have keys that match a set of keys in an array.
So given a DogClass Realm Object
class DogClass: Object {
#objc dynamic var dog_id = NSUUID().uuidString
#objc dynamic var dog_name = ""
override static func primaryKey() -> String? {
return "dog_id"
}
}
and suppose we know we want to retrieve three dogs that match some given primary keys
let keysToMatch = ["302AC133-3980-41F3-95E8-D3E7F639B769", "54ECC485-4910-44E5-98B9-0712BB99783E", "71FE403B-30CD-4E6C-B88A-D6FDBB08C509"]
let dogResults = realm.objects(DogClass.self).filter("dog_id IN %#", keysToMatch)
for dog in dogResults {
print(dog.dog_id, dog.dog_name)
}
Note the use of IN in the filter, which will match any dogs with id's in the given array.
You can also pass in a Realm List Object instead of a Swift array and get the same result.
let listOfKeysToMatch = List<String>()
listOfKeysToMatch.append("302AC133-3980-41F3-95E8-D3E7F639B769")
listOfKeysToMatch.append("54ECC485-4910-44E5-98B9-0712BB99783E")
listOfKeysToMatch.append("71FE403B-30CD-4E6C-B88A-D6FDBB08C509")
let dogResults2 = realm.objects(DogClass.self).filter("dog_id in %#", listOfKeysToMatch)
for dog in dogResults2 {
print(dog.dog_id, dog.dog_name)
}
let predicate = NSPredicate(format: "itemKey IN %#", primaryKeyArray)

realm predicate with an object inside object

I have the following Realm Objects
class Patient: Object {
#objc dynamic var name: String?
let list = List<RString>()
}
class RString: Object {
#objc dynamic var stringValue: String?
}
I need to filter Patient objects that have an RString component in List with stringValue = "test"
Is something like this possible?
patients = realm?.objects(Patient.self).filter("name = 'name1' AND #% IN list", RString(stringValue: 'test'))
You need to use a SUBQUERY to be able to access the properties of the elements of a List in an NSPredicate. The SUBQUERY will evaluate true for every Patient whose list property includes at least 1 RString element whose stringValue matches the provided String.
patients = realm?.objects(Patient.self).filter("name = %# AND SUBQUERY(list,$element,$element.stringValue == %#).#count>0", "name1", "test")

NS Predicate search for "do not have any"

I have a simple data model:
class Dog: Object {
dynamic var Name : String = ""
let Colour = List<ColourItem>()
}
class ColourItem: Object {
dynamic var colourName = ""
}
And I can do NSPredicate searches for all Dogs, or Dogs with a specific colour assigned to them:
let importPredicate = NSPredicate(format: "%# IN Colour.colourName", colourVar)
let results = realm.objects(Dog).filter(importPredicate)
But I am stuck on how to search for just Dogs without any colour assigned to them. (i.e.: I want to filter for dogs whose Colour property is empty).
Any ideas/hints?
You can query for dogs with an empty Colour list like so:
let results = realm.objects(Dog.self).filter("Colour.#count == 0")

Filtering Realm objects with Swift

I always get the following error when trying to filter my Realm database using NSPredicate:
Property 'text' is not a link in object of type 'getType'
I want to filter my Realm database to show only the items that have some specific text in them. This is what I've tried:
let realm = try! Realm()
let predicate = NSPredicate(format: "typez.text.filter = 'special'")
let filterThis = realm.objects(Publication).filter(predicate)
print(filterThis)
The relevant portion of my model classes is:
class Publication: Object, Mappable {
dynamic var id: Int = 0
var typez = List<getType>()
dynamic var url: String?
}
class getType: Object, Mappable {
dynamic var text: String = ""
}
You mentioned that the relevant portions of you model classes look like so:
class Publication: Object, Mappable {
dynamic var id: Int = 0
var typez = List<getType>()
dynamic var url: String?
}
class getType: Object, Mappable {
dynamic var text: String = ""
}
If I understand you correctly, you want to find Publication instances that have an entry in their typez list with text equal to special. You can express that as:
let realm = try! Realm()
let result = realm.objects(Publication).filter("ANY typez.text = 'special'")
print(result)
I was not liking the accepted answer here because it doesn't actually answer the question... but then it helped me more than I realized. I will now be using closures instead of NSPredicates whenever possible. The actual answer to this question should be a slightly modified version of #NSGangster's answer:
let realm = try! Realm()
//Array of publications
let realmObjects = realm.objects(Publication)
//any publication where .text property == special will be filtered. and filter out empty array
let filterThis = realmObjects.filter({ $0.typez.filter({ $0.text == "special" } != [] ) })
print(filterThis)
.. or something close to that.
But what I was looking for was a bit different. I needed a way to filter on exact words of a multi-word string, and using an NSPredicate with "CONTAINS" would match any containing substring, e.g. a search for "red" would match "fred". Realm doesn't support "LIKE" or regex yet, so using a closure was the only thing I could get to work:
//I was going for a "related terms" result for a dictionary app
let theResults = terms.filter(
{
//Looking for other terms in my collection that contained the
//title of the current term in their definition or more_info strings
$0.definition.components(separatedBy: " ").contains(term.title) ||
$0.more_info.components(separatedBy: " ").contains(term.title)
}
)
With as much of the day as I spent searching, hopefully this helps someone else with a similar issue.
I don't usually use NSPredicate's directly, instead I do an inline predicate closure within the filter paramter.
let realm = try! Realm()
//Array of publications
let realmObjects = realm.objects(Publication)
//any publication where .text property == special will be filtered. and filter out empty array
let filterThis = realmObjects.filter({ $0.getType.filter({ $0.text == "special" } != [] ) })
print(filterThis)