# Relations

Objects may reference other objects, for example using a simple reference or a list of objects. In database terms, we call those references **relations**. The object defining the relation we call the **source** object, the referenced object we call **target** object. So the relation has a direction.

If there is one target object, we call the relation **to-one.** And if there can be multiple target objects, we call it **to-many**. Relations are lazily initialized: the actual target objects are fetched from the database when they are first accessed. Once the target objects are fetched, they are cached for further accesses.

## To-One Relations

![To-One Relation](https://2249461369-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LR7sVOoZyL01-NkNVQJ%2Fuploads%2FzS0OKRalAozGxUbH88pS%2Fto-one-relations-2.png?alt=media\&token=f90889d9-aba6-4b7d-96b4-1b1131286f6f)

You define a to-one relation using the `ToOne` class, a smart proxy to the target object. It gets and caches the target object transparently.&#x20;

For example, an order is typically made by one customer. Thus, we could model the  `Order` class to have a to-one relation to the `Customer` like this:

```swift
class Customer: Entity {
    var id: Id = 0
    // ...
}

class Order: Entity {
    var id: Id = 0
    var customer: ToOne<Customer> = nil
    // ...
}
```

Given these entities and their to-one relation, you can create a relation and persist it:

```swift
let store = ...
// Illustrate that initially, nothing did exist
assert(try store.box(for: Customer.self).isEmpty())
assert(try store.box(for: Order.self).isEmpty())

let customer = Customer()
let order = Order()
order.customer.target = customer
let orderId = try store.box(for: Order.self).put(order) // puts order and customer

// Verify the `put` was called for the relation target as well
assert(try store.box(for: Customer.self).count() == 1)
assert(try store.box(for: Order.self).count() == 1)
```

{% hint style="info" %}
You can persist whole trees of object relations at once: If the customer object does not yet exist in the database, the `ToOne` will `put()` it. If it already exists, the `ToOne` will only create the relation (but not `put()` it).
{% endhint %}

For a comprehensive documentation of its features, visit [the ToOne API docs](https://objectbox.io/docfiles/swift/current/Classes/ToOne.html).

### Removing Relations

You have to set the target of a relation to `nil` and persist the changed object(s) via `Box.put()`to remove a relation permanently. Which one you reset doesn't matter, though. You have these options:

```swift
anOrder.customer.target = nil
// ... or ...
anOrder.customer.targetId = nil
// ... are both equivalent to:
anOrder.customer = nil
// ... whis is a short version of:
anOrder.customer = ToOne<Customer>(target: nil)
```

{% hint style="info" %}
When using structs, to update a ToOne, make sure to get a fresh copy of the Object from its Box, like `box.get(order.id)`. See the section about [Using Relations with Structs](#using-relations-with-structs).
{% endhint %}

{% hint style="info" %}
Removing a relation never removes participating objects.
{% endhint %}

### `ToOne` is a Lazy Relation Proxy

The target object of a relation is not eagerly loaded from the store; it is loaded lazily. Until you request the `ToOne.target`, it will not be read into main memory.

## To-Many Relations

To define a to-many relation, you can use a property of type `ToMany`. Like the ToOne class, the `ToMany`  class helps you to keep track of changes and to apply them to the database.

Note that **to-many relations are resolved lazily** on first access, and then **cached** in the source entity inside the `ToMany` object. So subsequent calls to any method, like the `count` of the `ToMany`, do not query the database, even if the relation was changed elsewhere. To avoid the cache and trigger a fresh reload from the database, call `reset()` on the `ToMany`.

There is a slight difference if you require a one-to-many (1:N) or many-to-many (N:M) relation. A 1:N relation is like the example above where a customer can have multiple orders, but an order is only associated with a single customer. An example for an N:M relation are students and teachers: students can have classes by several teachers but a teacher can also instruct several students.

### One-to-Many Relations (1:N)

<figure><img src="https://2249461369-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LR7sVOoZyL01-NkNVQJ%2Fuploads%2FvWmhHPeCiACnUaOhd7Fg%2Fone-to-many.png?alt=media&#x26;token=16309acf-b47a-4a04-b1f7-457a23037e8d" alt="one-to-many relations"><figcaption><p>One-To-Many Relation</p></figcaption></figure>

For every `ToOne` relation that you have, you can define a backlink. Backlinks are using the same relation information, but in the reverse direction. Thus, a backlink of a `ToOne` will result in a list of potentially multiple objects: all entities pointing to the same entity.\
**Example:** Two Order objects point to the same Customer using a `ToOne`. The backlink is a `ToMany` from the Customer referencing its two Order objects.

Let's extend the example from above to get the backlinks from `Customer` to `Order`:

```swift
class Customer: Entity {
    var id: Id = 0
    // objectbox: backlink = "customer"
    var orders: ToMany<Order> = nil
    // ...
}

class Order: Entity {
    var id: Id = 0
    var customer: ToOne<Customer> = nil
    // ...
}
```

Note you tell ObjectBox about your backlink using an `// objectbox: backlink = "name"` annotation (where *name* is the name of the ToOne property that makes up the other end of the relation).

Once the backlink is set up, you can traverse the relation in both directions:

```swift
// Store two new orders for a new customer
let customer = Customer()
let order1 = Order(customer: customer)
let order2 = Order(customer: customer)
try store.box(for: Order.self).put([order1, order2])

// ID of customer was also set by put()
assert(customer.id != 0)

// Backlink: customer has two orders
assert(try store.box(for: Customer.self).get(customer.id).orders.count() == 2)
```

{% hint style="info" %}
In database terminology, you create a (bi-directional) **one-to-many (1:N) relationship** by defining a ToOne with a backlink ToMany.
{% endhint %}

### Collection Nature of `ToMany`

`ToMany` conforms to Swift's `RandomAccessCollection` protocol. Thus, you can use it just like an array or similar collections and pass it around. And of course, you can create an Array if need be:

```swift
let orders = Array(customer.orders)
```

See also: [ToMany API docs](https://objectbox.io/docfiles/swift/current/Classes/ToMany.html)

### Modifying One-to-Many Relations

Apart from being a RandomAccessCollection, a `ToMany` relation is also a `RangeReplaceableCollection`. That means you can use `append(_:)` etc. on it just like on an `Array` to modify it. We've also added a `replace(_:)` method as a convenience to replace all entities referenced by the relation.

Once you've performed all the modifications you want, call `applyToDb()` on the `ToMany` to actually cause them to be written to the database. Note that `ToMany` applies change tracking and thus only writes updated relations to the database.

When you change its contents, `ToMany` will simply set the `ToOne` relation in the removed entities to `nil`, and will make added entities' `ToOne` point at the object containing the `ToMany` backlink. Note that, starting from version 1.4, you can add new (not yet persisted) objects, which `applyToDb()` will put automatically :

```swift
let newOrder = Order(summary: "Shoes")
// try orderBox.put(newOrder) // ObjectBox Swift 1.4+ does not need this
aCustomer.orders.replace([newOrder, oldOrder])
try aCustomer.orders.applyToDb()
```

{% hint style="info" %}
When using structs, to update a ToMany, make sure to get a fresh copy of the Object from its Box, like `box.get(aCustomer.id)`. See the section about [Using Relations with Structs](#using-relations-with-structs).
{% endhint %}

Also, modifying a `ToMany` backlink modifies the `ToOne` of the referenced entities (in this example, `newOrder` and `oldOrder`) and will `put()` those objects to actually write out the changed relation. To remove all references to an entity, you may pass an empty array to `replace()`:

```swift
// You cannot set `aCustomer.orders = nil`, so:
aCustomer.orders.replace([])
try aCustomer.orders.applyToDb()
```

{% hint style="info" %}
Removing a relation never removes the referenced objects from the database.
{% endhint %}

### Many-to-Many (N:M)

![Many-to-Many (N:M)](https://2249461369-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-LR7sVOoZyL01-NkNVQJ%2Fuploads%2FbeNLffNQR3Abcp9DzDLd%2Fmany-to-many2.png?alt=media\&token=33619cf8-c31e-465f-b91e-1571e40a2608)

To define a many-to-many relation you simply add a property using the `ToMany` class. Assuming a *students and teachers* example, this is how a simple `Student` class that has a to-many relation to `Teacher` entities can look like:

```swift
class Teacher: Entity {
    var id: Id = 0
    
    ...
}

class Student: Entity {
    var id: Id = 0
    var ToMany<Teacher> teachers = nil
    
    ...
}
```

**Adding** the teachers of a student works exactly like with an array, or a one-to-many relation:

```swift
let teacher1 = Teacher()
let teacher2 = Teacher()

let student1 = Student()
let student2 = Student()

// try store.box(for: Teacher.self).put([teacher1, teacher2])
try store.box(for: Student.self).put([student1, student2])

student1.teachers.append(teacher1)
student1.teachers.append(teacher2)

student2.teachers.append(teacher2)

try student1.teachers.applyToDb()
try student2.teachers.applyToDb()
```

To **get** the teachers of a student we just access the list:

```swift
var student1 = try boxStore.box(for: Student.self).get(student1.id)
for (let teacher in student1.teachers) {
    ...
}
```

And if a student drops out of a class, we can **remove** a teacher:

```swift
student1.teachers.remove(at: 0)
try student1.teachers.applyToDb()
```

{% hint style="info" %}
When using structs, to update a ToMany, make sure to get a fresh copy of the Object from its Box, like `box.get(student1.id)`. See the section about [Using Relations with Structs](#using-relations-with-structs).
{% endhint %}

{% hint style="info" %}
Removing a relation never removes the referenced objects from the database.
{% endhint %}

#### Access Many-To-Many in the reverse direction

Following the above example, you might want an easy way to find out what students a teacher has. Instead of having to perform a query, you can just add a to-many relation to the teacher and annotate it with the `// objectbox: backlink` annotation:

```swift
class Teacher: Entity {
    var id: Id = 0
    // objectbox: backlink = "teachers"
    var students: ToMany<Student> = nil
    ...
}

class Student: Entity {
    var id: Id = 0
    var teachers: ToMany<Teacher> = nil
    
    ...
}
```

This will tell ObjectBox that there is only one relation, `teachers`, and that `students` is just a reverse-lookup of this relation. In any other respect, a many-to-many backlink can be used just like its forward counterpart.

## Using Relations with Structs

When using structs, be aware that **after inserting an Object with `put` its ToOne and ToMany fields can't be updated**. To update a ToOne or ToMany relation always get a fresh copy of the Object from its Box:

```swift
// Insert a student
try box.put(&student)
// To update its ToMany (applies to ToOne as well), get a fresh copy
let updatableStudent = try box.get(student.id)
updatableStudent.teachers.append(teacher)
try updatableStudent.teachers.applyToDb()
```

## Relations in Queries

You can [traverse relations in queries](https://swift.objectbox.io/queries#building-queries-traversing-relations).

## Example: Modelling Tree Relations

You can model a tree relation with a to-one and a to-many relation pointing to itself:

```swift
class TreeNode: Entity {
    var id: Id = 0
    
    var parent: ToOne<TreeNode> = nil
    
    // objectbox: backlink = "parent"
    var children: ToMany<TreeNode> = nil
}
```

The generated entity lets you navigate its parent and children:

```swift
let parent = entity.parent.target
let children = Array(entity.children)
```
