Realm: Querying and Indexing with One-to-Many relationship - database

Lets consider the default Realm example of Person and Dog.
Person with name 'A' has 3 Dogs with respective names - 'X', 'Y', 'Z'
Person with name 'B' has 2 Dogs with respective names - 'X', 'Z'
So the code will be
[realm beginWriteTransaction];
Person *personA = [Person createInRealm:realm withObject:#{#"name": #"A"}];
Person *personB = [Person createInRealm:realm withObject:#{#"name": #"B"}];
[Dog createInRealm:realm withObject:#{#"name": #"X", #"owners": #[personA, personB]}];
[Dog createInRealm:realm withObject:#{#"name": #"Y", #"owners": #[personA]}];
[Dog createInRealm:realm withObject:#{#"name": #"Z", #"owners": #[personA, personB]}];
[realm commitWriteTransaction];
Now, in order to get all the dogs based on owners we write
RLMArray *dogs = [Dog objectsWhere:#"ANY owners = %#", personA];
or
RLMArray *dogs = [Dog objectsWhere:#"ANY owners = %#", personB];
How should we set index in this case to improve performance?
How can we inverse link so that we can do
RLMArray *persons = [Person objectsWhere:#"ANY dogs = %#", dogX];

That's not supported yet.
For now you can change the direction of your relationship and have a property of type RLMArray<Dog> on Person, if you need to traverse it more often in that direction. Alternatively you can maintain two relationships in both direction, you would need to sync them yourself.
We track adding support for that by issue #1324.

Related

Grails GORM - return count of objects who's array property > 0

Consider the following;
class Person {
int id
String name
static hasMany = [cars : Car]
}
class Car {
int id
String brand
static belongsTo = Person
static hasMany = [owners: Person]
}
The above will result in a person_cars join table. All I'm trying to find out is if there are any entries in that table, in words;
Do any Persons currently exist who own a car.
Happy to use any mechanism available (finders/criteria/HQL etc)
My opinion is better to add PersonCar entity.
For your question:
Car.count() > 0
As car belongTo Person, so that can't be any copy of car without been added to the person.
If person is nullable you can use:
Car.countByPersonIsNotNull()
Think that if car has person, so that there will be a value in that table.
Got it! A lot simpler than I thought.
Person.createCriteria().count {
owners {
count
}
}
This effectively gives me the number of person_cars records.

Create a multi dimensional array in swift and use the data to populate 3 tableviews

Thanks for taking the time to read my question.
I have experience in php with multidimensional arrays but i am struggling to transfer my knowledge over to swift...
The plan is to create an array like this:
Category1
-- product1
--- description
-- product2
--- description
Category2
-- product1
--- description
-- product2
--- description
Category3
-- product1
--- description
-- product2
--- description
And using 3 tableviews (1 on each viewcontroller) i would like to populate the info:
Tableview1 -- Category list.
Tableview2 -- Product for selected category list.
Tableview3 -- Description for selected.
I can populate 1 tableview using a simple array easily, but how would i:
1) Create the multidimensional array?
2) Count & populate each tableview?
3) Tie each child row to parent rows?
I really hope i am making sense and i am not looking for a free ride, just a helping hand as i am really enjoying learning the swift language!
Kind regards
Rory
Welcome to swift user1263909
How to create an array of strings
let stringArray = [String]()
How to create an array of strings with string literals
let categoryStringArray = ["Category1", "Catagory2", "Catagory3"]
How to create an array of string array's
let arrayOfArray = [[String]]()
how to create an array of string array literals
let a = [["Category1", "Catagory2", "Catagory3"],["product1", "product2", "product3"]]
accessing "Catagory3"
a[0][2]
However this all can get complicated fast. Why not use some structs?
struct Product {
var name:String
var description:String
var price:Double
//ect...
}
use struct to encapsulate data for easy use
let p1 = Product(name: "hat", description: "a hat", price: 2.00)
lets make an array of structs.
let arrOfProduct = [p1,Product(name: "tie", description: "a bow tie", price: 5.00)]
arrOfProduct now has two structs in it a tie and a hat.
How to access hat's price
arrOfProduct[0].price
oh we could also have a struct for catagories, it can contain products as an array
struct Category {
var name:String
var products:[Product]
}
Lets create a Category for Clothes.
let clothes = Category(name: "Clothing", products: arrOfProduct)
lets create another catagory
let food = Category(name: "Food", products: [Product(name: "Candy", description: "Yummy Candy", price: 2.0)])
and finally we can have an array of Catagory
let arrC = [clothes,food]
now I can loop through all catagories in first table view to fill out the cell names
for c in arrC{
print(c.name)
}
when you switch to a new view controller pass it the correct array of products
let productsClicked = arrC[0].products
then on that new view controller loop the products
for p in productsClicked{
print(p.name)
}
if they click a product pass the product to the next view controller and so on.
this will be much easier to maintain as you get new requirments.
Goodluck and welcome to swift. :D

Query on inverse relationship

I am new to Realm and having problem with query that drive me crazy for several days. I have 2 simple classes Dog and Person as:
class Dog: Object {
dynamic var dogName = ""
dynamic var dogAge = 1
var owners: [Person] {
return linkingObjects(Person.self, forProperty: "dogs")
}
}
class Person: Object {
dynamic var firstName = ""
let dogs = List<Dog>()
}
After adding data to Realm, I have the following data:
Oscar have 3 dogs namely A, B and C with respected age of 4, 6, and 9
Jennifer have 3 dogs namely D,E and F with respected age of 1, 2 and 7
One dog named Z, age 8 and have no owner.
let database = try! Realm()
// Query all Dogs- It should have 7 dogs
let allDogs = database.objects(Dog)
print("A total of \(allDogs.count) dogs") // 7 dogs - correct
// Query All dogs between age of 4 and 12 - there should be 5 dogs
let agedDogs = database.objects(Dog).filter("dogAge BETWEEN {4,12}")
print("Dogs between Age 4 and 12 = \(agedDogs.count) \n") // 5 dogs - OK
I want to know of all those agedDog, how many of them belong to Oscar?
I tried:
let specificOwnerAgedDogs = agedDogs.filter("ANY owners.firstName = 'Oscar'")
print("Oscar Dogs between Age 4 and 12 = \(specificOwnerAgedDogs)")
But this query results in an error:
Terminating app due to uncaught exception 'Invalid property name', reason: 'Property 'owners' not found in object of type 'Dog''
I am using the Realm Swift with Swift 2.1.1.
Realm Swift does not yet support provide direct support for queries that traverse inverse relationships, such as the owners property on your Dog class. This is something we're working on, and that should make it into a Realm Swift release in the near future.
For now, you can work around this limitation by performing some of the filtering in Swift rather than in the predicate:
let specificOwners = database.objects(Person).filter("firstName = 'Oscar'")
let specificOwnerAgedDogs = specificOwners.flatMap() { owner in
owner.dogs.filter() { $0.dogAge >= 4 && $0.dogAge <= 12 }
}
let noOwn = agedDogs.flatMap {
(ndog) -> Dog? in
ndog.owners == [] ? ndog : nil
}
I use this code to find out the list of all agedDogs that do not any owner?
Any thought on this?

Swift - best way to handle a dictionary of dictionaries?

I have an array of people that all have a list of favourite books.
Each book has several properties, such as book title, book description, book author.
What I want to do is create a dictionary of dictionaries to be able to access a specific person, and then access each book of that person and extract the book name, book title author etc from that that.
currently what I am doing is the declaring a dictionary like so:
var dictToHoldBooKValue = [String : [String : AnyObject]]()
the first String is the key, which I will create as the name of the person, the second pard is a dictionary, which will have a key of type string (which I will set as the book property name like "book Title" or "book author" and the any object is the value of that key.
example: I modify the dictionary like so:
dictToHoldBooKValue["Mark"] = ["book Title" : (bookTitle as? String)]
However, this approach does not seem to be working because "Mark" has several books he likes and so when I print the dict count it only shows one. Also, I have no idea how I would use this dict to to get at the book author property or any other property.
Anybody know a more elegant solution?
Thanks!
Make a struct to represent a book. Make it contain all the book attributes you want to track.
struct Book {
var title: String
var author: String
var year: Int
}
Your outer dictionary will be exactly as you described.
Your inner dictionary will have its key as the name of the book and it will contain Book structs.
var dictToHoldBooKValue = [String : [String : Book]]()
dictToHoldBooKValue["Mark"] = [
"You Only Live Twice" : Book("You Only Live Twice", "Ian Flemming", 1961),
"Casino Royale" : Book("Casino Royale", "Ian Flemming", 1959)
]
Access it like this:
print(dictToHoldBooKValue["Mark"]["You Only Live Twice"].author)
This is all fine if you are loading this data from somewhere and want to show it in a specific way. If you need to query data in a more flexible way, you should consider using CoreData.
If you want to use key-value coding, you can use typealiases to simplify your code:
typealias Book = [String: AnyObject]
typealias Books = [Book]
typealias Person = [String: AnyObject]
var people = [Person]()
var mark = Person()
var marksBooks = Books()
var theLittlePrince = [
"title" : "The Little Prince",
"author": "Antoine de Saint-Exupéry"
]
marksBooks.append(theLittlePrince)
mark["books"] = marksBooks
people.append(mark)
print(people)
Prints:
[["books": (
{
author = "Antoine de Saint-Exup\U00e9ry";
title = "The Little Prince";
}
)]]

Query for several same-level ancestors

I have the following entities:
class Book():
team = db.StringProperty()
class Entry():
role = db.StringProperty()
I create two Books:
book1 = Book(team='Plants').put()
book2 = Book(team='Zombies').put()
I create three Entrys:
entry1 = Entry(parent=book1, role='Peashooter').put()
entry2 = Entry(parent=book2, role='Gargantuar').put()
entry3 = Entry(parent=book2, role='Flag Zombie').put()
I can make a query for the 'Plants' Book:
query = Entry.all().ancestor(book1).fetch(100)
I'd like to make a query for several books. I can only think of list concatenation:
list1 = Entry.all().ancestor(book1).fetch(100)
list2 = Entry.all().ancestor(book2).fetch(100)
query = list1 + list2
Is there a more elegant way to do this other than list concatenation?
If you want to combine queries for several different ancestors (that don't share a common ancestor), the only way to do it is with separate queries, as you demonstrate. This is no different than if you want to query for several distinct values in a field.

Resources