Time Complexity of Realm Lists in Swift - swift

I was wondering how the Realm Lists were implemented in swift. To be more specific, I was wondering if they are Linked Lists or if they work similarly to Array Lists. For example, what would the time complexity of retrieving an item at an index be. Let's say I have Realm List A of length m and I want to retrieve A[n], would this operation run in O(n) like a Linked List or O(k) like in an Array List.
Edit:
Here is a little more clarification to the question:
Let's say I want to store my user's friend list in a Realm Database. The way I would do this is by defining a new Class User as follows:
Class User: Object{
#objc dynamic var name: String?
#objc dynamic var userName: String?
#objc dynamic var email: String?
var friendList = RealmSwift.List<String>() //Contains the names of all friends
}
Let's say that I've added some friends to my user's friendList and somewhere else in my app I want to access this friend list and display a specific friend as such:
var realm = try! Realm()
var user = realm.objects(User.self)[0]
var friendToDisplay = user.friendList[4]
print(friendToDisplay)
I'm wondering what is the time complexity of the user.friendList[4] operation. If Realm would treat the RealmSwift.List as a linked list, then the complexity would have to be O(n) while if when user is called to memory, RealmSwift.List is implemented as an ArrayList then accessing the fourth memory address in the list would be O(1).
My curiosity comes from wondering which would be faster:
Looping through the List and finding the name of the friend we want to display as such:
var realm = try! Realm()
var user = realm.objects(User.self)[0]
var friendName = "Peter"
for friend in user.friendList{
if friend == friendName {
print(friend)
}
}
or
Knowing the index of the friend we want to display let's say index 4 as such:
var realm = try! Realm()
var user = realm.objects(User.self)[0]
var friendToDisplay = user.friendList[4]
print(friendToDisplay)
In both of the cases let's say that the friend's name or the index that we are using exist in the friendList List.

The question is not going to be directly answerable due to the number of variables involved and the inherent nature of Realm. I will provide some info but let me clarify one point that's important.
Realm doesn't support primitives in Lists (very well).
EDIT: Release 10.7 added support for filters/queries as well as aggregate functions on primitives so the below info is no longer completely valid. However, it's still something to be aware of.
So you won't want to do this
var friendList = RealmSwift.List<String>()
You will however want to define a FriendClass that holds details about your friend
class FriendClass: Object {
#objc dynamic var friendName = ""
}
and then do this
let friendList = List<FriendClass>()
Now on to your question:
Your question asks about finding a friend by looping through a list or knowing the index. While you can do either programmatically, neither is an ideal solution.
If you want to want to find your FriendClass object for your friend Peter, this is the fastest way to do it
if let peter = realm.objects(FriendClass.self).filter("friendName == 'Peter'").first {
//do something with the peter object
}
keep in mind that is working with ALL of the friend objects in realm. You can likewise perform the same thing on a specific list instead. So if your user object has a friendsList list:
if let peter = user.friendsList.filter("friendName == 'Peter'").first {
//do something with the peter object
}
Here's the variable bit: Realm objects are lazily loaded. This allows Realm to contain millions of objects but not load them into memory until they are needed. That prevents overwhelming the device.
That's a huge variable as things like disk access and if the object is already loaded can have an impact. However, using the technique I presented above, whether it's one or 10,000 friends in your friends list the query is virtually instantaneous.
One of our projects has 10GB of data to which a filter like shown above presents an immediate response from Realm.
Now here come the other variables:
Primary Keys
If your objects have primary keys you can directly read that object without messing about
let peter = realm.object(ofType: FriendClass.self, forPrimaryKey: "peters key")
For clarity, objects with primary keys take O(log n) time. n is the number of objects of that type in the database. In a nutshell, the time it takes to retrieve a friend is unrelated to the number total objects in Realm.
Indexing
Indexed objects make writes a tad slower but make queries faster which, in the above example, if name were set as a indexedProperty it would increase the over all read speed when querying on that property
Lists
A list is another layer if indirection which, generally speaking would be 'slower' than reading objects directly. That being said, 'slower' is highly subjective and relative to the use case. Taking .03 seconds to retrieve a friend from a list of 100,000 is not impactful.

Related

SwiftUI CoreData not in Set of relationships

I am having a CoreData structure. It is working fine for me. Now I am trying to do an interesting FetchRequest, however I am struggeling to find the correct predicate.
I am having two ententies, Person and Venues. A person can be connected to many venues, but only once to the same venue. And other way around.
I have created a mapping table, which saves the person and venue as relationships. Everything is working fine here. I even implemented the inverse relationship.
In the picture left is my venue. I want to Fetch all persons based on a current venue, where there is NO Mapping created already.
My approach was to use pk_venue, which gives me a set of PersonVenueMapping. However, how can I only use the persons of that Set.
fetch.predicate = NSPredicate(format: "NOT (person IN %#)", venue!.pk_venue[HELP])
I came up with that Predicate. However, how can I access the persons of that set? I would need a array/ set of only the persons, that are connected to that current object.
Edit: Just to give you example.
Person: Max, Leon, Henry
Venue: Venue1, Venue2, Venue 3, Venue 4
PersonVenueMapping: Max <-> Venue1
Now when I select Max, I want Venue2 & Venue3 & Venue4
Thanks in advance
Your model looks very much like a normalised database type model. CoreData is not a database and so some of the requirements of modelling data are more flexible when using it. For instance many to many relationships are possible (and encouraged).
Create your model in this way...
Person
------
name: String
------
assignedVenues: Many relationship to `venue` type
Now every relationship has an inverse property, so...
Venue
------
name: String
------
managers: Many relationship to `Person` type
Now you can go from Max to his assigned venues just by doing...
let selectedPerson = // fetch Max from CoreData
let venues = selectedPerson.assignedVenues
Equally you can go from a venue to find all the people managing it...
let someVenue = // fetch venue from CoreData
let managers = someVenue.managers
To satisfy your question of finding all venues that Max is not managing you can now do a query like...
let selectedPerson = //fetch Max
let predicate = NSPredicate(format:"NONE managers == %#", selectedPerson) // Its been a long time since I did CoreData so forgive me if this syntax isnt correct but the idea is correct. :D
You can now use that predicate to get all venues where max is NOT one of the managers of the venue.
By removing the "mapping" that is required in something like SQLite etc... you can make things much simpler for yourself.
I am confused as to whether you are fetching persons with no mapping to a given venue, or venues with no mapping to a given person. I’ll assume the latter.
Your fetch request will therefore be for the entity Venue. Let’s call the selected Person selectedPerson. The following predicate should return only those Venues that have no mapping to selectedPerson:
NSPredicate(format:”SUBQUERY(pk_venue, $M, $M.person == %#).#count == 0”, selectedPerson)
or in natural language “fetch Venues for which the count of mappings ($M), with person equal to the selected person, is zero”.

How can I reorder elements in Realm LinkingObjects?

I have an Item data class, and each Item has one parent and many children Items, which are LinkingObjects from the parent property.
A simplified version:
#objcMembers class Item: Object {
dynamic var title: String = ""
dynamic var parent: Item?
let children = LinkingObjects(fromType: Item.self, property: "parent")
}
I need to be able to reorder the children, and persist that change. It seems that because LinkingObjects is a container type this wouldn't be possible? In this case, how would you recommend I approach this--should I change children to a normal List<Item> and just manually update the realm two ways every time? Is there a better solution?
The order in which the children appear matters, because users will want to choose their own ordering based on which items they want to see first.
The important part here is that you want to control the 'order in which the children appear'. If you can recognise the difference between the 'model' of the data and the 'view' of the data then you can work out the answer. Remember that the Realm model representation is not under your control, and you don't need it to be. How it's stored in the Realm database is not your problem.
In reality, the results of the LinkingObjects field when you request it will probably be the result of a database query, and not a straight pull from a table. So the order of objects in the field may be inconsistent, or adding further linked objects may cause the extra item to appear anywhere in the results.
As you say, the important thing is the order in which the children appear. And this can be controlled through a query. The LinkingObjects type allows you to query directly, i.e. you can call:
item.children.sorted(byKeyPath: "title")
One suggestion would be to add an extension to your model type to handle the sorting for you, providing an accessor for each sort type you require, e.g.:
extension Item
{
var childrenSortedByTitle: Results<Item>
{
return children.sorted(byKeyPath: "title")
}
}
Or you could make that a function with an ascending boolean parameter to use in the query. Or you could add a second query for ascending/descending. And you can add extra queries for each other parameter you would like to sort by. Or you could define an enum of sort fields and pass that it as an argument. Whichever of these methods works best for you. But it is definitely worth hiding the query details in the class extension itself (IMHO).

Realm Swift inverse relationships many-to-many

I'm currently trying to work out the best way to architect my realm objects for ease of retrieval.
I have 2 objects tags and object there are multiple tags and each one might contain many object. Similarly each object could have multiple tag associated with it
Ideally selecting a single tag should retrieve all object that have at least that one tag (but could obviously have multiple)
would my models be specified as
class Tag: Object {
let objects = List<Object>()
}
class Object {
let tags = List<Tag>()
}
I don't think I need to use an inverse relationship here or should I? Choosing a Category I should be able to just retrieve a list of all object references regardless, but then maintaining and updating the references to an object might be difficult here? I.e a user selects tag 'A' then updates the first object to also include tag 'B' I would need to update the object in the List for Tag A, then add a new item to the list for Tag 'B' and finally update the actual Object itself to include Tag 'B' in it's list of tags.
Just to be clear an Object will only ever display and allow editing of it's Tag objects. But the Tag object itself will need to know what Object's are applicable to it.
However it feels like I will have to do multiple updates when ideally I'd like to minimise this effort. Can anyone recommend a better way to do this? Or is there no way around this due to the limitations of Realm?
This is exactly what LinkingObjects is for. Changing the objects property in Tag to let objects = LinkingObjects(fromType: Object.self, property: "tags") will make it automatically update whenever a tag is added to an object.

Initialize Realm object without data in Swift

So suppose I have a Dog object that contains a reference to a Toy object:
class Dog: Object {
dynamic var toy: Toy!
convenience init(toyId: String) {
self.init()
let realm = try! Realm()
toy = realm.object(ofType: Toy.self, forPrimaryKey: toyId)
}
}
In this example, suppose I have a bunch of toys that have already been created in Realm, and I want to create a new instance of a dog and all I have is the toy id (not the actual toy object).
In the example above, I can make this work by doing a lookup for the toy, but if I'm creating a bunch of dogs all at once, this seems inefficient.
The other option, I suppose, is to fetch all the toys upfront and then pass the actual toy object to the dog initializer.
My question is, if I'm just trying to create a new dog, and link it to an existing toy, can that be done without having to fetch the toy?
I'm new to Realm, but when I was using Parse in the past they had a special initializer for this kind of scenario:
PFObject(withoutDataWithClassName: <String>, objectId: <String?>)
The idea was you could reference an object from the primary key, and only fetch the data if you end up needing it. Something like that, it seems, would be ideal for what I'm trying to do.
My question is, if I'm just trying to create a new dog, and link it to an existing toy, can that be done without having to fetch the toy?
It is necessary to fetch the toy to establish the relationship between it and the dog.
It's worth noting that looking up an object by primary key does not result in any properties of that object being loaded into memory. The returned object is just a pointer to a specific object in the Realm file on disk. The object's properties are read into memory only when they're accessed.

Parse - Array of pointers - Many to Many relationship query

I have a Many-to-Many relationship between the following tables : User and School. My users are parents, and they have child(ren) in one or several schools.
I spent a few hours reading about designing Many-to-Many relationships on NoSQL databases. I came to the conclusion that the right way to do it was to only store an array of User named parents within the School table.
In Parse.com, this can be materialized by an array of pointers toward the right User object.
Now, I want to retrieve all Schools for a given parent. Here is How I thought I should do :
var School = Parse.Object.extend("School");
var school_query = new Parse.Query(School);
var current_user = Parse.User.current();
school_query.equalTo("parents.objectId", current_user.id);
school_query.find().then(
function(schools) {
console.log("Success");
},
function(error) {
console.log("Error");
}
);
This is returning POST https://api.parse.com/1/classes/School 400 (Bad Request), calling the "error" callback, although there exist pointers corresponding to my current user within some School.parents array.
What is the correct way to query an array of pointers to retrieve its nesting object only if the array contains the pointer towards the current user?
If the "parents" column on School is an array of pointers, operands to it are objects, not object ids. Change the equality criterion to:
school_query.equalTo("parents", current_user);