Relationships & Embedded Objects - React Native SDK¶
Relationships¶
MongoDB Realm allows you to define explicit relationships between the types of objects in a Realm app. A relationship is an object property that references another Realm object type instead of a property type. You define relationships by setting the type of an object property to another object type in the property schema.
Relationships are direct references to other objects in a realm, which means that you don't need bridge tables or explicit joins to define a relationship like you would in a relational database. Instead you can access related objects by reading and writing to the property that defines the relationship. Realm Database executes read operations lazily as they come in, so querying a relationship is just as performant as reading a regular property.
There are three primary types of relationships between objects:
Many databases copy objects from database storage into application memory when you need to work with them directly. Objects often contain direct references to other objects, which leaves the developer with a choice of what to copy into memory:
- You can copy the entire referenced object ahead of time. This means that all referenced data is always available quickly but can use up a lot of resources for objects that are never accessed. Depending on the amount of available memory this may not be viable.
- You can copy only a foreign key value for each object ahead of time that you can use to query the full object if it's needed. These referenced lookups are memory-efficient but they require additional query code and too many lookups can slow your application down.
Realm's zero-copy query architecture avoids this tradeoff between memory footprint and runtime computational overhead. Instead, Realm queries can directly reference related objects and their properties on disk.
To-One Relationship¶
A to-one relationship means that an object is related in a specific way to no more than one other object. You define a to-one relationship for an object type in its object schema by specifying a property where the type is the related Realm object type.
An application could use the following object schemas to indicate
that a Person may or may not own a single Dog by including it in its
dog
property:
const Person = { name: "Person", properties: { name: "string", birthdate: "date", dog: "Dog?" } }; const Dog = { name: "Dog", properties: { name: "string", age: "int", breed: "string?" } };
To-Many Relationship¶
A to-many relationship means that an object is related in a specific way to multiple objects. You define a to-many relationship for an object type in its object schema by specifying a property where the type is a list or array of the related Realm object type.
An application could use the following object schemas to indicate
that a Person may own multiple Dogs by including them in its dog
property:
const Person = { name: "Person", properties: { name: "string", birthdate: "date", dogs: "Dog[]" } }; const Dog = { name: "Dog", properties: { name: "string", age: "int", breed: "string?" } };
Inverse Relationship¶
An inverse relationship links an object back to any other objects that refer to it in a defined to-one or to-many relationship. Relationship definitions are unidirectional, so you must explicitly define a property in the object's model as an inverse relationship.
For example, the to-many relationship "User has many Tasks" does not automatically create the inverse relationship "Task belongs to User". If you don't specify the inverse relationship in the object model, you would need to run a separate query to look up the user that is assigned to a given task.
To define an inverse relationship, define a linkingObjects
property in your
object model. The linkingObjects
definition specifies the object type and
property name of the relationship that it inverts.
You cannot manually set the value of an inverse relationship property. Realm Database automatically updates implicit relationships whenever an object is added or removed in the specified relationship.
An application could use the following object schemas to indicate that a User may be assigned many Tasks and that each Task should automatically keep track of which User it's assigned to.
- The User object's
tasks
property is defined as a to-many relationship with Task objects and contains all of a given user's assigned tasks. - The Task object's
assignee
property inverts the relationship and automatically updates to refer back to any User object that contains the Task in itstasks
property.
class User { public _id: ObjectId = ""; public _partition: string = ""; public name: string = ""; public tasks: Realm.List<Task>; public static schema: Realm.ObjectSchema = { name: "User", primaryKey: "_id", properties: { _id: "objectId", _partition: "string", name: "string", tasks: "Task[]" } }; } class Task { public _id: ObjectId = ""; public _partition: string = ""; public text: string; public assignee: Realm.Results<User>; public static schema: Realm.ObjectSchema = { name: "Task", primaryKey: "_id", properties: { _id: "objectId", _partition: "string", text: "string", assignee: { type: 'linkingObjects', objectType: 'User', property: 'tasks' } } }; };
Embedded Objects¶
An embedded object is a special type of Realm object that models complex data about a specific object. Embedded objects are similar to relationships, but they provide additional constraints and map more naturally to the denormalized MongoDB document model.
Realm enforces unique ownership constraints that treat each embedded object as nested data inside of a single, specific parent object. An embedded object inherits the lifecycle of its parent object and cannot exist as an independent Realm object. This means that embedded objects cannot have a primary key and that Realm automatically deletes embedded objects if their parent object is deleted.
You can use the same embedded object type in multiple parent object types and you can embed objects inside of other embedded objects. You can even recursively reference an embedded object type as an optional property in its own definition.
Realm Object Models¶
To specify that a Realm object model define an embedded object, set embedded
to true
. You can reference an embedded object type from parent object types
in the same way as you would define a relationship:
const AddressSchema = { name: "Address", embedded: true, // default: false properties: { street: "string?", city: "string?", country: "string?", postalCode: "string?", }, }; const ContactSchema = { name: "Contact", primaryKey: "_id", properties: { _id: "objectId", name: "string", address: "Address", // Embed a single object }, }; const BusinessSchema = { name: "Business", primaryKey: "_id", properties: { _id: "objectId", name: "string", addresses: { type: "list", objectType: "Address" }, // Embed an array of objects }, };
JSON Schema¶
Unlike regular Realm objects, which map to their own MongoDB collection, embedded objects map to embedded documents in the parent type's document schema:
{ "title": "Contact", "bsonType": "object", "required": ["_id"], "properties": { "_id": "objectId", "name": "string", "address": { "title": "Address", "bsonType": "object", "properties": { "street": "string", "city": "string", "country": "string", "postalCode": "string" } } } }
{ "title": "Business", "bsonType": "object", "required": ["_id", "name", "addresses"], "properties": { "_id": "objectId", "name": "string", "addresses": { "bsonType": "array", "items": { "title": "Address", "bsonType": "object", "properties": { "street": "string", "city": "string", "country": "string", "postalCode": "string" } } } } }