# 边

## 简介

边，是实体间的关系（或者是关联）。例如，用户的宠物，群组的用户（成员）。

![er-group-users](https://entgo.io/assets/er_user_pets_groups.png)

在上面的例子中，你可以看到两个使用边声明的关系。让我们来实现他们。

1\. `pets` / `owner` 边; 用户的宠物和宠物的主人：

`ent/schema/user.go`

```go
package schema

import (
    "github.com/facebookincubator/ent"
    "github.com/facebookincubator/ent/schema/edge"
)

// User schema.
type User struct {
    ent.Schema
}

// Fields of the user.
func (User) Fields() []ent.Field {
    return []ent.Field{
        // ...
    }
}

// Edges of the user.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("pets", Pet.Type),
    }
}
```

`ent/schema/pet.go`

```go
package schema

import (
    "github.com/facebookincubator/ent"
    "github.com/facebookincubator/ent/schema/edge"
)

// User schema.
type Pet struct {
    ent.Schema
}

// Fields of the user.
func (Pet) Fields() []ent.Field {
    return []ent.Field{
        // ...
    }
}

// Edges of the user.
func (Pet) Edges() []ent.Edge {
    return []ent.Edge{
        edge.From("owner", User.Type).
            Ref("pets").
            Unique(),
    }
}
```

如你所见，一个 `User` 实体可以拥有 **多个** `Pet`，但是一个 `Pet` 只能被 **一个** `User` 拥有。 在关系定义时，`pets` 这条边是一个 *02M*（一对多）关系，`owner` 这条边是一个 *M20*（多对一）关系。

`User` 模式拥有 `pets/owner` 关系，因为它使用了 `edge.To`，而 `Pet` 模式只是通过 `edge.From` 和 `Ref` 方法反向引用了 `User`.

因为从一个模式到另一个模式可以有多个引用，所以用 `Ref` 方法指明 `Pet` 想要引用 `User` 中的哪一条边，

可以使用 `Unique` 方法控制边（关系）的类型，下面会有更多的说明。

2\. `users` / `groups` 边; 群组包含的用户和用户所属的群组。

`ent/schema/group.go`

```go
package schema

import (
    "github.com/facebookincubator/ent"
    "github.com/facebookincubator/ent/schema/edge"
)

// Group schema.
type Group struct {
    ent.Schema
}

// Fields of the group.
func (Group) Fields() []ent.Field {
    return []ent.Field{
        // ...
    }
}

// Edges of the group.
func (Group) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("users", User.Type),
    }
}
```

`ent/schema/user.go`

```go
package schema

import (
    "github.com/facebookincubator/ent"
    "github.com/facebookincubator/ent/schema/edge"
)

// User schema.
type User struct {
    ent.Schema
}

// Fields of the user.
func (User) Fields() []ent.Field {
    return []ent.Field{
        // ...
    }
}

// Edges of the user.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.From("groups", Group.Type).
            Ref("users"),
        // "pets" declared in the example above.
        edge.To("pets", Pet.Type),
    }
}
```

如你所见，一个群组实体可以有 **多个** 用户，并且一个用户也可以属于 **多个** 群组。 在关系定义时，`users` 这条边是一个 *M2M*（多对多）关系，`groups` 这条件也是一个 *M2M* 的关系。

## To 和 From

`edge.To` 和 `edge.From` 是两个用于创建边（关系）的构建器.

在一个关系中，使用 `edge.To` 定义边的模式，拥有该关系；而使用 `edge.From` 定义边的模式；只是反向引用了该关系。

继续看一些例子，这些例子示范了如何使用边定义不同的关系。

## 关系

* [两种类型的一对一关系](#两种类型的一对一关系)
* [同类型的一对一关系](#同类型的一对一关系)
* [双向的一对一关系](#双向的一对一关系)
* [不同类型的一对多关系](#不同类型的一对多关系)
* [同类型的一对多关系](#同类型的一对多关系)
* [不同类型的多对多关系](#不同类型的多对多关系)
* [同类型的多对多关系](#同类型的多对多关系)
* [双向的多对多关系](#双向的多对多关系)

## 两种类型的一对一关系

![er-user-card](https://entgo.io/assets/er_user_card.png)

在这个例子中，一个用户只有 **一张** 信用卡，一张信用卡只能有 **一个** 户主。

在 `User` 模式中使用 `edge.To` 为信用卡定义一条名为 `card` 的边。并在 `Card` 模式中使用 `edge.From` 为用户定义一条名为 `owner` 的逆边。

`ent/schema/user.go`

```go
// Edges of the user.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("card", Card.Type).
            Unique(),
    }
}
```

`ent/schema/card.go`

```go
// Edges of the user.
func (Card) Edges() []ent.Edge {
    return []ent.Edge{
        edge.From("owner", User.Type).
            Ref("card").
            Unique().
            // 我们在构建器中添加 `Required` 方法。
            // 使得创建实体时也必须满足这条边，即：信用卡在创建时，不能没有户主。
            Required(),
    }
}
```

下面是一些与边交互的 API：

```go
func Do(ctx context.Context, client *ent.Client) error {
    a8m, err := client.User.
        Create().
        SetAge(30).
        SetName("Mashraki").
        Save(ctx)
    if err != nil {
        return fmt.Errorf("creating user: %v", err)
    }
    log.Println("user:", a8m)
    card1, err := client.Card.
        Create().
        SetOwner(a8m).
        SetNumber("1020").
        SetExpired(time.Now().Add(time.Minute)).
        Save(ctx)
    if err != nil {
        return fmt.Errorf("creating card: %v", err)
    }
    log.Println("card:", card1)
    // 返回满足条件的用户的信用卡，并且期望的数量是 1.
    card2, err := a8m.QueryCard().Only(ctx)
    if err != nil {
        return fmt.Errorf("querying card: %v", err)
    }
    log.Println("card:", card2)
    // 在信用卡实体中，可以通过反向引用查询其户主。
    owner, err := card2.QueryOwner().Only(ctx)
    if err != nil {
        return fmt.Errorf("querying owner: %v", err)
    }
    log.Println("owner:", owner)
    return nil
}
```

完整的例子请查看 [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/o2o2types).

## 同类型的一对一关系

![er-linked-list](https://entgo.io/assets/er_linked_list.png)

在这个链表例子中，我们有一个名为 `next`/`prev` 的递归关系。链表中的每个节点都只有 **一个** `next`和 `prev` 节点。 如果，节点 A 可以通过 `next` 指向 节点 B，则节点 B 也可以通过 `prev`(反向引用) 指向节点 A。

`ent/schema/node.go`

```go
// Edges of the Node.
func (Node) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("next", Node.Type).
            Unique().
            From("prev").
            Unique(),
    }
}
```

如你所见，在同类型关系的情况下，可以在一个构建器内声明边及其引用。

```diff
func (Node) Edges() []ent.Edge {
    return []ent.Edge{
+        edge.To("next", Node.Type).
+            Unique().
+            From("prev").
+            Unique(),

-      // 不必写两次。
-        edge.To("next", Node.Type).
-            Unique(),
-        edge.From("prev", Node.Type).
-            Ref("next).
-            Unique(),
    }
}
```

下面是一些与边交互的 API：

```go
func Do(ctx context.Context, client *ent.Client) error {
    head, err := client.Node.
        Create().
        SetValue(1).
        Save(ctx)
    if err != nil {
        return fmt.Errorf("creating the head: %v", err)
    }
    curr := head
    // 下面的代码会生成链表： 1<->2<->3<->4<->5.
    for i := 0; i < 4; i++ {
        curr, err = client.Node.
            Create().
            SetValue(curr.Value + 1).
            SetPrev(curr).
            Save(ctx)
        if err != nil {
            return err
        }
    }

    // 遍历并打印列表. 如果遇到错误 `FirstX` 会 panics.
    for curr = head; curr != nil; curr = curr.QueryNext().FirstX(ctx) {
        fmt.Printf("%d ", curr.Value)
    }
    // Output: 1 2 3 4 5

    // 构建循环链表:
    // 链表的最后一个元素，没有 "next".
    tail, err := client.Node.
        Query().
        Where(node.Not(node.HasNext())).
        Only(ctx)
    if err != nil {
        return fmt.Errorf("getting the tail of the list: %v", tail)
    }
    tail, err = tail.Update().SetNext(head).Save(ctx)
    if err != nil {
        return err
    }
    // 检查修改是否生效：
    prev, err := head.QueryPrev().Only(ctx)
    if err != nil {
        return fmt.Errorf("getting head's prev: %v", err)
    }
    fmt.Printf("\n%v", prev.Value == tail.Value)
    // Output: true
    return nil
}
```

完整的例子请查看 [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/o2o2recur).

## 双向的一对一关系

![er-user-spouse](https://entgo.io/assets/er_user_spouse.png)

在这个用户-配偶例子中，我们有一个名为 `spouse` 的 **对称一对一** 关系。每个用户只能有 **一个配偶**。 如果用户 A 的配偶是（使用 `spouse` 关系） 是 B，那么也可以得知 B 的配偶（使用 `spouse` 关系）。

注意，在双向关系中，不存在拥有/属于这种说法。

`ent/schema/user.go`

```go
// Edges of the User.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("spouse", User.Type).
            Unique(),
    }
}
```

下面是一些与边交互的 API：

```go
func Do(ctx context.Context, client *ent.Client) error {
    a8m, err := client.User.
        Create().
        SetAge(30).
        SetName("a8m").
        Save(ctx)
    if err != nil {
        return fmt.Errorf("creating user: %v", err)
    }
    nati, err := client.User.
        Create().
        SetAge(28).
        SetName("nati").
        SetSpouse(a8m).
        Save(ctx)
    if err != nil {
        return fmt.Errorf("creating user: %v", err)
    }

    // 查询名为 配偶 的边
    // 不同于 `Only`, `OnlyX`遇到错误会引起 panics. 
    spouse := nati.QuerySpouse().OnlyX(ctx)
    fmt.Println(spouse.Name)
    // Output: a8m

    spouse = a8m.QuerySpouse().OnlyX(ctx)
    fmt.Println(spouse.Name)
    // Output: nati

    // 查询有配偶用户的数量。
    // 不同于 `Count`, `CountX`遇到错误会引起 panics. 
    count := client.User.
        Query().
        Where(user.HasSpouse()).
        CountX(ctx)
    fmt.Println(count)
    // Output: 2

    // 获取有配偶，且其配偶姓名为 "a8m" 的用户。
    spouse = client.User.
        Query().
        Where(user.HasSpouseWith(user.Name("a8m"))).
        OnlyX(ctx)
    fmt.Println(spouse.Name)
    // Output: nati
    return nil
}
```

完整的例子请查看 [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/o2obidi).

## 不同类型的一对多关系

![er-user-pets](https://entgo.io/assets/er_user_pets.png)

在这个 用户-宠物 的例子中，用户和宠物之间存在一个 O2M （一对多）关系。 每个用户可以有 **多个** 宠物，但是一个宠物只有 **一个** 主人（用户）。如果用户 A 通过 `pets` 边添加了一个宠物 B，那么，宠物 B 可以通过 `owner` 边（反向引用边）找到他的主人。

注意，从 `Pet` （宠物）的角度来说，这就是多对一的关系（M20,many-to-one）。

`ent/schema/user.go`

```go
// Edges of the User.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("pets", Pet.Type),
    }
}
```

`ent/schema/pet.go`

```go
// Edges of the Pet.
func (Pet) Edges() []ent.Edge {
    return []ent.Edge{
        edge.From("owner", User.Type).
            Ref("pets").
            Unique(),
    }
}
```

下面是一些与边交互的 API：

```go
func Do(ctx context.Context, client *ent.Client) error {
    // 创建两个宠物。
    pedro, err := client.Pet.
        Create().
        SetName("pedro").
        Save(ctx)
    if err != nil {
        return fmt.Errorf("creating pet: %v", err)
    }
    lola, err := client.Pet.
        Create().
        SetName("lola").
        Save(ctx)
    if err != nil {
        return fmt.Errorf("creating pet: %v", err)
    }
    // 创建用户的同时给他添加两个宠物。
    a8m, err := client.User.
        Create().
        SetAge(30).
        SetName("a8m").
        AddPets(pedro, lola).
        Save(ctx)
    if err != nil {
        return fmt.Errorf("creating user: %v", err)
    }
    fmt.Println("User created:", a8m)
    // Output: User(id=1, age=30, name=a8m)

    // 查询主人，不同于 `Only`, `OnlyX` 遇到错误时会引起 panics.
    owner := pedro.QueryOwner().OnlyX(ctx)
    fmt.Println(owner.Name)
    // Output: a8m

    // 遍历子图，不同于 `Count`, `CountX` 遇到错误时会引起 panics.
    count := pedro.
        QueryOwner(). // a8m
        QueryPets().  // pedro, lola
        CountX(ctx)   // count
    fmt.Println(count)
    // Output: 2
    return nil
}
```

完整的例子请查看 [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/o2m2types).

## 同类型的一对多关系

![er-tree](https://entgo.io/assets/er_tree.png)

In this example, we have a recursive O2M relation between tree's nodes and their children (or their parent).\
Each node in the tree **has many** children, and **has one** parent. If node A adds B to its children, B can get its owner using the `owner` edge. 这个例子中，在树的节点及其子节点（或父节点）之间存在一对多（O2M）关系的关系。 树中的每个节点有 **多个** 子节点，但是它只有 **一个** 父节点。 如果节点 A 有一个子节点 B，那么节点 B 可以通过 `owner` 边找到节点 A。

`ent/schema/node.go`

```go
// Edges of the Node.
func (Node) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("children", Node.Type).
            From("parent").
            Unique(),
    }
}
```

如你所见，在同类型关系的情况下，可以在一个构建器内声明边及其引用。

```diff
func (Node) Edges() []ent.Edge {
    return []ent.Edge{
+        edge.To("children", Node.Type).
+            From("parent").
+            Unique(),

-      // 不必写两次。
-        edge.To("children", Node.Type),
-        edge.From("parent", Node.Type).
-            Ref("children").
-            Unique(),
    }
}
```

下面是一些与边交互的 API：

```go
func Do(ctx context.Context, client *ent.Client) error {
    root, err := client.Node.
        Create().
        SetValue(2).
        Save(ctx)
    if err != nil {
        return fmt.Errorf("creating the root: %v", err)
    }
    // 构建一颗这样的树：
    //
    //       2
    //     /   \
    //    1     4
    //        /   \
    //       3     5
    //
    // 不同于 `Create`, `CreateX` 遇到错误时会引起 panics.
    n1 := client.Node.
        Create().
        SetValue(1).
        SetParent(root).
        SaveX(ctx)
    n4 := client.Node.
        Create().
        SetValue(4).
        SetParent(root).
        SaveX(ctx)
    n3 := client.Node.
        Create().
        SetValue(3).
        SetParent(n4).
        SaveX(ctx)
    n5 := client.Node.
        Create().
        SetValue(5).
        SetParent(n4).
        SaveX(ctx)

    fmt.Println("Tree leafs", []int{n1.Value, n3.Value, n5.Value})
    // Output: Tree leafs [1 3 5]

    // 获取所有叶子节点（没有子节点的节点）。
    // Unlike `Int`, `IntX` panics if an error occurs.
    // 不同于 `Int`, `IntX` 遇到错误时会引起 panics.
    ints := client.Node.
        Query().                             // 全部节点.
        Where(node.Not(node.HasChildren())). // 叶子节点.
        Order(ent.Asc(node.FieldValue)).     // 根据 `value` 字段升序排序.
        GroupBy(node.FieldValue).            // 仅提取 `value` 字段。
        IntsX(ctx)
    fmt.Println(ints)
    // Output: [1 3 5]

    // 获取孤儿节点（没有父节点的节点）。
    // Unlike `Only`, `OnlyX` panics if an error occurs.
    // 不用于 `Only`, `OnlyX` 遇到错误时会引起 panics.
    orphan := client.Node.
        Query().
        Where(node.Not(node.HasParent())).
        OnlyX(ctx)
    fmt.Println(orphan)
    // Output: Node(id=1, value=2)

    return nil
}
```

完整的例子请查看 [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/o2mrecur).

## 不同类型的多对多关系

![er-user-groups](https://entgo.io/assets/er_user_groups.png)

在这个例子中，在群组和用户之间存在一个多对多（M2M）的关系。 每个群主可以有 **多个** 用户，每个用户也可以加入 **多个** 群组。

`ent/schema/group.go`

```go
// Edges of the Group.
func (Group) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("users", User.Type),
    }
}
```

`ent/schema/user.go`

```go
// Edges of the User.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.From("groups", Group.Type).
            Ref("users"),
    }
}
```

下面是一些与边交互的 API：

```go
func Do(ctx context.Context, client *ent.Client) error {
    // 不同于 `Save`, `SaveX` 遇到错误时会引起 panics.
    hub := client.Group.
        Create().
        SetName("GitHub").
        SaveX(ctx)
    lab := client.Group.
        Create().
        SetName("GitLab").
        SaveX(ctx)
    a8m := client.User.
        Create().
        SetAge(30).
        SetName("a8m").
        AddGroups(hub, lab).
        SaveX(ctx)
    nati := client.User.
        Create().
        SetAge(28).
        SetName("nati").
        AddGroups(hub).
        SaveX(ctx)

    // 关系查询
    groups, err := a8m.
        QueryGroups().
        All(ctx)
    if err != nil {
        return fmt.Errorf("querying a8m groups: %v", err)
    }
    fmt.Println(groups)
    // Output: [Group(id=1, name=GitHub) Group(id=2, name=GitLab)]

    groups, err = nati.
        QueryGroups().
        All(ctx)
    if err != nil {
        return fmt.Errorf("querying nati groups: %v", err)
    }
    fmt.Println(groups)
    // Output: [Group(id=1, name=GitHub)]

    // 图遍历
    users, err := a8m.
        QueryGroups().                                           // [hub, lab]
        Where(group.Not(group.HasUsersWith(user.Name("nati")))). // [lab]
        QueryUsers().                                            // [a8m]
        QueryGroups().                                           // [hub, lab]
        QueryUsers().                                            // [a8m, nati]
        All(ctx)
    if err != nil {
        return fmt.Errorf("traversing the graph: %v", err)
    }
    fmt.Println(users)
    // Output: [User(id=1, age=30, name=a8m) User(id=2, age=28, name=nati)]
    return nil
}
```

完整的例子请查看 [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/m2m2types).

## 同类型的多对多关系

![er-following-followers](https://entgo.io/assets/er_following_followers.png)

下面这个 关注-粉丝 的例子，在用户及其粉丝之间存在一个多对多（M2M）的关系。 每个用户可以关注 **多个** 用户，也可以有 **多个** 粉丝。

`ent/schema/user.go`

```go
// Edges of the User.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("following", User.Type).
            From("followers"),
    }
}
```

如你所见，在同类型关系的情况下，可以在一个构建器内声明边及其引用。

```diff
func (User) Edges() []ent.Edge {
    return []ent.Edge{
+        edge.To("following", User.Type).
+            From("followers"),

-       // 不必写两次
-        edge.To("following", User.Type),
-        edge.From("followers", User.Type).
-            Ref("following"),
    }
}
```

下面是一些与边交互的 API：

```go
func Do(ctx context.Context, client *ent.Client) error {
    // 不同于 `Save`, `SaveX` 遇到错误时会引起 panics.
    a8m := client.User.
        Create().
        SetAge(30).
        SetName("a8m").
        SaveX(ctx)
    nati := client.User.
        Create().
        SetAge(28).
        SetName("nati").
        AddFollowers(a8m).
        SaveX(ctx)

    // 查询关注/粉丝列表:

    flw := a8m.QueryFollowing().AllX(ctx)
    fmt.Println(flw)
    // Output: [User(id=2, age=28, name=nati)]

    flr := a8m.QueryFollowers().AllX(ctx)
    fmt.Println(flr)
    // Output: []

    flw = nati.QueryFollowing().AllX(ctx)
    fmt.Println(flw)
    // Output: []

    flr = nati.QueryFollowers().AllX(ctx)
    fmt.Println(flr)
    // Output: [User(id=1, age=30, name=a8m)]

    // 图遍历:

    ages := nati.
        QueryFollowers().       // [a8m]
        QueryFollowing().       // [nati]
        GroupBy(user.FieldAge). // [28]
        IntsX(ctx)
    fmt.Println(ages)
    // Output: [28]

    names := client.User.
        Query().
        Where(user.Not(user.HasFollowers())).
        GroupBy(user.FieldName).
        StringsX(ctx)
    fmt.Println(names)
    // Output: [a8m]
    return nil
}
```

完整的例子请查看 [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/m2mrecur).

## 双向的多对多关系

![er-user-friends](https://entgo.io/assets/er_user_friends.png)

In this user-friends example, we have a **symmetric M2M relation** named `friends`. Each user can **have many** friends. If user A becomes a friend of B, B is also a friend of A. 在这个 用户-朋友 的例子中，存在一个名为 `freiends` 的双向多对多关系。 每个用户可以有 **多个** 朋友。如果用户 A 是用户 B 的朋友，那么用户 B 也肯定是用户 A 的朋友。

注意，在双向关系中，不存在拥有/属于这种说法。

`ent/schema/user.go`

```go
// Edges of the User.
func (User) Edges() []ent.Edge {
    return []ent.Edge{
        edge.To("friends", User.Type),
    }
}
```

下面是一些与边交互的 API：

```go
func Do(ctx context.Context, client *ent.Client) error {
    // 不同于 `Save`, `SaveX` 遇到错误时会引起 panics.
    a8m := client.User.
        Create().
        SetAge(30).
        SetName("a8m").
        SaveX(ctx)
    nati := client.User.
        Create().
        SetAge(28).
        SetName("nati").
        AddFriends(a8m).
        SaveX(ctx)

    // 查询朋友列表。不同于 `All`, `AllX` 遇到错误是会引起 panics.
    friends := nati.
        QueryFriends().
        AllX(ctx)
    fmt.Println(friends)
    // Output: [User(id=1, age=30, name=a8m)]

    friends = a8m.
        QueryFriends().
        AllX(ctx)
    fmt.Println(friends)
    // Output: [User(id=2, age=28, name=nati)]

    // 图遍历：
    friends = client.User.
        Query().
        Where(user.HasFriends()).
        AllX(ctx)
    fmt.Println(friends)
    // Output: [User(id=1, age=30, name=a8m) User(id=2, age=28, name=nati)]
    return nil
}
```

完整的例子请查看 [GitHub](https://github.com/facebookincubator/ent/tree/master/examples/m2mbidi).

## 必选项

Edges can be defined as required in the entity creation using the `Required` method on the builder. 可以使用构建器中的 `Required` 方法定义关系，使得实体创建时必须满足该关系。

```go
// Edges of the user.
func (Card) Edges() []ent.Edge {
    return []ent.Edge{
        edge.From("owner", User.Type).
            Ref("card").
            Unique().
            Required(),
    }
}
```

比如说，无法创建一张没有户主的信用卡。

## 索引

可以在多个字段或者某些边上添加索引。但是，需要注意的是，目前只有 SQL 支持索引特性。

更多关于索引的内容，可以查阅 [索引](https://3ks.gitbook.io/ent-zh_cn/schema/schema-indexes) 部分。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://3ks.gitbook.io/ent-zh_cn/schema/schema-edges.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
