From 307eb5e30bfbbd27efabab1e89ffe41116881a18 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki <7413593+a8m@users.noreply.github.com> Date: Mon, 23 Jan 2023 15:53:33 +0200 Subject: [PATCH] doc/edges: add erds to examples (#3268) --- doc/md/schema-edges.mdx | 197 ++++++++++++++++++++++-- examples/o2mrecur/ent/migrate/schema.go | 2 +- examples/o2mrecur/ent/mutation.go | 90 +++++++++-- examples/o2mrecur/ent/node.go | 19 +-- examples/o2mrecur/ent/node/node.go | 18 +-- examples/o2mrecur/ent/node/where.go | 35 +++++ examples/o2mrecur/ent/node_create.go | 16 +- examples/o2mrecur/ent/node_query.go | 25 +-- examples/o2mrecur/ent/node_update.go | 40 +++-- examples/o2mrecur/ent/schema/node.go | 3 + examples/o2mrecur/example_test.go | 4 +- examples/o2orecur/ent/migrate/schema.go | 2 +- examples/o2orecur/ent/mutation.go | 90 +++++++++-- examples/o2orecur/ent/node.go | 19 +-- examples/o2orecur/ent/node/node.go | 18 +-- examples/o2orecur/ent/node/where.go | 35 +++++ examples/o2orecur/ent/node_create.go | 16 +- examples/o2orecur/ent/node_query.go | 25 +-- examples/o2orecur/ent/node_update.go | 40 +++-- examples/o2orecur/ent/schema/node.go | 3 + 20 files changed, 527 insertions(+), 170 deletions(-) diff --git a/doc/md/schema-edges.mdx b/doc/md/schema-edges.mdx index fc0d85bfc..e18da490c 100644 --- a/doc/md/schema-edges.mdx +++ b/doc/md/schema-edges.mdx @@ -8,16 +8,33 @@ import TabItem from '@theme/TabItem'; ## Quick Summary -Edges are the relations (or associations) of entities. For example, user's pets, or group's users. +Edges are the relations (or associations) of entities. For example, user's pets, or group's users: + + ![er-group-users](https://entgo.io/images/assets/er_user_pets_groups.png) + + + +[![erd-group-users](https://entgo.io/images/assets/erd/edges-quick-summary.png)](https://gh.atlasgo.cloud/explore/38be6c19) + +
+ ERD was generated by Atlas +
+ +
+
+ In the example above, you can see 2 relations declared using edges. Let's go over them. -1\. `pets` / `owner` edges; user's pets and pet's owner - +1\. `pets` / `owner` edges; user's pets and pet's owner: -```go title="ent/schema/user.go" + + + +```go title="ent/schema/user.go" {23} package schema import ( @@ -44,8 +61,10 @@ func (User) Edges() []ent.Edge { } } ``` + + -```go title="ent/schema/pet.go" +```go title="ent/schema/pet.go" {23-25} package schema import ( @@ -74,6 +93,8 @@ func (Pet) Edges() []ent.Edge { } } ``` + + As you can see, a `User` entity can **have many** pets, but a `Pet` entity can **have only one** owner. In relationship definition, the `pets` edge is a *O2M* (one-to-many) relationship, and the `owner` edge @@ -88,9 +109,12 @@ references from one schema to other. The cardinality of the edge/relationship can be controlled using the `Unique` method, and it's explained more widely below. -2\. `users` / `groups` edges; group's users and user's groups - +2\. `users` / `groups` edges; group's users and user's groups: -```go title="ent/schema/group.go" + + + +```go title="ent/schema/group.go" {23} package schema import ( @@ -117,8 +141,10 @@ func (Group) Edges() []ent.Edge { } } ``` + + -```go title="ent/schema/user.go" +```go title="ent/schema/user.go" {23-24} package schema import ( @@ -148,6 +174,8 @@ func (User) Edges() []ent.Edge { } } ``` + + As you can see, a Group entity can **have many** users, and a User entity can have **have many** groups. In relationship definition, the `users` edge is a *M2M* (many-to-many) relationship, and the `groups` @@ -175,13 +203,31 @@ Let's go over a few examples that show how to define different relation types us ## O2O Two Types + + + ![er-user-card](https://entgo.io/images/assets/er_user_card.png) + + + +[![edges-o2o-two-types](https://entgo.io/images/assets/erd/edges-o2o-two-types.png)](https://gh.atlasgo.cloud/explore/b6264668) + +
+ ERD was generated by Atlas +
+ +
+
+ In this example, a user **has only one** credit-card, and a card **has only one** owner. The `User` schema defines an `edge.To` card named `card`, and the `Card` schema defines a back-reference to this edge using `edge.From` named `owner`. + + + ```go title="ent/schema/user.go" // Edges of the user. func (User) Edges() []ent.Edge { @@ -191,6 +237,8 @@ func (User) Edges() []ent.Edge { } } ``` + + ```go title="ent/schema/card.go" // Edges of the Card. @@ -206,6 +254,8 @@ func (Card) Edges() []ent.Edge { } } ``` + + The API for interacting with these edges is as follows: ```go @@ -251,8 +301,23 @@ The full example exists in [GitHub](https://github.com/ent/ent/tree/master/examp ## O2O Same Type + + + ![er-linked-list](https://entgo.io/images/assets/er_linked_list.png) + + + +[![edges-linked-list](https://entgo.io/images/assets/erd/edges-o2o-same-type.png)](https://gh.atlasgo.cloud/explore/6645164b) + +
+ ERD was generated by Atlas +
+ +
+
+ In this linked-list example, we have a **recursive relation** named `next`/`prev`. Each node in the list can **have only one** `next` node. If a node A points (using `next`) to node B, B can get its pointer using `prev` (the back-reference edge). @@ -346,8 +411,23 @@ The full example exists in [GitHub](https://github.com/ent/ent/tree/master/examp ## O2O Bidirectional + + + ![er-user-spouse](https://entgo.io/images/assets/er_user_spouse.png) + + + +[![edges-o2o-bidirectional](https://entgo.io/images/assets/erd/edges-o2o-bidirectional.png)](https://gh.atlasgo.cloud/explore/e2e76d55) + +
+ ERD was generated by Atlas +
+ +
+
+ In this user-spouse example, we have a **symmetric O2O relation** named `spouse`. Each user can **have only one** spouse. If user A sets its spouse (using `spouse`) to B, B can get its spouse using the `spouse` edge. @@ -441,15 +521,33 @@ The full example exists in [GitHub](https://github.com/ent/ent/tree/master/examp ## O2M Two Types + + + ![er-user-pets](https://entgo.io/images/assets/er_user_pets.png) + + + +[![edges-o2m-two-types](https://entgo.io/images/assets/erd/edges-o2m-two-types.png)](https://gh.atlasgo.cloud/explore/8a360fce) + +
+ ERD was generated by Atlas +
+ +
+
+ In this user-pets example, we have a O2M relation between user and its pets. Each user **has many** pets, and a pet **has one** owner. If user A adds a pet B using the `pets` edge, B can get its owner using the `owner` edge (the back-reference edge). Note that this relation is also a M2O (many-to-one) from the point of view of the `Pet` schema. -```go title="ent/schema/user.go" + + + +```go title="ent/schema/user.go" {4} // Edges of the User. func (User) Edges() []ent.Edge { return []ent.Edge{ @@ -457,8 +555,10 @@ func (User) Edges() []ent.Edge { } } ``` + + -```go title="ent/schema/pet.go" +```go title="ent/schema/pet.go" {4-6} // Edges of the Pet. func (Pet) Edges() []ent.Edge { return []ent.Edge{ @@ -468,6 +568,8 @@ func (Pet) Edges() []ent.Edge { } } ``` + + The API for interacting with these edges is as follows: @@ -520,7 +622,7 @@ func Do(ctx context.Context, client *ent.Client) error { Note that, the foreign-key column can be configured and exposed as an entity field using the [Edge Field](#edge-field) option as follows: -```go {4,15} +```go title="ent/schema/pet.go" {4,15} // Fields of the Pet. func (Pet) Fields() []ent.Field { return []ent.Field{ @@ -544,8 +646,23 @@ The full example exists in [GitHub](https://github.com/ent/ent/tree/master/examp ## O2M Same Type + + + ![er-tree](https://entgo.io/images/assets/er_tree.png) + + + +[![edges-o2m-same-type](https://entgo.io/images/assets/erd/edges-o2m-same-type.png)](https://gh.atlasgo.cloud/explore/74c74ac7) + +
+ ERD was generated by Atlas +
+ +
+
+ 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. @@ -674,8 +791,23 @@ The full example exists in [GitHub](https://github.com/ent/ent/tree/master/examp ## M2M Two Types + + + ![er-user-groups](https://entgo.io/images/assets/er_user_groups.png) + + + +[![edges-m2m-two-types](https://entgo.io/images/assets/erd/edges-m2m-two-types.png)](https://gh.atlasgo.cloud/explore/25b70c29) + +
+ ERD was generated by Atlas +
+ +
+
+ In this groups-users example, we have a M2M relation between groups and their users. Each group **has many** users, and each user can be joined to **many** groups. @@ -780,8 +912,23 @@ The full example exists in [GitHub](https://github.com/ent/ent/tree/master/examp ## M2M Same Type + + + ![er-following-followers](https://entgo.io/images/assets/er_following_followers.png) + + + +[![edges-m2m-same-type](https://entgo.io/images/assets/erd/edges-m2m-same-type.png)](https://gh.atlasgo.cloud/explore/8cb4fd4e) + +
+ ERD was generated by Atlas +
+ +
+
+ In this following-followers example, we have a M2M relation between users to their followers. Each user can follow **many** users, and can have **many** followers. @@ -888,8 +1035,23 @@ The full example exists in [GitHub](https://github.com/ent/ent/tree/master/examp ## M2M Bidirectional + + + ![er-user-friends](https://entgo.io/images/assets/er_user_friends.png) + + + +[![edges-m2m-bidirectional](https://entgo.io/images/assets/erd/edges-m2m-bidirectional.png)](https://gh.atlasgo.cloud/explore/74c5bd30) + +
+ ERD was generated by Atlas +
+ +
+
+ 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. @@ -1058,8 +1220,23 @@ In the following example, we demonstrate how to model the friendship between two required fields of the relationship (`user_id` and `friend_id`), and an additional field named `created_at` whose value is automatically set on creation. + + + ![er_edgeschema_bidi](https://entgo.io/images/assets/er_edgeschema_bidi.png) + + + +[![edges-schema](https://entgo.io/images/assets/erd/edges-schema.png)](https://gh.atlasgo.cloud/explore/90e2415f) + +
+ ERD was generated by Atlas +
+ +
+
+ 0 { diff --git a/examples/o2mrecur/ent/node_query.go b/examples/o2mrecur/ent/node_query.go index c7f2d883f..2d134b085 100644 --- a/examples/o2mrecur/ent/node_query.go +++ b/examples/o2mrecur/ent/node_query.go @@ -28,7 +28,6 @@ type NodeQuery struct { predicates []predicate.Node withParent *NodeQuery withChildren *NodeQuery - withFKs bool // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -406,19 +405,12 @@ func (nq *NodeQuery) prepareQuery(ctx context.Context) error { func (nq *NodeQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Node, error) { var ( nodes = []*Node{} - withFKs = nq.withFKs _spec = nq.querySpec() loadedTypes = [2]bool{ nq.withParent != nil, nq.withChildren != nil, } ) - if nq.withParent != nil { - withFKs = true - } - if withFKs { - _spec.Node.Columns = append(_spec.Node.Columns, node.ForeignKeys...) - } _spec.ScanValues = func(columns []string) ([]any, error) { return (*Node).scanValues(nil, columns) } @@ -457,10 +449,7 @@ func (nq *NodeQuery) loadParent(ctx context.Context, query *NodeQuery, nodes []* ids := make([]int, 0, len(nodes)) nodeids := make(map[int][]*Node) for i := range nodes { - if nodes[i].node_children == nil { - continue - } - fk := *nodes[i].node_children + fk := nodes[i].ParentID if _, ok := nodeids[fk]; !ok { ids = append(ids, fk) } @@ -477,7 +466,7 @@ func (nq *NodeQuery) loadParent(ctx context.Context, query *NodeQuery, nodes []* for _, n := range neighbors { nodes, ok := nodeids[n.ID] if !ok { - return fmt.Errorf(`unexpected foreign-key "node_children" returned %v`, n.ID) + return fmt.Errorf(`unexpected foreign-key "parent_id" returned %v`, n.ID) } for i := range nodes { assign(nodes[i], n) @@ -495,7 +484,6 @@ func (nq *NodeQuery) loadChildren(ctx context.Context, query *NodeQuery, nodes [ init(nodes[i]) } } - query.withFKs = true query.Where(predicate.Node(func(s *sql.Selector) { s.Where(sql.InValues(node.ChildrenColumn, fks...)) })) @@ -504,13 +492,10 @@ func (nq *NodeQuery) loadChildren(ctx context.Context, query *NodeQuery, nodes [ return err } for _, n := range neighbors { - fk := n.node_children - if fk == nil { - return fmt.Errorf(`foreign-key "node_children" is nil for node %v`, n.ID) - } - node, ok := nodeids[*fk] + fk := n.ParentID + node, ok := nodeids[fk] if !ok { - return fmt.Errorf(`unexpected foreign-key "node_children" returned %v for node %v`, *fk, n.ID) + return fmt.Errorf(`unexpected foreign-key "parent_id" returned %v for node %v`, fk, n.ID) } assign(node, n) } diff --git a/examples/o2mrecur/ent/node_update.go b/examples/o2mrecur/ent/node_update.go index d297c1db6..502808151 100644 --- a/examples/o2mrecur/ent/node_update.go +++ b/examples/o2mrecur/ent/node_update.go @@ -44,20 +44,26 @@ func (nu *NodeUpdate) AddValue(i int) *NodeUpdate { return nu } -// SetParentID sets the "parent" edge to the Node entity by ID. -func (nu *NodeUpdate) SetParentID(id int) *NodeUpdate { - nu.mutation.SetParentID(id) +// SetParentID sets the "parent_id" field. +func (nu *NodeUpdate) SetParentID(i int) *NodeUpdate { + nu.mutation.SetParentID(i) return nu } -// SetNillableParentID sets the "parent" edge to the Node entity by ID if the given value is not nil. -func (nu *NodeUpdate) SetNillableParentID(id *int) *NodeUpdate { - if id != nil { - nu = nu.SetParentID(*id) +// SetNillableParentID sets the "parent_id" field if the given value is not nil. +func (nu *NodeUpdate) SetNillableParentID(i *int) *NodeUpdate { + if i != nil { + nu.SetParentID(*i) } return nu } +// ClearParentID clears the value of the "parent_id" field. +func (nu *NodeUpdate) ClearParentID() *NodeUpdate { + nu.mutation.ClearParentID() + return nu +} + // SetParent sets the "parent" edge to the Node entity. func (nu *NodeUpdate) SetParent(n *Node) *NodeUpdate { return nu.SetParentID(n.ID) @@ -283,20 +289,26 @@ func (nuo *NodeUpdateOne) AddValue(i int) *NodeUpdateOne { return nuo } -// SetParentID sets the "parent" edge to the Node entity by ID. -func (nuo *NodeUpdateOne) SetParentID(id int) *NodeUpdateOne { - nuo.mutation.SetParentID(id) +// SetParentID sets the "parent_id" field. +func (nuo *NodeUpdateOne) SetParentID(i int) *NodeUpdateOne { + nuo.mutation.SetParentID(i) return nuo } -// SetNillableParentID sets the "parent" edge to the Node entity by ID if the given value is not nil. -func (nuo *NodeUpdateOne) SetNillableParentID(id *int) *NodeUpdateOne { - if id != nil { - nuo = nuo.SetParentID(*id) +// SetNillableParentID sets the "parent_id" field if the given value is not nil. +func (nuo *NodeUpdateOne) SetNillableParentID(i *int) *NodeUpdateOne { + if i != nil { + nuo.SetParentID(*i) } return nuo } +// ClearParentID clears the value of the "parent_id" field. +func (nuo *NodeUpdateOne) ClearParentID() *NodeUpdateOne { + nuo.mutation.ClearParentID() + return nuo +} + // SetParent sets the "parent" edge to the Node entity. func (nuo *NodeUpdateOne) SetParent(n *Node) *NodeUpdateOne { return nuo.SetParentID(n.ID) diff --git a/examples/o2mrecur/ent/schema/node.go b/examples/o2mrecur/ent/schema/node.go index 06d3ff512..f53347364 100644 --- a/examples/o2mrecur/ent/schema/node.go +++ b/examples/o2mrecur/ent/schema/node.go @@ -19,6 +19,8 @@ type Node struct { func (Node) Fields() []ent.Field { return []ent.Field{ field.Int("value"), + field.Int("parent_id"). + Optional(), } } @@ -27,6 +29,7 @@ func (Node) Edges() []ent.Edge { return []ent.Edge{ edge.To("children", Node.Type). From("parent"). + Field("parent_id"). Unique(), } } diff --git a/examples/o2mrecur/example_test.go b/examples/o2mrecur/example_test.go index 2830435e6..81686bea6 100644 --- a/examples/o2mrecur/example_test.go +++ b/examples/o2mrecur/example_test.go @@ -32,7 +32,7 @@ func Example_O2MRecur() { // Output: // Tree leafs [1 3 5] // [1 3 5] - // Node(id=1, value=2) + // Node(id=1, value=2, parent_id=0) } func Do(ctx context.Context, client *ent.Client) error { @@ -96,7 +96,7 @@ func Do(ctx context.Context, client *ent.Client) error { Where(node.Not(node.HasParent())). OnlyX(ctx) fmt.Println(orphan) - // Output: Node(id=1, value=2) + // Output: Node(id=1, value=2, parent_id=0) return nil } diff --git a/examples/o2orecur/ent/migrate/schema.go b/examples/o2orecur/ent/migrate/schema.go index 1011218ad..52b6a4c2f 100644 --- a/examples/o2orecur/ent/migrate/schema.go +++ b/examples/o2orecur/ent/migrate/schema.go @@ -16,7 +16,7 @@ var ( NodesColumns = []*schema.Column{ {Name: "id", Type: field.TypeInt, Increment: true}, {Name: "value", Type: field.TypeInt}, - {Name: "node_next", Type: field.TypeInt, Unique: true, Nullable: true}, + {Name: "prev_id", Type: field.TypeInt, Unique: true, Nullable: true}, } // NodesTable holds the schema information for the "nodes" table. NodesTable = &schema.Table{ diff --git a/examples/o2orecur/ent/mutation.go b/examples/o2orecur/ent/mutation.go index 29e1fd502..78f06a9ca 100644 --- a/examples/o2orecur/ent/mutation.go +++ b/examples/o2orecur/ent/mutation.go @@ -203,9 +203,53 @@ func (m *NodeMutation) ResetValue() { m.addvalue = nil } -// SetPrevID sets the "prev" edge to the Node entity by id. -func (m *NodeMutation) SetPrevID(id int) { - m.prev = &id +// SetPrevID sets the "prev_id" field. +func (m *NodeMutation) SetPrevID(i int) { + m.prev = &i +} + +// PrevID returns the value of the "prev_id" field in the mutation. +func (m *NodeMutation) PrevID() (r int, exists bool) { + v := m.prev + if v == nil { + return + } + return *v, true +} + +// OldPrevID returns the old "prev_id" field's value of the Node entity. +// If the Node object wasn't provided to the builder, the object is fetched from the database. +// An error is returned if the mutation operation is not UpdateOne, or the database query fails. +func (m *NodeMutation) OldPrevID(ctx context.Context) (v int, err error) { + if !m.op.Is(OpUpdateOne) { + return v, errors.New("OldPrevID is only allowed on UpdateOne operations") + } + if m.id == nil || m.oldValue == nil { + return v, errors.New("OldPrevID requires an ID field in the mutation") + } + oldValue, err := m.oldValue(ctx) + if err != nil { + return v, fmt.Errorf("querying old value for OldPrevID: %w", err) + } + return oldValue.PrevID, nil +} + +// ClearPrevID clears the value of the "prev_id" field. +func (m *NodeMutation) ClearPrevID() { + m.prev = nil + m.clearedFields[node.FieldPrevID] = struct{}{} +} + +// PrevIDCleared returns if the "prev_id" field was cleared in this mutation. +func (m *NodeMutation) PrevIDCleared() bool { + _, ok := m.clearedFields[node.FieldPrevID] + return ok +} + +// ResetPrevID resets all changes to the "prev_id" field. +func (m *NodeMutation) ResetPrevID() { + m.prev = nil + delete(m.clearedFields, node.FieldPrevID) } // ClearPrev clears the "prev" edge to the Node entity. @@ -215,15 +259,7 @@ func (m *NodeMutation) ClearPrev() { // PrevCleared reports if the "prev" edge to the Node entity was cleared. func (m *NodeMutation) PrevCleared() bool { - return m.clearedprev -} - -// PrevID returns the "prev" edge ID in the mutation. -func (m *NodeMutation) PrevID() (id int, exists bool) { - if m.prev != nil { - return *m.prev, true - } - return + return m.PrevIDCleared() || m.clearedprev } // PrevIDs returns the "prev" edge IDs in the mutation. @@ -315,10 +351,13 @@ func (m *NodeMutation) Type() string { // order to get all numeric fields that were incremented/decremented, call // AddedFields(). func (m *NodeMutation) Fields() []string { - fields := make([]string, 0, 1) + fields := make([]string, 0, 2) if m.value != nil { fields = append(fields, node.FieldValue) } + if m.prev != nil { + fields = append(fields, node.FieldPrevID) + } return fields } @@ -329,6 +368,8 @@ func (m *NodeMutation) Field(name string) (ent.Value, bool) { switch name { case node.FieldValue: return m.Value() + case node.FieldPrevID: + return m.PrevID() } return nil, false } @@ -340,6 +381,8 @@ func (m *NodeMutation) OldField(ctx context.Context, name string) (ent.Value, er switch name { case node.FieldValue: return m.OldValue(ctx) + case node.FieldPrevID: + return m.OldPrevID(ctx) } return nil, fmt.Errorf("unknown Node field %s", name) } @@ -356,6 +399,13 @@ func (m *NodeMutation) SetField(name string, value ent.Value) error { } m.SetValue(v) return nil + case node.FieldPrevID: + v, ok := value.(int) + if !ok { + return fmt.Errorf("unexpected type %T for field %s", value, name) + } + m.SetPrevID(v) + return nil } return fmt.Errorf("unknown Node field %s", name) } @@ -400,7 +450,11 @@ func (m *NodeMutation) AddField(name string, value ent.Value) error { // ClearedFields returns all nullable fields that were cleared during this // mutation. func (m *NodeMutation) ClearedFields() []string { - return nil + var fields []string + if m.FieldCleared(node.FieldPrevID) { + fields = append(fields, node.FieldPrevID) + } + return fields } // FieldCleared returns a boolean indicating if a field with the given name was @@ -413,6 +467,11 @@ func (m *NodeMutation) FieldCleared(name string) bool { // ClearField clears the value of the field with the given name. It returns an // error if the field is not defined in the schema. func (m *NodeMutation) ClearField(name string) error { + switch name { + case node.FieldPrevID: + m.ClearPrevID() + return nil + } return fmt.Errorf("unknown Node nullable field %s", name) } @@ -423,6 +482,9 @@ func (m *NodeMutation) ResetField(name string) error { case node.FieldValue: m.ResetValue() return nil + case node.FieldPrevID: + m.ResetPrevID() + return nil } return fmt.Errorf("unknown Node field %s", name) } diff --git a/examples/o2orecur/ent/node.go b/examples/o2orecur/ent/node.go index 7e7de15af..73b8e1413 100644 --- a/examples/o2orecur/ent/node.go +++ b/examples/o2orecur/ent/node.go @@ -21,10 +21,11 @@ type Node struct { ID int `json:"id,omitempty"` // Value holds the value of the "value" field. Value int `json:"value,omitempty"` + // PrevID holds the value of the "prev_id" field. + PrevID int `json:"prev_id,omitempty"` // Edges holds the relations/edges for other nodes in the graph. // The values are being populated by the NodeQuery when eager-loading is set. - Edges NodeEdges `json:"edges"` - node_next *int + Edges NodeEdges `json:"edges"` } // NodeEdges holds the relations/edges for other nodes in the graph. @@ -69,9 +70,7 @@ func (*Node) scanValues(columns []string) ([]any, error) { values := make([]any, len(columns)) for i := range columns { switch columns[i] { - case node.FieldID, node.FieldValue: - values[i] = new(sql.NullInt64) - case node.ForeignKeys[0]: // node_next + case node.FieldID, node.FieldValue, node.FieldPrevID: values[i] = new(sql.NullInt64) default: return nil, fmt.Errorf("unexpected column %q for type Node", columns[i]) @@ -100,12 +99,11 @@ func (n *Node) assignValues(columns []string, values []any) error { } else if value.Valid { n.Value = int(value.Int64) } - case node.ForeignKeys[0]: + case node.FieldPrevID: if value, ok := values[i].(*sql.NullInt64); !ok { - return fmt.Errorf("unexpected type %T for edge-field node_next", value) + return fmt.Errorf("unexpected type %T for field prev_id", values[i]) } else if value.Valid { - n.node_next = new(int) - *n.node_next = int(value.Int64) + n.PrevID = int(value.Int64) } } } @@ -147,6 +145,9 @@ func (n *Node) String() string { builder.WriteString(fmt.Sprintf("id=%v, ", n.ID)) builder.WriteString("value=") builder.WriteString(fmt.Sprintf("%v", n.Value)) + builder.WriteString(", ") + builder.WriteString("prev_id=") + builder.WriteString(fmt.Sprintf("%v", n.PrevID)) builder.WriteByte(')') return builder.String() } diff --git a/examples/o2orecur/ent/node/node.go b/examples/o2orecur/ent/node/node.go index deb36152d..b93d51a59 100644 --- a/examples/o2orecur/ent/node/node.go +++ b/examples/o2orecur/ent/node/node.go @@ -13,6 +13,8 @@ const ( FieldID = "id" // FieldValue holds the string denoting the value field in the database. FieldValue = "value" + // FieldPrevID holds the string denoting the prev_id field in the database. + FieldPrevID = "prev_id" // EdgePrev holds the string denoting the prev edge name in mutations. EdgePrev = "prev" // EdgeNext holds the string denoting the next edge name in mutations. @@ -22,23 +24,18 @@ const ( // PrevTable is the table that holds the prev relation/edge. PrevTable = "nodes" // PrevColumn is the table column denoting the prev relation/edge. - PrevColumn = "node_next" + PrevColumn = "prev_id" // NextTable is the table that holds the next relation/edge. NextTable = "nodes" // NextColumn is the table column denoting the next relation/edge. - NextColumn = "node_next" + NextColumn = "prev_id" ) // Columns holds all SQL columns for node fields. var Columns = []string{ FieldID, FieldValue, -} - -// ForeignKeys holds the SQL foreign-keys that are owned by the "nodes" -// table and are not defined as standalone fields in the schema. -var ForeignKeys = []string{ - "node_next", + FieldPrevID, } // ValidColumn reports if the column name is valid (part of the table columns). @@ -48,10 +45,5 @@ func ValidColumn(column string) bool { return true } } - for i := range ForeignKeys { - if column == ForeignKeys[i] { - return true - } - } return false } diff --git a/examples/o2orecur/ent/node/where.go b/examples/o2orecur/ent/node/where.go index acf7ffa10..e046e9da9 100644 --- a/examples/o2orecur/ent/node/where.go +++ b/examples/o2orecur/ent/node/where.go @@ -62,6 +62,11 @@ func Value(v int) predicate.Node { return predicate.Node(sql.FieldEQ(FieldValue, v)) } +// PrevID applies equality check predicate on the "prev_id" field. It's identical to PrevIDEQ. +func PrevID(v int) predicate.Node { + return predicate.Node(sql.FieldEQ(FieldPrevID, v)) +} + // ValueEQ applies the EQ predicate on the "value" field. func ValueEQ(v int) predicate.Node { return predicate.Node(sql.FieldEQ(FieldValue, v)) @@ -102,6 +107,36 @@ func ValueLTE(v int) predicate.Node { return predicate.Node(sql.FieldLTE(FieldValue, v)) } +// PrevIDEQ applies the EQ predicate on the "prev_id" field. +func PrevIDEQ(v int) predicate.Node { + return predicate.Node(sql.FieldEQ(FieldPrevID, v)) +} + +// PrevIDNEQ applies the NEQ predicate on the "prev_id" field. +func PrevIDNEQ(v int) predicate.Node { + return predicate.Node(sql.FieldNEQ(FieldPrevID, v)) +} + +// PrevIDIn applies the In predicate on the "prev_id" field. +func PrevIDIn(vs ...int) predicate.Node { + return predicate.Node(sql.FieldIn(FieldPrevID, vs...)) +} + +// PrevIDNotIn applies the NotIn predicate on the "prev_id" field. +func PrevIDNotIn(vs ...int) predicate.Node { + return predicate.Node(sql.FieldNotIn(FieldPrevID, vs...)) +} + +// PrevIDIsNil applies the IsNil predicate on the "prev_id" field. +func PrevIDIsNil() predicate.Node { + return predicate.Node(sql.FieldIsNull(FieldPrevID)) +} + +// PrevIDNotNil applies the NotNil predicate on the "prev_id" field. +func PrevIDNotNil() predicate.Node { + return predicate.Node(sql.FieldNotNull(FieldPrevID)) +} + // HasPrev applies the HasEdge predicate on the "prev" edge. func HasPrev() predicate.Node { return predicate.Node(func(s *sql.Selector) { diff --git a/examples/o2orecur/ent/node_create.go b/examples/o2orecur/ent/node_create.go index bf95e0876..c24b01f15 100644 --- a/examples/o2orecur/ent/node_create.go +++ b/examples/o2orecur/ent/node_create.go @@ -29,16 +29,16 @@ func (nc *NodeCreate) SetValue(i int) *NodeCreate { return nc } -// SetPrevID sets the "prev" edge to the Node entity by ID. -func (nc *NodeCreate) SetPrevID(id int) *NodeCreate { - nc.mutation.SetPrevID(id) +// SetPrevID sets the "prev_id" field. +func (nc *NodeCreate) SetPrevID(i int) *NodeCreate { + nc.mutation.SetPrevID(i) return nc } -// SetNillablePrevID sets the "prev" edge to the Node entity by ID if the given value is not nil. -func (nc *NodeCreate) SetNillablePrevID(id *int) *NodeCreate { - if id != nil { - nc = nc.SetPrevID(*id) +// SetNillablePrevID sets the "prev_id" field if the given value is not nil. +func (nc *NodeCreate) SetNillablePrevID(i *int) *NodeCreate { + if i != nil { + nc.SetPrevID(*i) } return nc } @@ -157,7 +157,7 @@ func (nc *NodeCreate) createSpec() (*Node, *sqlgraph.CreateSpec) { for _, k := range nodes { edge.Target.Nodes = append(edge.Target.Nodes, k) } - _node.node_next = &nodes[0] + _node.PrevID = nodes[0] _spec.Edges = append(_spec.Edges, edge) } if nodes := nc.mutation.NextIDs(); len(nodes) > 0 { diff --git a/examples/o2orecur/ent/node_query.go b/examples/o2orecur/ent/node_query.go index 9e81b8371..c6db4575d 100644 --- a/examples/o2orecur/ent/node_query.go +++ b/examples/o2orecur/ent/node_query.go @@ -28,7 +28,6 @@ type NodeQuery struct { predicates []predicate.Node withPrev *NodeQuery withNext *NodeQuery - withFKs bool // intermediate query (i.e. traversal path). sql *sql.Selector path func(context.Context) (*sql.Selector, error) @@ -406,19 +405,12 @@ func (nq *NodeQuery) prepareQuery(ctx context.Context) error { func (nq *NodeQuery) sqlAll(ctx context.Context, hooks ...queryHook) ([]*Node, error) { var ( nodes = []*Node{} - withFKs = nq.withFKs _spec = nq.querySpec() loadedTypes = [2]bool{ nq.withPrev != nil, nq.withNext != nil, } ) - if nq.withPrev != nil { - withFKs = true - } - if withFKs { - _spec.Node.Columns = append(_spec.Node.Columns, node.ForeignKeys...) - } _spec.ScanValues = func(columns []string) ([]any, error) { return (*Node).scanValues(nil, columns) } @@ -456,10 +448,7 @@ func (nq *NodeQuery) loadPrev(ctx context.Context, query *NodeQuery, nodes []*No ids := make([]int, 0, len(nodes)) nodeids := make(map[int][]*Node) for i := range nodes { - if nodes[i].node_next == nil { - continue - } - fk := *nodes[i].node_next + fk := nodes[i].PrevID if _, ok := nodeids[fk]; !ok { ids = append(ids, fk) } @@ -476,7 +465,7 @@ func (nq *NodeQuery) loadPrev(ctx context.Context, query *NodeQuery, nodes []*No for _, n := range neighbors { nodes, ok := nodeids[n.ID] if !ok { - return fmt.Errorf(`unexpected foreign-key "node_next" returned %v`, n.ID) + return fmt.Errorf(`unexpected foreign-key "prev_id" returned %v`, n.ID) } for i := range nodes { assign(nodes[i], n) @@ -491,7 +480,6 @@ func (nq *NodeQuery) loadNext(ctx context.Context, query *NodeQuery, nodes []*No fks = append(fks, nodes[i].ID) nodeids[nodes[i].ID] = nodes[i] } - query.withFKs = true query.Where(predicate.Node(func(s *sql.Selector) { s.Where(sql.InValues(node.NextColumn, fks...)) })) @@ -500,13 +488,10 @@ func (nq *NodeQuery) loadNext(ctx context.Context, query *NodeQuery, nodes []*No return err } for _, n := range neighbors { - fk := n.node_next - if fk == nil { - return fmt.Errorf(`foreign-key "node_next" is nil for node %v`, n.ID) - } - node, ok := nodeids[*fk] + fk := n.PrevID + node, ok := nodeids[fk] if !ok { - return fmt.Errorf(`unexpected foreign-key "node_next" returned %v for node %v`, *fk, n.ID) + return fmt.Errorf(`unexpected foreign-key "prev_id" returned %v for node %v`, fk, n.ID) } assign(node, n) } diff --git a/examples/o2orecur/ent/node_update.go b/examples/o2orecur/ent/node_update.go index 6bfa2afa0..83d6be499 100644 --- a/examples/o2orecur/ent/node_update.go +++ b/examples/o2orecur/ent/node_update.go @@ -44,20 +44,26 @@ func (nu *NodeUpdate) AddValue(i int) *NodeUpdate { return nu } -// SetPrevID sets the "prev" edge to the Node entity by ID. -func (nu *NodeUpdate) SetPrevID(id int) *NodeUpdate { - nu.mutation.SetPrevID(id) +// SetPrevID sets the "prev_id" field. +func (nu *NodeUpdate) SetPrevID(i int) *NodeUpdate { + nu.mutation.SetPrevID(i) return nu } -// SetNillablePrevID sets the "prev" edge to the Node entity by ID if the given value is not nil. -func (nu *NodeUpdate) SetNillablePrevID(id *int) *NodeUpdate { - if id != nil { - nu = nu.SetPrevID(*id) +// SetNillablePrevID sets the "prev_id" field if the given value is not nil. +func (nu *NodeUpdate) SetNillablePrevID(i *int) *NodeUpdate { + if i != nil { + nu.SetPrevID(*i) } return nu } +// ClearPrevID clears the value of the "prev_id" field. +func (nu *NodeUpdate) ClearPrevID() *NodeUpdate { + nu.mutation.ClearPrevID() + return nu +} + // SetPrev sets the "prev" edge to the Node entity. func (nu *NodeUpdate) SetPrev(n *Node) *NodeUpdate { return nu.SetPrevID(n.ID) @@ -253,20 +259,26 @@ func (nuo *NodeUpdateOne) AddValue(i int) *NodeUpdateOne { return nuo } -// SetPrevID sets the "prev" edge to the Node entity by ID. -func (nuo *NodeUpdateOne) SetPrevID(id int) *NodeUpdateOne { - nuo.mutation.SetPrevID(id) +// SetPrevID sets the "prev_id" field. +func (nuo *NodeUpdateOne) SetPrevID(i int) *NodeUpdateOne { + nuo.mutation.SetPrevID(i) return nuo } -// SetNillablePrevID sets the "prev" edge to the Node entity by ID if the given value is not nil. -func (nuo *NodeUpdateOne) SetNillablePrevID(id *int) *NodeUpdateOne { - if id != nil { - nuo = nuo.SetPrevID(*id) +// SetNillablePrevID sets the "prev_id" field if the given value is not nil. +func (nuo *NodeUpdateOne) SetNillablePrevID(i *int) *NodeUpdateOne { + if i != nil { + nuo.SetPrevID(*i) } return nuo } +// ClearPrevID clears the value of the "prev_id" field. +func (nuo *NodeUpdateOne) ClearPrevID() *NodeUpdateOne { + nuo.mutation.ClearPrevID() + return nuo +} + // SetPrev sets the "prev" edge to the Node entity. func (nuo *NodeUpdateOne) SetPrev(n *Node) *NodeUpdateOne { return nuo.SetPrevID(n.ID) diff --git a/examples/o2orecur/ent/schema/node.go b/examples/o2orecur/ent/schema/node.go index 93a43dcac..6bd99d405 100644 --- a/examples/o2orecur/ent/schema/node.go +++ b/examples/o2orecur/ent/schema/node.go @@ -19,6 +19,8 @@ type Node struct { func (Node) Fields() []ent.Field { return []ent.Field{ field.Int("value"), + field.Int("prev_id"). + Optional(), } } @@ -28,6 +30,7 @@ func (Node) Edges() []ent.Edge { edge.To("next", Node.Type). Unique(). From("prev"). + Field("prev_id"). Unique(), } }