How to list a Property in realm Swift 3 - ios

How to list a property in realm DB like SELECT columnName FROM mytablein SQL?
Here is my try:let person = self.realm.objects(Person.self).filter("age")

You can access a single property (since Realm models are native objects, they have properties, not columns) of all instances of your particular model class stored in Realm using map.
filter, as its name suggests can be used to only work on a subset of all instances of a certain type that all fulfilled the same condition (for example you can use filter to find all people whose age is above 18 by saying: let adults = self.realm.objects(Person.self).filter("age > 18")).
Get the age property of all instances of Person persisted in Realm using map:
let people = self.realm.objects(Person.self)
let ages = people.map{$0.age}
or in one line giving an Array as an output:
let ages = Array(self.realm.objects(Person.self)).map{$0.age}

you can get list of records like this
let realmCities = try! Realm()
lazy var arrDefaultCities: Results<Cities> = { self.realmCities.objects(Cities.self).sorted(byKeyPath: "cityName", ascending: true) }()
func filterCities()
{
let statePredicate = NSPredicate(format: "stateId = %d", objState.stateId)
arrDefaultCities = try! Realm().objects(Cities.self).filter(statePredicate).sorted(byKeyPath: "cityName", ascending: true)
self.filterArrCities.removeAll()
for objCities : Cities in arrDefaultCities{
if objCities.cityName == APP_DELEGATE.currentCity
{
self.objCity = objCities
}
self.filterArrCities.append(objCities.cityName)
}
}

Related

How Query and Count entries in a Realm Database

I'd like to build a search on which the user can filter down the results step-by-step. So with no choice set, there is a button which says e.g. "1,234,567 Results" and if you choose a color for example the results set shrinks... we all know this kind of search. I did build it many times, but this is the first time in Realm (and swift).
Lets Say I have 5 Persons in my Person Table, then there are about 145,224 Dog entries per Person and about 2,507,327 Cat entries per Dog. How do I query and Count nested Objects in Realm?
class Person: Object {
#objc dynamic var name = ""
let dogs = List<Dog>()
// ...other Properties
}
extension Person {
static func all(in realm: Realm = try! Realm()) -> Results<Person> {
return realm.objects(Person.self)
}
}
// counts -> 145,224 db entries per person
class Dog: Object {
#objc dynamic var name = ""
dynamic var Person: Person?
let cats = List<Cats>()
// ...other Properties as well
}
extension Dog {
static func all(in realm: Realm = try! Realm()) -> Results<Dog> {
return realm.objects(Dog.self)
}
}
// counts -> 2,507,327 db entries per dogs
class Cat: Object {
#objc dynamic var name = ""
dynamic var Cat: Cat?
}
extension Cat {
static func all(in realm: Realm = try! Realm()) -> Results<Cat> {
return realm.objects(Cat.self)
}
}
// Get the default Realm
let realm = try! Realm()
// Query Realm for all dogs
let dogs = Person.all(in: realm).flatMap { $0.dogs }
dogs.count // => takes ~20 seconds to count
In other words, what is the fastest way to get (count) all Dog entries of all Persons (let the cats by side for now).
I tried to workaround the problem by limit the results to 1000. If the results are >1000, then label the button like so "> 1000 Results". But even than it takes very long (I guess the get all count anyway).
So what did I do wrong?
They way you were computing the count required all Dog objects to be loaded into memory which is really inefficient. This is why you were seeing such poor performance. You want to take advantage of Realm's lazy loading features. You may want to read up on that.
I would update your Dog object by getting rid of your managed Person property and replace it with LinkingObjects. If you store a Dog in a Person.dogs List, then realm will create a back link to the Person for you. You will likely want to do the same thing with Cat. That way you can set up some really powerful nested queries.
For convenience you can add a computed Person property to index into the LinkingObjects. Just know that you won't be able to use that property in any of your queries.
class Dog: Object {
#objc dynamic var name = ""
let persons = LinkingObjects(fromType: Person.self, property: "dogs")
var person: Person? { persons.first }
}
One way to compute the count is to query of all Person objects and sum the count of dogs for each Person.
let count = realm.objects(Person.self).reduce(0) { result, person in
return result + person.dogs.count
}
Another options is to query for Dog objects that have a corresponding Person. If a Dog is not in a Person.dogs List, then it won't show up the query.
let realm = try! Realm()
let count = realm.objects(Dog.self).filter("persons.#count > 0").count
Either option is going to be much, much more efficient than what you had.
Hope this helps.

Fetching distinct values from cloudkit swift

I have a database students with columns :
courseID, StudentID,FirstName, LastName
Now a student can take more than one course.
I have a screen where I am fetching just the students names.
And so obviously I am getting duplicate values. Now I know I can change the type of courseID from string to a string list to do away this problem but that would mean a hell lot of coding changes. is there anyway I can modify my CKQuery to fetch distinct values?
As of now the code looks like this:
func FetchRecords(){
print("Fetch Records")
let predicate = NSPredicate(value: true)
let query = CKQuery(recordType: "Students", predicate: predicate)
let operation = CKQueryOperation(query: query)
operation.desiredKeys = ["FirstName","LastName","StudentID"]
var newStud = [Student]()
print("Begin")
operation.recordFetchedBlock = { (record) in
let stud_rec = Student()
print(record["FirstName"] as! String)
stud_rec.FName = (record["FirstName"] as! String)
stud_rec.LName = (record["LastName"] as! String)
stud_rec.MatrN = (record["StudentID"] as! String)
newStud.append(stud_rec)
}
Amrita I think there is data model design issue where you have felt one of consequences -- a lot of "duplicate values".
The root solution is to avoid many-many relationship between courseID and studentID via a record type say Enrollment to track which student takes which course at which year. This of course demands a "hell lot of coding change." that you also want to avoid.
So maybe you may try this quick surface patch to "fetch distinct values" where you may use Swift Set operations to deal with fetched duplicates as below:
let fetchedDuplicate = ["Jon", "Sid", "Tom", "Jon"]
let uniqueSet = Set(fetchedDuplicate) // it is now {"Jon", "Sid", "Tom"}
let uniqueArray = Array(uniqueSet) // ["Jon", "Sid", "Tom"]
I hope it helps.

filter list on inverse relationship

I struggle a bit today, taking the example given on RealmSwift documentation, what I am trying to do is find the query that will allow me to get the dogs (from the dog object) who only have at least one owner.
class Person: Object {
// ... other property declarations
let dogs = List<Dog>()
}
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
let owners = LinkingObjects(fromType: Person.self, property: "dogs")
}
I have this basic method :
public class func getDogs() -> Results<Dog>? {
do {
let aRealm = try Realm()
let dogs = aRealm.objects(Dog.self).filter("ANY owners != nil")
return dogs
} catch {
print(error)
}
return nil
}
but it fails so I assume my query is incorrect, though I failed to find any documentation on this, any insight would be much appreciated.
You can use the aggregate expression, #count. The following query filters dogs that have at lease more than one owner.
let dogs = aRealm.objects(Dog.self).filter("owners.#count > 0")
Please see more details: https://realm.io/docs/swift/latest/#filtering

Adding up an attribute inside entity for CoreData ios 10

I have a tableView and that tableview is being populated with data from Coredata. The data breaks down like this.
Entity - Person
Entity - Statement
The statement entity has an attribute called amountOwed and it is of decimal type.
The relationships is that a person can have many statements, but each statement belongs to a single person.
Here is the path of the data that I would like to add up. Person > Statement > AmountOwed.
In the tableView function I have a let that represents the Person entity.
let person = fetchedResultsController.object(at: indexPath)
I know its working because I can print out the persons name like so
print(person.name) // Bob
What I want to be able to do is add up all the amountOwed attributes for each Person inside the Statement entity and display them on a cell.
I have been trying to follow an example of calculated fetches but I seem to not quiet understand how to target my Statements Entities which are linked to each Person entity.
let fetchRequest = NSFetchRequest<NSDictionary>(entityName:"statement")
fetchRequest.resultType = .dictionaryResultType
let sumExpressionDesc = NSExpressionDescription()
sumExpressionDesc.name = "sumDeals"
let specialCountExp = NSExpression(forKeyPath: #keyPath(Person.statement[indexPath].amountOwed))
sumExpressionDesc.expression = NSExpression(forFunction: "sum:", arguments: [specialCountExp])
sumExpressionDesc.expressionResultsType = .interger32AttributeType
fetchRequest.propertiesToFetch = [sumExpressionDesc]
do{
let results = try coreDataStack.managedContext.fetch(fetchRequest)
let resultDict = results.first!
let numDeals = resultDict["sumDeals"]
print(numDeals!)
}
catch let error as NSError{
print("Count not fetched \(error), \(error.userInfo)")
}
Do I need to fetch a Statement entity or should I just use the FetchedREsultsController? If I do use my fetchedResultsController does the keypath to the Statement Entity look like this
person[indexPath].statement.amountOwed
You can do that in one line. If the relationship from Person to Statement is called statements, you get the total of the amounts with
let amountTotal = newPerson.value(forKeyPath: "statements.#sum.amount") as? Int64
Change the downcast at the end from Int64 to whatever is appropriate for your amount attribute-- Double or whatever.
OK on your People+CoreDataClass add:
var totalOwed: Float {
get {
var value: Float = 0
if let statements = self.statements.allObjects() as? [Statement] {
for s in statements {
value = value + s.sum
}
}
return value
}
}
And remove all the code from your fetch that is unnecessary

Sort entities based on fetched property

In my application (written in Swift) I use a model for Core Data with two configurations, each of these uses different save path (one is read-only in the application bundle and another is in the Documents folder). I set in one of two configurations an entity "Domanda" with the following attributes:
In the other configuration I set an entity "StatDomanda" with the following attributes:
"StatDomanda" is used for user data, while "Domanda" is used as preloaded source. I know that I can't set up a relationship between the two entities (two configurations), but I would order the "Domanda" entity based on the "corrette" attribute of the "StatDomanda" entity where StatDomanda.numero == Domanda.numero and StatDomanda .argomento == Domanda.argomento.nomeArgomento.
Not all "Domanda" objects have corresponding "StatDomanda" objects.
I set a fetched property (called "statistiche") in the "Domanda" entity with this predicate
$FETCH_SOURCE.numero = numero AND
$FETCH_SOURCE.argomento.nomeArgomento = argomento
but I do not understand how to use it to order the objects.
Thanks for the help!
I found a solution, I don't know if is the best way but it works!
In my core data manager class (ModelManager) I have this function used to fetch all the "Domanda" entities:
func getAllQuestions() -> [Domanda] {
let fetchRequest = NSFetchRequest(entityName: "Domanda")
let fetchResults = managedObjectContext!.executeFetchRequest(fetchRequest, error: nil) as? [Domanda]
return fetchResults!
}
In my ViewController I use this code to obtain the sorted array of "Domanda" entities based on attribute "corrette" of "statistiche" fetched property:
override func viewDidLoad() {
super.viewDidLoad()
var arrayDomande = ModelManager.instance.getAllQuestions()
var sortedArrayDomande = self.sortArray(arrayToSort: arrayDomande)
}
func sortArray(arrayToSort sarray: Array<Domanda>) -> Array<Domanda> {
// 1 - Filter the array to find only questions that have stats
var onlyAnswered = sarray.filter({($0.valueForKey("statistiche") as [Domanda]).count == 1})
// 2 - Sort the array based on fetched property
var sortedArrayDomande = sorted(onlyAnswered, {
let first = ($0.valueForKey("statistiche") as [StatDomanda])[0]
let firstC = first.corrette.integerValue
let second = ($1.valueForKey("statistiche") as [StatDomanda])[0]
let secondC = second.corrette.integerValue
return firstC > secondC
})
return sortedArrayDomande
}
or even in condensed form:
// 2 - Sort the array based on fetched property
var sortedArrayDomande = sorted(onlyAnswered, {($0.valueForKey("statistiche") as [StatDomanda])[0].corrette.integerValue > ($1.valueForKey("statistiche") as [StatDomanda])[0].corrette.integerValue})
Goodbye guys, see you next time!

Resources