Mobile Bytes #6: What are Realm Relationships?

Hello Everyone, :wave:

I hope you are enjoying the byte size Realm Knowledge. This week we will learn some basics on Relationships.

In the Realm Database, A relationship is a connection between two Realm Objects.

In MongoDB Realm, you can define relationships between two objects ONLY within the same partition/realm. Relationships are direct references to other objects in a Realm/Partition.

Please Note: Relationships between objects in different partitions are not permitted. One alternative is to define a manual reference, like a foreign-key-esque concept, store a string in a field that corresponds to a field in another realm, and then query for that field in a different realm.

Relationships are represented in the Schema found in the Realm UI and can be created manually by editing the schema or automatically when using Development Mode.

There are three primary types of relationships between objects:

One-to-One Relationship

As the name implies, A Realm object is related to no more than one other object. Let’s take an example of an Author and a Book

Java

public class Book extends RealmObject {
    public String name;
    public Boolean isRead; 
}

public class Author extends RealmObject {
    public String name;
    public Book book;
}

Javascript

const Book = {
 name: "Book",
 properties:{
       name: "string",
       isRead: "Boolean"
     },
}; 

const Author ={
  name: "Author",
  properties: {
      name: "string",
      book: "Book"
     }
};

Every Author can have zero or one Book instance. Setting the relationship field to null will clear the reference.

One-to-Many Relationship

A one-to-many relationship is when one object relates to multiple objects. In the same above example, an Author can have multiple books. This will be a relationship between a RealmList and a RealmObject.

Java

public class Book extends RealmObject {
    public String name;
    public Boolean isRead; 
}

public class Author extends RealmObject {
    public String name;
    public RealmList<Book> books;
}

Javascript

const Book = {
 name: "Book",
 properties:{
       name: "string",
       isRead: "Boolean"
     },
}; 

const Author ={
  name: "Author",
  properties: {
      name: "string",
      books: "Book[]"
     }
};

RealmLists can contain multiple RealmObjects of the same or different type (Mixed) depending on how you define it. RealmList can be used to model both one-to-many and many-to-many relationships.

Java

realm.executeTransaction(new Realm.Transaction() {
   @Override
   public void execute(Realm realm) {
       Author author = realm.createObject(Author.class);
       author.name = "JK Rowling";

       Book book1 = realm.createObject(Book.class);
       book1.name = "Harry Potter and Philosopher’s Stone";
       book1.isRead = true;
       author.books.add(book1);

       Book book2 = realm.createObject(Book.class);
       book2.name = "Harry Potter and Chamber of Secrets";
       book2.isRead = false;
       author.books.add(book2);
   }
});

Inverse Relationships

Relationship definitions by default are unidirectional. As per the above relationships, you can follow the link from an Author to a Book, but there is no way to go from a Book to its Author object/s.

This is resolved by giving the Book a @LinkingObjects annotation.

@LinkingObjects

This essentially means that for any relationship you have in your schema (either 0…1 or 0…*), you can now see the objects that are linking to your current object.

The Class Model defining an Author with a list of Books

public class Book extends RealmObject {
    public String name;
    public Boolean isRead; 
}

public class Author extends RealmObject {
    public String name;
    public RealmList<Book> books;
}

You can provide a link in the opposite direction, from Book to Author with the @LinkingObjects annotation like below:

Java

public class Book extends RealmObject {
    public String name;
    public Boolean isRead; 

    @LinkingObjects("books")
    public final RealmResults<Author> authors = null;
}

public class Author extends RealmObject {
    public String name;
    public RealmList<Book> books;
}

Javascript

const Book = {
 name: "Book",
 properties:{
       name: "string",
       isRead: "Boolean"
       authors:{type: 'linkingObjects', objectType:'Author', property: "books"}
     },
}; 

const Author ={
  name: "Author",
  properties: {
      name: "string",
      books: "Book[]"
     }
};

Please Note: An inverse relationship can only be seen as 0…* meaning it must be declared as RealmResults. Also, when declared, it is final and the accessors are transformed to return the managed linking objects.

Check the Realm SDK documentation to learn more on Relationships.

To Note

  • Relationships are useful for defining a Realm Object Model which is layered with Realm Sync because the schema on the client-side Realm SDK and the server-side MongoDB Atlas must match for Sync to work.

  • Relationships are integral to building a client-side mobile app in the Object-Oriented paradigm.

  • Realm Relationships differ from MongoDB’s document model so I will discuss that separately.

In the next week’s session, we will sync our current mobile schema model to MongoDB Atlas.

I hope you enjoyed reading this. I would love to know your experiences working with Relationships, what worked and did not work and how you resolved it.

Cheers :performing_arts:

4 Likes

Super great article - bravo!

One thing to I would like to mention about inverse relationships is that it’s automatic - when an object that has a LinkingObject property is added to a List, the inverse relationship is automatically ‘created’ - which is really cool and saves a lot of time when developing a relationship graph.

Keep in mind that LinkingObjects is plural - meaning that property could point back to several objects e.g. a Book could have one author (the first object it points back to) or it could be several Authors.

Question - and forgive me if I mis-read/mis-understood

Are Realm List collections still homogenous (meaning they can only contain one type of object)?

Does Swift have a Mixed property or is the corresponding property a AnyRealmValue?

2 Likes

Thanks Jay :smiley: for your kind words and a pointer to correct LinkingObjects as plural.

They can contain different objects if a Mixed type is used. The naming for this type varies in different Realm SDKs based on the vocabulary of the respective SDK.

For anyone else, looking for this, the links are as below:

AnyRealmValue => Swift SDK

Mixed => Node.JS SDK

RealmAny => Android SDK

RealmValue => .NET SDK

Mixed => ReactNative SDK

I hope this clarifies. Let me know if there are any more questions.

Cheers :performing_arts:

1 Like

@henna.s, thank’s for the guides on Realm relationships.

Can you shed some light on some more advanced relationships? For example this one Relationship for array with objects where I have an array of objects and one property of the object shall be a relationship?

Secondly, how can I model @LinkingObjects in the Realm UI? I have multiple frontend teams using my models and I’d like to be as service-minded as possible to them so they can copy&paste.

Can you clarify that question? Model it for what use or purpose? Can you provide some example code of what you want to do?

See your link for some followup on the first question.

A typical example is the one from Henna above. The “Book” has LinkingObjects to “Authors” but I can’t express this in Realm UI. Each SDK client needs to enrich the classes themselves.

G’Day, @Mikael_Gurenius,

The inverse relationships are not synced to Atlas. Realm Relationships are different from MongoDB Document Model. Please refer to the attached link in my post above (Mobile to MongoDB Atlas), where I am writing an application to sync the book and author classes to Atlas.

Please bear with me a little more time and I will upload the complete application on Github for you to check.

I noticed Tyer responded to you on your question on relationships and I assume you have been able to resolve the issue you had? Please feel free to ask if you would have any further questions.

Cheers, :performing_arts:

1 Like

Yes, Tyer has resolved one of them. The other remain, and as you just stated, there is no way to model an inverse relationship in Realm UI. That’s too bad. Here, we have one team doing modelling in Atlas / Realm UI and multiple frontend teams using those models. Inverse relationships play a major role for us and it’s error-prone to have each frontend team adding them on top of the models generated in Realm UI.

Thank you for your response. The inverse relationships don’t sync but the relationship still exists.

The BookLog Application that I am creating will give more clarity on this. Alternatively, you can try executing any queries on the inverse relationship in your client device and verify the result.

I look forward to your response.

Cheers, :performing_arts:

1 Like

If you really need to persist the relationship you can just create a 1-1 relationship in both directions - there’s nothing preventing you from creating a regular circular relationship.

1 Like

3 posts were split to a new topic: Linking Objects v/s Embedded Objects

Sure, that is an option. However, it leads to other issues:

  1. App developer needs to update two objects (manageable).

  2. Our experience shows that changes to an array yields a full sync of the array. Even with medium sized relationships (say 1.000 links), it quickly becomes a bottleneck when the linked objects are added or deleted frequently.

That may be the case but when you say array can you be a bit more specific? What array objects are they? Perhaps you can provide some example code and show what those objects look like?

Sure, it was a reply to @Ian_Ward about the 1:1 relationships in both directions. If I create a document with

{
   "_id":"ABCD",
   "header": "Header 1",
   "items": [ "A100", "B235", "C146" ]    <--- 1 to 1.000
}

The items are references to another collection where each item has a reference to the header. Updating this array every time time the item collection changes triggers a large sync of the full array. Keeping this extra reference only because I can’t model linkingObjects in RealmUI is a lot of overhead.

How are you updating the array? It should not be triggering a full sync of the entire array

This is unfortunately a byproduct of MongoDB change events and change streams. We use these to replicate changes made to MongoDB into Realm. The general rules of thumb for how the server creates a change event for a list is that if items are appended to the end of a list we can detect that, but if items are shuffled around in a list, or an element is added at the beggining of middle of a list, MongoDB applies that operation as a full-array-replace.

See here for more details: https://www.mongodb.com/docs/manual/reference/change-events/

As for how you could fix this, are you using $push / $pushAll to update the array? It seems line the answer there is no, but I think that doing so would likely fix your problem.

Thanks,
Tyler

Ah that’s correct - in general you should not depend on the list order of objects and instead define your own sort() function on the client to maintain your order to users.