Eager Loading

Overview

ent supports querying entities with their associations (through their edges). The associated entities are populated to the Edges field in the returned object.

Let’s give an example of what the API looks like for the following schema:

er-group-users

Query all users with their pets:

  1. users, err := client.User.
  2. Query().
  3. WithPets().
  4. All(ctx)
  5. if err != nil {
  6. return err
  7. }
  8. // The returned users look as follows:
  9. //
  10. // [
  11. // User {
  12. // ID: 1,
  13. // Name: "a8m",
  14. // Edges: {
  15. // Pets: [Pet(...), ...]
  16. // ...
  17. // }
  18. // },
  19. // ...
  20. // ]
  21. //
  22. for _, u := range users {
  23. for _, p := range u.Edges.Pets {
  24. fmt.Printf("User(%v) -> Pet(%v)\n", u.ID, p.ID)
  25. // Output:
  26. // User(...) -> Pet(...)
  27. }
  28. }

Eager loading allows to query more than one association (including nested), and also filter, sort or limit their result. For example:

  1. admins, err := client.User.
  2. Query().
  3. Where(user.Admin(true)).
  4. // Populate the `pets` that associated with the `admins`.
  5. WithPets().
  6. // Populate the first 5 `groups` that associated with the `admins`.
  7. WithGroups(func(q *ent.GroupQuery) {
  8. q.Limit(5) // Limit to 5.
  9. q.WithUsers() // Populate the `users` of each `groups`.
  10. }).
  11. All(ctx)
  12. if err != nil {
  13. return err
  14. }
  15. // The returned users look as follows:
  16. //
  17. // [
  18. // User {
  19. // ID: 1,
  20. // Name: "admin1",
  21. // Edges: {
  22. // Pets: [Pet(...), ...]
  23. // Groups: [
  24. // Group {
  25. // ID: 7,
  26. // Name: "GitHub",
  27. // Edges: {
  28. // Users: [User(...), ...]
  29. // ...
  30. // }
  31. // }
  32. // ]
  33. // }
  34. // },
  35. // ...
  36. // ]
  37. //
  38. for _, admin := range admins {
  39. for _, p := range admin.Edges.Pets {
  40. fmt.Printf("Admin(%v) -> Pet(%v)\n", u.ID, p.ID)
  41. // Output:
  42. // Admin(...) -> Pet(...)
  43. }
  44. for _, g := range admin.Edges.Groups {
  45. for _, u := range g.Edges.Users {
  46. fmt.Printf("Admin(%v) -> Group(%v) -> User(%v)\n", u.ID, g.ID, u.ID)
  47. // Output:
  48. // Admin(...) -> Group(...) -> User(...)
  49. }
  50. }
  51. }

API

Each query-builder has a list of methods in the form of With<E>(...func(<N>Query)) for each of its edges. <E> stands for the edge name (like, WithGroups) and <N> for the edge type (like, GroupQuery).

Note that, only SQL dialects support this feature.

Implementation

Since a query-builder can load more than one association, it’s not possible to load them using one JOIN operation. Therefore, ent executes additional queries for loading associations. One query for M2O/O2M and O2O edges, and 2 queries for loading M2M edges.

Note that, we expect to improve this in the next versions of ent.