跳到主要内容

预加载

概览

ent 支持通过实体的关联进行查询(通过他们的边)。关联实体被填充到返回对象的 Edges 字段中。

让我们用一个示例看一下下面模式 API 是什么样的:

er-group-users

查询全部用户及他们的宠物

users, err := client.User.
Query().
WithPets().
All(ctx)
if err != nil {
return err
}
// The returned users look as follows:
//
// [
// User {
// ID: 1,
// Name: "a8m",
// Edges: {
// Pets: [Pet(...), ...]
// ...
// }
// },
// ...
// ]
//
for _, u := range users {
for _, p := range u.Edges.Pets {
fmt.Printf("User(%v) -> Pet(%v)\n", u.ID, p.ID)
// Output:
// User(...) -> Pet(...)
}
}

预加载允许查询不止一种关联(包括嵌套关联),并过滤、排序或数量限制查询结果,例如:

admins, err := client.User.
Query().
Where(user.Admin(true)).
// Populate the `pets` that associated with the `admins`.
WithPets().
// Populate the first 5 `groups` that associated with the `admins`.
WithGroups(func(q *ent.GroupQuery) {
q.Limit(5) // Limit to 5.
q.WithUsers() // Populate the `users` of each `groups`.
}).
All(ctx)
if err != nil {
return err
}

// The returned users look as follows:
//
// [
// User {
// ID: 1,
// Name: "admin1",
// Edges: {
// Pets: [Pet(...), ...]
// Groups: [
// Group {
// ID: 7,
// Name: "GitHub",
// Edges: {
// Users: [User(...), ...]
// ...
// }
// }
// ]
// }
// },
// ...
// ]
//
for _, admin := range admins {
for _, p := range admin.Edges.Pets {
fmt.Printf("Admin(%v) -> Pet(%v)\n", u.ID, p.ID)
// Output:
// Admin(...) -> Pet(...)
}
for _, g := range admin.Edges.Groups {
for _, u := range g.Edges.Users {
fmt.Printf("Admin(%v) -> Group(%v) -> User(%v)\n", u.ID, g.ID, u.ID)
// Output:
// Admin(...) -> Group(...) -> User(...)
}
}
}

API

每个查询构建器都在 With<E>(...func(<N>Query)) 列表中有针对每条边的一系列方法。 <E> 表示边名称(如 WithGroups),<N> 表示边类型(如 GroupQuery)。

注意只有SQL方言支持这种特性。

命名边

有些情况下需要预加载自定义名称的边。例如 GraphQL 查询有引用同一边但是不同参数的别名。 为解决这个问题,Ent 另外提供一个名为 WithNamed<E> 的 API 可以使用 namedges 功能开关并与 EntGQL 字段集 无缝集成。

请参阅 GraphQL 选项卡,了解此 API 背后的设计理念。

posts, err := client.Post.Query().
WithNamedComments("published", func(q *ent.CommentQuery) {
q.Where(comment.StatusEQ(comment.StatusPublished))
})
WithNamedComments("draft", func(q *ent.CommentQuery) {
q.Where(comment.StatusEQ(comment.StatusDraft))
}).
Paginate(...)

// Get the preloaded edges by their name:
for _, p := range posts {
published, err := p.Edges.NamedComments("published")
if err != nil {
return err
}
draft, err := p.Edges.NamedComments("draft")
if err != nil {
return err
}
}

实现

由于Ent查询可预先加载多条边,因此无法通过单次 JOIN 操作加载所有关联关系。 为此,Ent 会执行额外查询来加载每条关联关系。此行为预计将在未来版本中得到优化。