ent/doc: O2M same type example

Reviewed By: alexsn

Differential Revision: D17050185

fbshipit-source-id: ef12cc82ab9bf155713faf94129fd5fe503664e4
This commit is contained in:
Ariel Mashraki
2019-08-26 03:17:15 -07:00
committed by Facebook Github Bot
parent 551236f06d
commit 3e018277b1
23 changed files with 2476 additions and 3 deletions

View File

@@ -0,0 +1,11 @@
# Tree O2M Relation
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.
### Generate Assets
```console
go generate ./...
```

View File

@@ -0,0 +1,124 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"context"
"fmt"
"log"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/migrate"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/node"
"github.com/facebookincubator/ent/dialect/sql"
)
// Client is the client that holds all ent builders.
type Client struct {
config
// Schema is the client for creating, migrating and dropping schema.
Schema *migrate.Schema
// Node is the client for interacting with the Node builders.
Node *NodeClient
}
// NewClient creates a new client configured with the given options.
func NewClient(opts ...Option) *Client {
c := config{log: log.Println}
c.options(opts...)
return &Client{
config: c,
Schema: migrate.NewSchema(c.driver),
Node: NewNodeClient(c),
}
}
// Tx returns a new transactional client.
func (c *Client) Tx(ctx context.Context) (*Tx, error) {
if _, ok := c.driver.(*txDriver); ok {
return nil, fmt.Errorf("ent: cannot start a transaction within a transaction")
}
tx, err := newTx(ctx, c.driver)
if err != nil {
return nil, fmt.Errorf("ent: starting a transaction: %v", err)
}
cfg := config{driver: tx, log: c.log, verbose: c.verbose}
return &Tx{
config: cfg,
Node: NewNodeClient(cfg),
}, nil
}
// NodeClient is a client for the Node schema.
type NodeClient struct {
config
}
// NewNodeClient returns a client for the Node from the given config.
func NewNodeClient(c config) *NodeClient {
return &NodeClient{config: c}
}
// Create returns a create builder for Node.
func (c *NodeClient) Create() *NodeCreate {
return &NodeCreate{config: c.config}
}
// Update returns an update builder for Node.
func (c *NodeClient) Update() *NodeUpdate {
return &NodeUpdate{config: c.config}
}
// UpdateOne returns an update builder for the given entity.
func (c *NodeClient) UpdateOne(n *Node) *NodeUpdateOne {
return c.UpdateOneID(n.ID)
}
// UpdateOneID returns an update builder for the given id.
func (c *NodeClient) UpdateOneID(id int) *NodeUpdateOne {
return &NodeUpdateOne{config: c.config, id: id}
}
// Delete returns a delete builder for Node.
func (c *NodeClient) Delete() *NodeDelete {
return &NodeDelete{config: c.config}
}
// DeleteOne returns a delete builder for the given entity.
func (c *NodeClient) DeleteOne(n *Node) *NodeDeleteOne {
return c.DeleteOneID(n.ID)
}
// DeleteOneID returns a delete builder for the given id.
func (c *NodeClient) DeleteOneID(id int) *NodeDeleteOne {
return &NodeDeleteOne{c.Delete().Where(node.ID(id))}
}
// Create returns a query builder for Node.
func (c *NodeClient) Query() *NodeQuery {
return &NodeQuery{config: c.config}
}
// QueryParent queries the parent edge of a Node.
func (c *NodeClient) QueryParent(n *Node) *NodeQuery {
query := &NodeQuery{config: c.config}
id := n.ID
t1 := sql.Table(node.Table)
t2 := sql.Select(node.ParentColumn).
From(sql.Table(node.ParentTable)).
Where(sql.EQ(node.FieldID, id))
query.sql = sql.Select().From(t1).Join(t2).On(t1.C(node.FieldID), t2.C(node.ParentColumn))
return query
}
// QueryChildren queries the children edge of a Node.
func (c *NodeClient) QueryChildren(n *Node) *NodeQuery {
query := &NodeQuery{config: c.config}
id := n.ID
query.sql = sql.Select().From(sql.Table(node.Table)).
Where(sql.EQ(node.ChildrenColumn, id))
return query
}

View File

@@ -0,0 +1,51 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"github.com/facebookincubator/ent/dialect"
)
// Option function to configure the client.
type Option func(*config)
// Config is the configuration for the client and its builder.
type config struct {
// driver is the driver used for execute database requests.
driver dialect.Driver
// verbose enable a verbosity logging.
verbose bool
// log used for logging on verbose mode.
log func(...interface{})
}
// Options applies the options on the config object.
func (c *config) options(opts ...Option) {
for _, opt := range opts {
opt(c)
}
if c.verbose {
c.driver = dialect.Debug(c.driver, c.log)
}
}
// Verbose sets the client logging to verbose.
func Verbose() Option {
return func(c *config) {
c.verbose = true
}
}
// Log sets the client logging to verbose.
func Log(fn func(...interface{})) Option {
return func(c *config) {
c.log = fn
}
}
// Driver configures the client driver.
func Driver(driver dialect.Driver) Option {
return func(c *config) {
c.driver = driver
}
}

View File

@@ -0,0 +1,20 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"context"
)
type contextKey struct{}
// FromContext returns the Client stored in a context, or nil if there isn't one.
func FromContext(ctx context.Context) *Client {
c, _ := ctx.Value(contextKey{}).(*Client)
return c
}
// NewContext returns a new context with the given Client attached.
func NewContext(parent context.Context, c *Client) context.Context {
return context.WithValue(parent, contextKey{}, c)
}

View File

@@ -0,0 +1,192 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"fmt"
"strings"
"github.com/facebookincubator/ent/dialect"
"github.com/facebookincubator/ent/dialect/sql"
)
// Order applies an ordering on either graph traversal or sql selector.
type Order func(*sql.Selector)
// Asc applies the given fields in ASC order.
func Asc(fields ...string) Order {
return Order(
func(s *sql.Selector) {
for _, f := range fields {
s.OrderBy(sql.Asc(f))
}
},
)
}
// Desc applies the given fields in DESC order.
func Desc(fields ...string) Order {
return Order(
func(s *sql.Selector) {
for _, f := range fields {
s.OrderBy(sql.Desc(f))
}
},
)
}
// Aggregate applies an aggregation step on the group-by traversal/selector.
type Aggregate struct {
// SQL the column wrapped with the aggregation function.
SQL func(*sql.Selector) string
}
// As is a pseudo aggregation function for renaming another other functions with custom names. For example:
//
// GroupBy(field1, field2).
// Aggregate(ent.As(ent.Sum(field1), "sum_field1"), (ent.As(ent.Sum(field2), "sum_field2")).
// Scan(ctx, &v)
//
func As(fn Aggregate, end string) Aggregate {
return Aggregate{
SQL: func(s *sql.Selector) string {
return sql.As(fn.SQL(s), end)
},
}
}
// Count applies the "count" aggregation function on each group.
func Count() Aggregate {
return Aggregate{
SQL: func(s *sql.Selector) string {
return sql.Count("*")
},
}
}
// Max applies the "max" aggregation function on the given field of each group.
func Max(field string) Aggregate {
return Aggregate{
SQL: func(s *sql.Selector) string {
return sql.Max(s.C(field))
},
}
}
// Mean applies the "mean" aggregation function on the given field of each group.
func Mean(field string) Aggregate {
return Aggregate{
SQL: func(s *sql.Selector) string {
return sql.Avg(s.C(field))
},
}
}
// Min applies the "min" aggregation function on the given field of each group.
func Min(field string) Aggregate {
return Aggregate{
SQL: func(s *sql.Selector) string {
return sql.Min(s.C(field))
},
}
}
// Sum applies the "sum" aggregation function on the given field of each group.
func Sum(field string) Aggregate {
return Aggregate{
SQL: func(s *sql.Selector) string {
return sql.Sum(s.C(field))
},
}
}
// ErrNotFound returns when trying to fetch a specific entity and it was not found in the database.
type ErrNotFound struct {
label string
}
// Error implements the error interface.
func (e *ErrNotFound) Error() string {
return fmt.Sprintf("ent: %s not found", e.label)
}
// IsNotFound returns a boolean indicating whether the error is a not found error.
func IsNotFound(err error) bool {
_, ok := err.(*ErrNotFound)
return ok
}
// MaskNotFound masks nor found error.
func MaskNotFound(err error) error {
if IsNotFound(err) {
return nil
}
return err
}
// ErrNotSingular returns when trying to fetch a singular entity and more then one was found in the database.
type ErrNotSingular struct {
label string
}
// Error implements the error interface.
func (e *ErrNotSingular) Error() string {
return fmt.Sprintf("ent: %s not singular", e.label)
}
// IsNotSingular returns a boolean indicating whether the error is a not singular error.
func IsNotSingular(err error) bool {
_, ok := err.(*ErrNotSingular)
return ok
}
// ErrConstraintFailed returns when trying to create/update one or more entities and
// one or more of their constraints failed. For example, violation of edge or field uniqueness.
type ErrConstraintFailed struct {
msg string
wrap error
}
// Error implements the error interface.
func (e ErrConstraintFailed) Error() string {
return fmt.Sprintf("ent: unique constraint failed: %s", e.msg)
}
// Unwrap implements the errors.Wrapper interface.
func (e *ErrConstraintFailed) Unwrap() error {
return e.wrap
}
// IsConstraintFailure returns a boolean indicating whether the error is a constraint failure.
func IsConstraintFailure(err error) bool {
_, ok := err.(*ErrConstraintFailed)
return ok
}
func isSQLConstraintError(err error) (*ErrConstraintFailed, bool) {
// Error number 1062 is ER_DUP_ENTRY in mysql, and "UNIQUE constraint failed" is SQLite prefix.
if msg := err.Error(); strings.HasPrefix(msg, "Error 1062") || strings.HasPrefix(msg, "UNIQUE constraint failed") {
return &ErrConstraintFailed{msg, err}, true
}
return nil, false
}
// rollback calls to tx.Rollback and wraps the given error with the rollback error if occurred.
func rollback(tx dialect.Tx, err error) error {
if rerr := tx.Rollback(); rerr != nil {
err = fmt.Errorf("%s: %v", err.Error(), rerr)
}
if err, ok := isSQLConstraintError(err); ok {
return err
}
return err
}
// keys returns the keys/ids from the edge map.
func keys(m map[int]struct{}) []int {
s := make([]int, 0, len(m))
for id, _ := range m {
s = append(s, id)
}
return s
}

View File

@@ -0,0 +1,53 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"context"
"log"
"github.com/facebookincubator/ent/dialect/sql"
)
// dsn for the database. In order to run the tests locally, run the following command:
//
// ENT_INTEGRATION_ENDPOINT="root:pass@tcp(localhost:3306)/test?parseTime=True" go test -v
//
var dsn string
func ExampleNode() {
if dsn == "" {
return
}
ctx := context.Background()
drv, err := sql.Open("mysql", dsn)
if err != nil {
log.Fatalf("failed creating database client: %v", err)
}
defer drv.Close()
client := NewClient(Driver(drv))
// creating vertices for the node's edges.
n1 := client.Node.
Create().
SetValue(1).
SaveX(ctx)
log.Println("node created:", n1)
// create node vertex with its edges.
n := client.Node.
Create().
SetValue(1).
AddChildren(n1).
SaveX(ctx)
log.Println("node created:", n)
// query edges.
n1, err = n.QueryChildren().First(ctx)
if err != nil {
log.Fatalf("failed querying children: %v", err)
}
log.Println("children found:", n1)
// Output:
}

View File

@@ -0,0 +1,3 @@
package ent
//go:generate go run ../../../entc/cmd/entc/entc.go generate --header "Code generated (@generated) by entc, DO NOT EDIT." ./schema

View File

@@ -0,0 +1,48 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package migrate
import (
"context"
"fmt"
"github.com/facebookincubator/ent/dialect"
"github.com/facebookincubator/ent/dialect/sql/schema"
)
var (
// WithGlobalUniqueID sets the universal ids options to the migration.
// If this option is enabled, ent migration will allocate a 1<<32 range
// for the ids of each entity (table).
// Note that this option cannot be applied on tables that already exist.
WithGlobalUniqueID = schema.WithGlobalUniqueID
// WithDropColumn sets the drop column option to the migration.
// If this option is enabled, ent migration will drop old columns
// that were used for both fields and edges. This defaults to false.
WithDropColumn = schema.WithDropColumn
// WithDropIndex sets the drop index option to the migration.
// If this option is enabled, ent migration will drop old indexes
// that were defined in the schema. This defaults to false.
// Note that unique constraints are defined using `UNIQUE INDEX`,
// and therefore, it's recommended to enable this option to get more
// flexibility in the schema changes.
WithDropIndex = schema.WithDropIndex
)
// Schema is the API for creating, migrating and dropping a schema.
type Schema struct {
drv dialect.Driver
universalID bool
}
// NewSchema creates a new schema client.
func NewSchema(drv dialect.Driver) *Schema { return &Schema{drv: drv} }
// Create creates all schema resources.
func (s *Schema) Create(ctx context.Context, opts ...schema.MigrateOption) error {
migrate, err := schema.NewMigrate(s.drv, opts...)
if err != nil {
return fmt.Errorf("ent/migrate: %v", err)
}
return migrate.Create(ctx, Tables...)
}

View File

@@ -0,0 +1,40 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package migrate
import (
"github.com/facebookincubator/ent/dialect/sql/schema"
"github.com/facebookincubator/ent/schema/field"
)
var (
// NodesColumns holds the columns for the "nodes" table.
NodesColumns = []*schema.Column{
{Name: "id", Type: field.TypeInt, Increment: true},
{Name: "value", Type: field.TypeInt},
{Name: "parent_id", Type: field.TypeInt, Nullable: true},
}
// NodesTable holds the schema information for the "nodes" table.
NodesTable = &schema.Table{
Name: "nodes",
Columns: NodesColumns,
PrimaryKey: []*schema.Column{NodesColumns[0]},
ForeignKeys: []*schema.ForeignKey{
{
Symbol: "nodes_nodes_children",
Columns: []*schema.Column{NodesColumns[2]},
RefColumns: []*schema.Column{NodesColumns[0]},
OnDelete: schema.SetNull,
},
},
}
// Tables holds all the tables in the schema.
Tables = []*schema.Table{
NodesTable,
}
)
func init() {
NodesTable.ForeignKeys[0].RefTable = NodesTable
}

View File

@@ -0,0 +1,96 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"bytes"
"fmt"
"github.com/facebookincubator/ent/dialect/sql"
)
// Node is the model entity for the Node schema.
type Node struct {
config
// ID of the ent.
ID int `json:"id,omitempty"`
// Value holds the value of the "value" field.
Value int `json:"value,omitempty"`
}
// FromRows scans the sql response data into Node.
func (n *Node) FromRows(rows *sql.Rows) error {
var vn struct {
ID int
Value sql.NullInt64
}
// the order here should be the same as in the `node.Columns`.
if err := rows.Scan(
&vn.ID,
&vn.Value,
); err != nil {
return err
}
n.ID = vn.ID
n.Value = int(vn.Value.Int64)
return nil
}
// QueryParent queries the parent edge of the Node.
func (n *Node) QueryParent() *NodeQuery {
return (&NodeClient{n.config}).QueryParent(n)
}
// QueryChildren queries the children edge of the Node.
func (n *Node) QueryChildren() *NodeQuery {
return (&NodeClient{n.config}).QueryChildren(n)
}
// Update returns a builder for updating this Node.
// Note that, you need to call Node.Unwrap() before calling this method, if this Node
// was returned from a transaction, and the transaction was committed or rolled back.
func (n *Node) Update() *NodeUpdateOne {
return (&NodeClient{n.config}).UpdateOne(n)
}
// Unwrap unwraps the entity that was returned from a transaction after it was closed,
// so that all next queries will be executed through the driver which created the transaction.
func (n *Node) Unwrap() *Node {
tx, ok := n.config.driver.(*txDriver)
if !ok {
panic("ent: Node is not a transactional entity")
}
n.config.driver = tx.drv
return n
}
// String implements the fmt.Stringer.
func (n *Node) String() string {
buf := bytes.NewBuffer(nil)
buf.WriteString("Node(")
buf.WriteString(fmt.Sprintf("id=%v", n.ID))
buf.WriteString(fmt.Sprintf(", value=%v", n.Value))
buf.WriteString(")")
return buf.String()
}
// Nodes is a parsable slice of Node.
type Nodes []*Node
// FromRows scans the sql response data into Nodes.
func (n *Nodes) FromRows(rows *sql.Rows) error {
for rows.Next() {
vn := &Node{}
if err := vn.FromRows(rows); err != nil {
return err
}
*n = append(*n, vn)
}
return nil
}
func (n Nodes) config(cfg config) {
for i := range n {
n[i].config = cfg
}
}

View File

@@ -0,0 +1,29 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package node
const (
// Label holds the string label denoting the node type in the database.
Label = "node"
// FieldID holds the string denoting the id field in the database.
FieldID = "id"
// FieldValue holds the string denoting the value vertex property in the database.
FieldValue = "value"
// Table holds the table name of the node in the database.
Table = "nodes"
// ParentTable is the table the holds the parent relation/edge.
ParentTable = "nodes"
// ParentColumn is the table column denoting the parent relation/edge.
ParentColumn = "parent_id"
// ChildrenTable is the table the holds the children relation/edge.
ChildrenTable = "nodes"
// ChildrenColumn is the table column denoting the children relation/edge.
ChildrenColumn = "parent_id"
)
// Columns holds all SQL columns are node fields.
var Columns = []string{
FieldID,
FieldValue,
}

View File

@@ -0,0 +1,300 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package node
import (
"github.com/facebookincubator/ent/examples/o2mrecur/ent/predicate"
"github.com/facebookincubator/ent/dialect/sql"
)
// ID filters vertices based on their identifier.
func ID(id int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
},
)
}
// IDEQ applies the EQ predicate on the ID field.
func IDEQ(id int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldID), id))
},
)
}
// IDNEQ applies the NEQ predicate on the ID field.
func IDNEQ(id int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldID), id))
},
)
}
// IDGT applies the GT predicate on the ID field.
func IDGT(id int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldID), id))
},
)
}
// IDGTE applies the GTE predicate on the ID field.
func IDGTE(id int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldID), id))
},
)
}
// IDLT applies the LT predicate on the ID field.
func IDLT(id int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldID), id))
},
)
}
// IDLTE applies the LTE predicate on the ID field.
func IDLTE(id int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldID), id))
},
)
}
// IDIn applies the In predicate on the ID field.
func IDIn(ids ...int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(ids) == 0 {
s.Where(sql.False())
return
}
v := make([]interface{}, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.In(s.C(FieldID), v...))
},
)
}
// IDNotIn applies the NotIn predicate on the ID field.
func IDNotIn(ids ...int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(ids) == 0 {
s.Where(sql.False())
return
}
v := make([]interface{}, len(ids))
for i := range v {
v[i] = ids[i]
}
s.Where(sql.NotIn(s.C(FieldID), v...))
},
)
}
// Value applies equality check predicate on the "value" field. It's identical to ValueEQ.
func Value(v int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldValue), v))
},
)
}
// ValueEQ applies the EQ predicate on the "value" field.
func ValueEQ(v int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.EQ(s.C(FieldValue), v))
},
)
}
// ValueNEQ applies the NEQ predicate on the "value" field.
func ValueNEQ(v int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.NEQ(s.C(FieldValue), v))
},
)
}
// ValueGT applies the GT predicate on the "value" field.
func ValueGT(v int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.GT(s.C(FieldValue), v))
},
)
}
// ValueGTE applies the GTE predicate on the "value" field.
func ValueGTE(v int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.GTE(s.C(FieldValue), v))
},
)
}
// ValueLT applies the LT predicate on the "value" field.
func ValueLT(v int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.LT(s.C(FieldValue), v))
},
)
}
// ValueLTE applies the LTE predicate on the "value" field.
func ValueLTE(v int) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
s.Where(sql.LTE(s.C(FieldValue), v))
},
)
}
// ValueIn applies the In predicate on the "value" field.
func ValueIn(vs ...int) predicate.Node {
v := make([]interface{}, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Node(
func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(vs) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.In(s.C(FieldValue), v...))
},
)
}
// ValueNotIn applies the NotIn predicate on the "value" field.
func ValueNotIn(vs ...int) predicate.Node {
v := make([]interface{}, len(vs))
for i := range v {
v[i] = vs[i]
}
return predicate.Node(
func(s *sql.Selector) {
// if not arguments were provided, append the FALSE constants,
// since we can't apply "IN ()". This will make this predicate falsy.
if len(vs) == 0 {
s.Where(sql.False())
return
}
s.Where(sql.NotIn(s.C(FieldValue), v...))
},
)
}
// HasParent applies the HasEdge predicate on the "parent" edge.
func HasParent() predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
t1 := s.Table()
s.Where(sql.NotNull(t1.C(ParentColumn)))
},
)
}
// HasParentWith applies the HasEdge predicate on the "parent" edge with a given conditions (other predicates).
func HasParentWith(preds ...predicate.Node) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
t1 := s.Table()
t2 := sql.Select(FieldID).From(sql.Table(ParentTable))
for _, p := range preds {
p(t2)
}
s.Where(sql.In(t1.C(ParentColumn), t2))
},
)
}
// HasChildren applies the HasEdge predicate on the "children" edge.
func HasChildren() predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
t1 := s.Table()
s.Where(
sql.In(
t1.C(FieldID),
sql.Select(ChildrenColumn).
From(sql.Table(ChildrenTable)).
Where(sql.NotNull(ChildrenColumn)),
),
)
},
)
}
// HasChildrenWith applies the HasEdge predicate on the "children" edge with a given conditions (other predicates).
func HasChildrenWith(preds ...predicate.Node) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
t1 := s.Table()
t2 := sql.Select(ChildrenColumn).From(sql.Table(ChildrenTable))
for _, p := range preds {
p(t2)
}
s.Where(sql.In(t1.C(FieldID), t2))
},
)
}
// And groups list of predicates with the AND operator between them.
func And(predicates ...predicate.Node) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
for _, p := range predicates {
p(s)
}
},
)
}
// Or groups list of predicates with the OR operator between them.
func Or(predicates ...predicate.Node) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
for i, p := range predicates {
if i > 0 {
s.Or()
}
p(s)
}
},
)
}
// Not applies the not operator on the given predicate.
func Not(p predicate.Node) predicate.Node {
return predicate.Node(
func(s *sql.Selector) {
p(s.Not())
},
)
}

View File

@@ -0,0 +1,149 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/node"
"github.com/facebookincubator/ent/dialect/sql"
)
// NodeCreate is the builder for creating a Node entity.
type NodeCreate struct {
config
value *int
parent map[int]struct{}
children map[int]struct{}
}
// SetValue sets the value field.
func (nc *NodeCreate) SetValue(i int) *NodeCreate {
nc.value = &i
return nc
}
// SetParentID sets the parent edge to Node by id.
func (nc *NodeCreate) SetParentID(id int) *NodeCreate {
if nc.parent == nil {
nc.parent = make(map[int]struct{})
}
nc.parent[id] = struct{}{}
return nc
}
// SetNillableParentID sets the parent edge to Node by id if the given value is not nil.
func (nc *NodeCreate) SetNillableParentID(id *int) *NodeCreate {
if id != nil {
nc = nc.SetParentID(*id)
}
return nc
}
// SetParent sets the parent edge to Node.
func (nc *NodeCreate) SetParent(n *Node) *NodeCreate {
return nc.SetParentID(n.ID)
}
// AddChildIDs adds the children edge to Node by ids.
func (nc *NodeCreate) AddChildIDs(ids ...int) *NodeCreate {
if nc.children == nil {
nc.children = make(map[int]struct{})
}
for i := range ids {
nc.children[ids[i]] = struct{}{}
}
return nc
}
// AddChildren adds the children edges to Node.
func (nc *NodeCreate) AddChildren(n ...*Node) *NodeCreate {
ids := make([]int, len(n))
for i := range n {
ids[i] = n[i].ID
}
return nc.AddChildIDs(ids...)
}
// Save creates the Node in the database.
func (nc *NodeCreate) Save(ctx context.Context) (*Node, error) {
if nc.value == nil {
return nil, errors.New("ent: missing required field \"value\"")
}
if len(nc.parent) > 1 {
return nil, errors.New("ent: multiple assignments on a unique edge \"parent\"")
}
return nc.sqlSave(ctx)
}
// SaveX calls Save and panics if Save returns an error.
func (nc *NodeCreate) SaveX(ctx context.Context) *Node {
v, err := nc.Save(ctx)
if err != nil {
panic(err)
}
return v
}
func (nc *NodeCreate) sqlSave(ctx context.Context) (*Node, error) {
var (
res sql.Result
n = &Node{config: nc.config}
)
tx, err := nc.driver.Tx(ctx)
if err != nil {
return nil, err
}
builder := sql.Insert(node.Table).Default(nc.driver.Dialect())
if nc.value != nil {
builder.Set(node.FieldValue, *nc.value)
n.Value = *nc.value
}
query, args := builder.Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return nil, rollback(tx, err)
}
id, err := res.LastInsertId()
if err != nil {
return nil, rollback(tx, err)
}
n.ID = int(id)
if len(nc.parent) > 0 {
for eid := range nc.parent {
query, args := sql.Update(node.ParentTable).
Set(node.ParentColumn, eid).
Where(sql.EQ(node.FieldID, id)).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return nil, rollback(tx, err)
}
}
}
if len(nc.children) > 0 {
p := sql.P()
for eid := range nc.children {
p.Or().EQ(node.FieldID, eid)
}
query, args := sql.Update(node.ChildrenTable).
Set(node.ChildrenColumn, id).
Where(sql.And(p, sql.IsNull(node.ChildrenColumn))).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return nil, rollback(tx, err)
}
affected, err := res.RowsAffected()
if err != nil {
return nil, rollback(tx, err)
}
if int(affected) < len(nc.children) {
return nil, rollback(tx, &ErrConstraintFailed{msg: fmt.Sprintf("one of \"children\" %v already connected to a different \"Node\"", keys(nc.children))})
}
}
if err := tx.Commit(); err != nil {
return nil, err
}
return n, nil
}

View File

@@ -0,0 +1,61 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"context"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/node"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/predicate"
"github.com/facebookincubator/ent/dialect/sql"
)
// NodeDelete is the builder for deleting a Node entity.
type NodeDelete struct {
config
predicates []predicate.Node
}
// Where adds a new predicate for the builder.
func (nd *NodeDelete) Where(ps ...predicate.Node) *NodeDelete {
nd.predicates = append(nd.predicates, ps...)
return nd
}
// Exec executes the deletion query.
func (nd *NodeDelete) Exec(ctx context.Context) error {
return nd.sqlExec(ctx)
}
// ExecX is like Exec, but panics if an error occurs.
func (nd *NodeDelete) ExecX(ctx context.Context) {
if err := nd.Exec(ctx); err != nil {
panic(err)
}
}
func (nd *NodeDelete) sqlExec(ctx context.Context) error {
var res sql.Result
selector := sql.Select().From(sql.Table(node.Table))
for _, p := range nd.predicates {
p(selector)
}
query, args := sql.Delete(node.Table).FromSelect(selector).Query()
return nd.driver.Exec(ctx, query, args, &res)
}
// NodeDeleteOne is the builder for deleting a single Node entity.
type NodeDeleteOne struct {
nd *NodeDelete
}
// Exec executes the deletion query.
func (ndo *NodeDeleteOne) Exec(ctx context.Context) error {
return ndo.nd.Exec(ctx)
}
// ExecX is like Exec, but panics if an error occurs.
func (ndo *NodeDeleteOne) ExecX(ctx context.Context) {
ndo.nd.ExecX(ctx)
}

View File

@@ -0,0 +1,495 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"math"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/node"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/predicate"
"github.com/facebookincubator/ent/dialect/sql"
)
// NodeQuery is the builder for querying Node entities.
type NodeQuery struct {
config
limit *int
offset *int
order []Order
unique []string
predicates []predicate.Node
// intermediate queries.
sql *sql.Selector
}
// Where adds a new predicate for the builder.
func (nq *NodeQuery) Where(ps ...predicate.Node) *NodeQuery {
nq.predicates = append(nq.predicates, ps...)
return nq
}
// Limit adds a limit step to the query.
func (nq *NodeQuery) Limit(limit int) *NodeQuery {
nq.limit = &limit
return nq
}
// Offset adds an offset step to the query.
func (nq *NodeQuery) Offset(offset int) *NodeQuery {
nq.offset = &offset
return nq
}
// Order adds an order step to the query.
func (nq *NodeQuery) Order(o ...Order) *NodeQuery {
nq.order = append(nq.order, o...)
return nq
}
// QueryParent chains the current query on the parent edge.
func (nq *NodeQuery) QueryParent() *NodeQuery {
query := &NodeQuery{config: nq.config}
t1 := sql.Table(node.Table)
t2 := nq.sqlQuery()
t2.Select(t2.C(node.ParentColumn))
query.sql = sql.Select(t1.Columns(node.Columns...)...).
From(t1).
Join(t2).
On(t1.C(node.FieldID), t2.C(node.ParentColumn))
return query
}
// QueryChildren chains the current query on the children edge.
func (nq *NodeQuery) QueryChildren() *NodeQuery {
query := &NodeQuery{config: nq.config}
t1 := sql.Table(node.Table)
t2 := nq.sqlQuery()
t2.Select(t2.C(node.FieldID))
query.sql = sql.Select().
From(t1).
Join(t2).
On(t1.C(node.ChildrenColumn), t2.C(node.FieldID))
return query
}
// Get returns a Node entity by its id.
func (nq *NodeQuery) Get(ctx context.Context, id int) (*Node, error) {
return nq.Where(node.ID(id)).Only(ctx)
}
// GetX is like Get, but panics if an error occurs.
func (nq *NodeQuery) GetX(ctx context.Context, id int) *Node {
n, err := nq.Get(ctx, id)
if err != nil {
panic(err)
}
return n
}
// First returns the first Node entity in the query. Returns *ErrNotFound when no node was found.
func (nq *NodeQuery) First(ctx context.Context) (*Node, error) {
ns, err := nq.Limit(1).All(ctx)
if err != nil {
return nil, err
}
if len(ns) == 0 {
return nil, &ErrNotFound{node.Label}
}
return ns[0], nil
}
// FirstX is like First, but panics if an error occurs.
func (nq *NodeQuery) FirstX(ctx context.Context) *Node {
n, err := nq.First(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return n
}
// FirstID returns the first Node id in the query. Returns *ErrNotFound when no id was found.
func (nq *NodeQuery) FirstID(ctx context.Context) (id int, err error) {
var ids []int
if ids, err = nq.Limit(1).IDs(ctx); err != nil {
return
}
if len(ids) == 0 {
err = &ErrNotFound{node.Label}
return
}
return ids[0], nil
}
// FirstXID is like FirstID, but panics if an error occurs.
func (nq *NodeQuery) FirstXID(ctx context.Context) int {
id, err := nq.FirstID(ctx)
if err != nil && !IsNotFound(err) {
panic(err)
}
return id
}
// Only returns the only Node entity in the query, returns an error if not exactly one entity was returned.
func (nq *NodeQuery) Only(ctx context.Context) (*Node, error) {
ns, err := nq.Limit(2).All(ctx)
if err != nil {
return nil, err
}
switch len(ns) {
case 1:
return ns[0], nil
case 0:
return nil, &ErrNotFound{node.Label}
default:
return nil, &ErrNotSingular{node.Label}
}
}
// OnlyX is like Only, but panics if an error occurs.
func (nq *NodeQuery) OnlyX(ctx context.Context) *Node {
n, err := nq.Only(ctx)
if err != nil {
panic(err)
}
return n
}
// OnlyID returns the only Node id in the query, returns an error if not exactly one id was returned.
func (nq *NodeQuery) OnlyID(ctx context.Context) (id int, err error) {
var ids []int
if ids, err = nq.Limit(2).IDs(ctx); err != nil {
return
}
switch len(ids) {
case 1:
id = ids[0]
case 0:
err = &ErrNotFound{node.Label}
default:
err = &ErrNotSingular{node.Label}
}
return
}
// OnlyXID is like OnlyID, but panics if an error occurs.
func (nq *NodeQuery) OnlyXID(ctx context.Context) int {
id, err := nq.OnlyID(ctx)
if err != nil {
panic(err)
}
return id
}
// All executes the query and returns a list of Nodes.
func (nq *NodeQuery) All(ctx context.Context) ([]*Node, error) {
return nq.sqlAll(ctx)
}
// AllX is like All, but panics if an error occurs.
func (nq *NodeQuery) AllX(ctx context.Context) []*Node {
ns, err := nq.All(ctx)
if err != nil {
panic(err)
}
return ns
}
// IDs executes the query and returns a list of Node ids.
func (nq *NodeQuery) IDs(ctx context.Context) ([]int, error) {
return nq.sqlIDs(ctx)
}
// IDsX is like IDs, but panics if an error occurs.
func (nq *NodeQuery) IDsX(ctx context.Context) []int {
ids, err := nq.IDs(ctx)
if err != nil {
panic(err)
}
return ids
}
// Count returns the count of the given query.
func (nq *NodeQuery) Count(ctx context.Context) (int, error) {
return nq.sqlCount(ctx)
}
// CountX is like Count, but panics if an error occurs.
func (nq *NodeQuery) CountX(ctx context.Context) int {
count, err := nq.Count(ctx)
if err != nil {
panic(err)
}
return count
}
// Exist returns true if the query has elements in the graph.
func (nq *NodeQuery) Exist(ctx context.Context) (bool, error) {
return nq.sqlExist(ctx)
}
// ExistX is like Exist, but panics if an error occurs.
func (nq *NodeQuery) ExistX(ctx context.Context) bool {
exist, err := nq.Exist(ctx)
if err != nil {
panic(err)
}
return exist
}
// Clone returns a duplicate of the query builder, including all associated steps. It can be
// used to prepare common query builders and use them differently after the clone is made.
func (nq *NodeQuery) Clone() *NodeQuery {
return &NodeQuery{
config: nq.config,
limit: nq.limit,
offset: nq.offset,
order: append([]Order{}, nq.order...),
unique: append([]string{}, nq.unique...),
predicates: append([]predicate.Node{}, nq.predicates...),
// clone intermediate queries.
sql: nq.sql.Clone(),
}
}
// GroupBy used to group vertices by one or more fields/columns.
// It is often used with aggregate functions, like: count, max, mean, min, sum.
//
// Example:
//
// var v []struct {
// Value int `json:"value,omitempty"`
// Count int `json:"count,omitempty"`
// }
//
// client.Node.Query().
// GroupBy(node.FieldValue).
// Aggregate(ent.Count()).
// Scan(ctx, &v)
//
func (nq *NodeQuery) GroupBy(field string, fields ...string) *NodeGroupBy {
group := &NodeGroupBy{config: nq.config}
group.fields = append([]string{field}, fields...)
group.sql = nq.sqlQuery()
return group
}
func (nq *NodeQuery) sqlAll(ctx context.Context) ([]*Node, error) {
rows := &sql.Rows{}
selector := nq.sqlQuery()
if unique := nq.unique; len(unique) == 0 {
selector.Distinct()
}
query, args := selector.Query()
if err := nq.driver.Query(ctx, query, args, rows); err != nil {
return nil, err
}
defer rows.Close()
var ns Nodes
if err := ns.FromRows(rows); err != nil {
return nil, err
}
ns.config(nq.config)
return ns, nil
}
func (nq *NodeQuery) sqlCount(ctx context.Context) (int, error) {
rows := &sql.Rows{}
selector := nq.sqlQuery()
unique := []string{node.FieldID}
if len(nq.unique) > 0 {
unique = nq.unique
}
selector.Count(sql.Distinct(selector.Columns(unique...)...))
query, args := selector.Query()
if err := nq.driver.Query(ctx, query, args, rows); err != nil {
return 0, err
}
defer rows.Close()
if !rows.Next() {
return 0, errors.New("ent: no rows found")
}
var n int
if err := rows.Scan(&n); err != nil {
return 0, fmt.Errorf("ent: failed reading count: %v", err)
}
return n, nil
}
func (nq *NodeQuery) sqlExist(ctx context.Context) (bool, error) {
n, err := nq.sqlCount(ctx)
if err != nil {
return false, fmt.Errorf("ent: check existence: %v", err)
}
return n > 0, nil
}
func (nq *NodeQuery) sqlIDs(ctx context.Context) ([]int, error) {
vs, err := nq.sqlAll(ctx)
if err != nil {
return nil, err
}
var ids []int
for _, v := range vs {
ids = append(ids, v.ID)
}
return ids, nil
}
func (nq *NodeQuery) sqlQuery() *sql.Selector {
t1 := sql.Table(node.Table)
selector := sql.Select(t1.Columns(node.Columns...)...).From(t1)
if nq.sql != nil {
selector = nq.sql
selector.Select(selector.Columns(node.Columns...)...)
}
for _, p := range nq.predicates {
p(selector)
}
for _, p := range nq.order {
p(selector)
}
if offset := nq.offset; offset != nil {
// limit is mandatory for offset clause. We start
// with default value, and override it below if needed.
selector.Offset(*offset).Limit(math.MaxInt64)
}
if limit := nq.limit; limit != nil {
selector.Limit(*limit)
}
return selector
}
// NodeQuery is the builder for group-by Node entities.
type NodeGroupBy struct {
config
fields []string
fns []Aggregate
// intermediate queries.
sql *sql.Selector
}
// Aggregate adds the given aggregation functions to the group-by query.
func (ngb *NodeGroupBy) Aggregate(fns ...Aggregate) *NodeGroupBy {
ngb.fns = append(ngb.fns, fns...)
return ngb
}
// Scan applies the group-by query and scan the result into the given value.
func (ngb *NodeGroupBy) Scan(ctx context.Context, v interface{}) error {
return ngb.sqlScan(ctx, v)
}
// ScanX is like Scan, but panics if an error occurs.
func (ngb *NodeGroupBy) ScanX(ctx context.Context, v interface{}) {
if err := ngb.Scan(ctx, v); err != nil {
panic(err)
}
}
// Strings returns list of strings from group-by. It is only allowed when querying group-by with one field.
func (ngb *NodeGroupBy) Strings(ctx context.Context) ([]string, error) {
if len(ngb.fields) > 1 {
return nil, errors.New("ent: NodeGroupBy.Strings is not achievable when grouping more than 1 field")
}
var v []string
if err := ngb.Scan(ctx, &v); err != nil {
return nil, err
}
return v, nil
}
// StringsX is like Strings, but panics if an error occurs.
func (ngb *NodeGroupBy) StringsX(ctx context.Context) []string {
v, err := ngb.Strings(ctx)
if err != nil {
panic(err)
}
return v
}
// Ints returns list of ints from group-by. It is only allowed when querying group-by with one field.
func (ngb *NodeGroupBy) Ints(ctx context.Context) ([]int, error) {
if len(ngb.fields) > 1 {
return nil, errors.New("ent: NodeGroupBy.Ints is not achievable when grouping more than 1 field")
}
var v []int
if err := ngb.Scan(ctx, &v); err != nil {
return nil, err
}
return v, nil
}
// IntsX is like Ints, but panics if an error occurs.
func (ngb *NodeGroupBy) IntsX(ctx context.Context) []int {
v, err := ngb.Ints(ctx)
if err != nil {
panic(err)
}
return v
}
// Float64s returns list of float64s from group-by. It is only allowed when querying group-by with one field.
func (ngb *NodeGroupBy) Float64s(ctx context.Context) ([]float64, error) {
if len(ngb.fields) > 1 {
return nil, errors.New("ent: NodeGroupBy.Float64s is not achievable when grouping more than 1 field")
}
var v []float64
if err := ngb.Scan(ctx, &v); err != nil {
return nil, err
}
return v, nil
}
// Float64sX is like Float64s, but panics if an error occurs.
func (ngb *NodeGroupBy) Float64sX(ctx context.Context) []float64 {
v, err := ngb.Float64s(ctx)
if err != nil {
panic(err)
}
return v
}
// Bools returns list of bools from group-by. It is only allowed when querying group-by with one field.
func (ngb *NodeGroupBy) Bools(ctx context.Context) ([]bool, error) {
if len(ngb.fields) > 1 {
return nil, errors.New("ent: NodeGroupBy.Bools is not achievable when grouping more than 1 field")
}
var v []bool
if err := ngb.Scan(ctx, &v); err != nil {
return nil, err
}
return v, nil
}
// BoolsX is like Bools, but panics if an error occurs.
func (ngb *NodeGroupBy) BoolsX(ctx context.Context) []bool {
v, err := ngb.Bools(ctx)
if err != nil {
panic(err)
}
return v
}
func (ngb *NodeGroupBy) sqlScan(ctx context.Context, v interface{}) error {
rows := &sql.Rows{}
query, args := ngb.sqlQuery().Query()
if err := ngb.driver.Query(ctx, query, args, rows); err != nil {
return err
}
defer rows.Close()
return sql.ScanSlice(rows, v)
}
func (ngb *NodeGroupBy) sqlQuery() *sql.Selector {
selector := ngb.sql
columns := make([]string, 0, len(ngb.fields)+len(ngb.fns))
columns = append(columns, ngb.fields...)
for _, fn := range ngb.fns {
columns = append(columns, fn.SQL(selector))
}
return selector.Select(columns...).GroupBy(ngb.fields...)
}

View File

@@ -0,0 +1,462 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"context"
"errors"
"fmt"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/node"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/predicate"
"github.com/facebookincubator/ent/dialect/sql"
)
// NodeUpdate is the builder for updating Node entities.
type NodeUpdate struct {
config
value *int
parent map[int]struct{}
children map[int]struct{}
clearedParent bool
removedChildren map[int]struct{}
predicates []predicate.Node
}
// Where adds a new predicate for the builder.
func (nu *NodeUpdate) Where(ps ...predicate.Node) *NodeUpdate {
nu.predicates = append(nu.predicates, ps...)
return nu
}
// SetValue sets the value field.
func (nu *NodeUpdate) SetValue(i int) *NodeUpdate {
nu.value = &i
return nu
}
// SetParentID sets the parent edge to Node by id.
func (nu *NodeUpdate) SetParentID(id int) *NodeUpdate {
if nu.parent == nil {
nu.parent = make(map[int]struct{})
}
nu.parent[id] = struct{}{}
return nu
}
// SetNillableParentID sets the parent edge to Node by id if the given value is not nil.
func (nu *NodeUpdate) SetNillableParentID(id *int) *NodeUpdate {
if id != nil {
nu = nu.SetParentID(*id)
}
return nu
}
// SetParent sets the parent edge to Node.
func (nu *NodeUpdate) SetParent(n *Node) *NodeUpdate {
return nu.SetParentID(n.ID)
}
// AddChildIDs adds the children edge to Node by ids.
func (nu *NodeUpdate) AddChildIDs(ids ...int) *NodeUpdate {
if nu.children == nil {
nu.children = make(map[int]struct{})
}
for i := range ids {
nu.children[ids[i]] = struct{}{}
}
return nu
}
// AddChildren adds the children edges to Node.
func (nu *NodeUpdate) AddChildren(n ...*Node) *NodeUpdate {
ids := make([]int, len(n))
for i := range n {
ids[i] = n[i].ID
}
return nu.AddChildIDs(ids...)
}
// ClearParent clears the parent edge to Node.
func (nu *NodeUpdate) ClearParent() *NodeUpdate {
nu.clearedParent = true
return nu
}
// RemoveChildIDs removes the children edge to Node by ids.
func (nu *NodeUpdate) RemoveChildIDs(ids ...int) *NodeUpdate {
if nu.removedChildren == nil {
nu.removedChildren = make(map[int]struct{})
}
for i := range ids {
nu.removedChildren[ids[i]] = struct{}{}
}
return nu
}
// RemoveChildren removes children edges to Node.
func (nu *NodeUpdate) RemoveChildren(n ...*Node) *NodeUpdate {
ids := make([]int, len(n))
for i := range n {
ids[i] = n[i].ID
}
return nu.RemoveChildIDs(ids...)
}
// Save executes the query and returns the number of rows/vertices matched by this operation.
func (nu *NodeUpdate) Save(ctx context.Context) (int, error) {
if len(nu.parent) > 1 {
return 0, errors.New("ent: multiple assignments on a unique edge \"parent\"")
}
return nu.sqlSave(ctx)
}
// SaveX is like Save, but panics if an error occurs.
func (nu *NodeUpdate) SaveX(ctx context.Context) int {
affected, err := nu.Save(ctx)
if err != nil {
panic(err)
}
return affected
}
// Exec executes the query.
func (nu *NodeUpdate) Exec(ctx context.Context) error {
_, err := nu.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (nu *NodeUpdate) ExecX(ctx context.Context) {
if err := nu.Exec(ctx); err != nil {
panic(err)
}
}
func (nu *NodeUpdate) sqlSave(ctx context.Context) (n int, err error) {
selector := sql.Select(node.FieldID).From(sql.Table(node.Table))
for _, p := range nu.predicates {
p(selector)
}
rows := &sql.Rows{}
query, args := selector.Query()
if err = nu.driver.Query(ctx, query, args, rows); err != nil {
return 0, err
}
defer rows.Close()
var ids []int
for rows.Next() {
var id int
if err := rows.Scan(&id); err != nil {
return 0, fmt.Errorf("ent: failed reading id: %v", err)
}
ids = append(ids, id)
}
if len(ids) == 0 {
return 0, nil
}
tx, err := nu.driver.Tx(ctx)
if err != nil {
return 0, err
}
var (
update bool
res sql.Result
builder = sql.Update(node.Table).Where(sql.InInts(node.FieldID, ids...))
)
if nu.value != nil {
update = true
builder.Set(node.FieldValue, *nu.value)
}
if update {
query, args := builder.Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return 0, rollback(tx, err)
}
}
if nu.clearedParent {
query, args := sql.Update(node.ParentTable).
SetNull(node.ParentColumn).
Where(sql.InInts(node.FieldID, ids...)).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return 0, rollback(tx, err)
}
}
if len(nu.parent) > 0 {
for eid := range nu.parent {
query, args := sql.Update(node.ParentTable).
Set(node.ParentColumn, eid).
Where(sql.InInts(node.FieldID, ids...)).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return 0, rollback(tx, err)
}
}
}
if len(nu.removedChildren) > 0 {
eids := make([]int, len(nu.removedChildren))
for eid := range nu.removedChildren {
eids = append(eids, eid)
}
query, args := sql.Update(node.ChildrenTable).
SetNull(node.ChildrenColumn).
Where(sql.InInts(node.ChildrenColumn, ids...)).
Where(sql.InInts(node.FieldID, eids...)).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return 0, rollback(tx, err)
}
}
if len(nu.children) > 0 {
for _, id := range ids {
p := sql.P()
for eid := range nu.children {
p.Or().EQ(node.FieldID, eid)
}
query, args := sql.Update(node.ChildrenTable).
Set(node.ChildrenColumn, id).
Where(sql.And(p, sql.IsNull(node.ChildrenColumn))).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return 0, rollback(tx, err)
}
affected, err := res.RowsAffected()
if err != nil {
return 0, rollback(tx, err)
}
if int(affected) < len(nu.children) {
return 0, rollback(tx, &ErrConstraintFailed{msg: fmt.Sprintf("one of \"children\" %v already connected to a different \"Node\"", keys(nu.children))})
}
}
}
if err = tx.Commit(); err != nil {
return 0, err
}
return len(ids), nil
}
// NodeUpdateOne is the builder for updating a single Node entity.
type NodeUpdateOne struct {
config
id int
value *int
parent map[int]struct{}
children map[int]struct{}
clearedParent bool
removedChildren map[int]struct{}
}
// SetValue sets the value field.
func (nuo *NodeUpdateOne) SetValue(i int) *NodeUpdateOne {
nuo.value = &i
return nuo
}
// SetParentID sets the parent edge to Node by id.
func (nuo *NodeUpdateOne) SetParentID(id int) *NodeUpdateOne {
if nuo.parent == nil {
nuo.parent = make(map[int]struct{})
}
nuo.parent[id] = struct{}{}
return nuo
}
// SetNillableParentID sets the parent edge to Node by id if the given value is not nil.
func (nuo *NodeUpdateOne) SetNillableParentID(id *int) *NodeUpdateOne {
if id != nil {
nuo = nuo.SetParentID(*id)
}
return nuo
}
// SetParent sets the parent edge to Node.
func (nuo *NodeUpdateOne) SetParent(n *Node) *NodeUpdateOne {
return nuo.SetParentID(n.ID)
}
// AddChildIDs adds the children edge to Node by ids.
func (nuo *NodeUpdateOne) AddChildIDs(ids ...int) *NodeUpdateOne {
if nuo.children == nil {
nuo.children = make(map[int]struct{})
}
for i := range ids {
nuo.children[ids[i]] = struct{}{}
}
return nuo
}
// AddChildren adds the children edges to Node.
func (nuo *NodeUpdateOne) AddChildren(n ...*Node) *NodeUpdateOne {
ids := make([]int, len(n))
for i := range n {
ids[i] = n[i].ID
}
return nuo.AddChildIDs(ids...)
}
// ClearParent clears the parent edge to Node.
func (nuo *NodeUpdateOne) ClearParent() *NodeUpdateOne {
nuo.clearedParent = true
return nuo
}
// RemoveChildIDs removes the children edge to Node by ids.
func (nuo *NodeUpdateOne) RemoveChildIDs(ids ...int) *NodeUpdateOne {
if nuo.removedChildren == nil {
nuo.removedChildren = make(map[int]struct{})
}
for i := range ids {
nuo.removedChildren[ids[i]] = struct{}{}
}
return nuo
}
// RemoveChildren removes children edges to Node.
func (nuo *NodeUpdateOne) RemoveChildren(n ...*Node) *NodeUpdateOne {
ids := make([]int, len(n))
for i := range n {
ids[i] = n[i].ID
}
return nuo.RemoveChildIDs(ids...)
}
// Save executes the query and returns the updated entity.
func (nuo *NodeUpdateOne) Save(ctx context.Context) (*Node, error) {
if len(nuo.parent) > 1 {
return nil, errors.New("ent: multiple assignments on a unique edge \"parent\"")
}
return nuo.sqlSave(ctx)
}
// SaveX is like Save, but panics if an error occurs.
func (nuo *NodeUpdateOne) SaveX(ctx context.Context) *Node {
n, err := nuo.Save(ctx)
if err != nil {
panic(err)
}
return n
}
// Exec executes the query on the entity.
func (nuo *NodeUpdateOne) Exec(ctx context.Context) error {
_, err := nuo.Save(ctx)
return err
}
// ExecX is like Exec, but panics if an error occurs.
func (nuo *NodeUpdateOne) ExecX(ctx context.Context) {
if err := nuo.Exec(ctx); err != nil {
panic(err)
}
}
func (nuo *NodeUpdateOne) sqlSave(ctx context.Context) (n *Node, err error) {
selector := sql.Select(node.Columns...).From(sql.Table(node.Table))
node.ID(nuo.id)(selector)
rows := &sql.Rows{}
query, args := selector.Query()
if err = nuo.driver.Query(ctx, query, args, rows); err != nil {
return nil, err
}
defer rows.Close()
var ids []int
for rows.Next() {
var id int
n = &Node{config: nuo.config}
if err := n.FromRows(rows); err != nil {
return nil, fmt.Errorf("ent: failed scanning row into Node: %v", err)
}
id = n.ID
ids = append(ids, id)
}
switch n := len(ids); {
case n == 0:
return nil, fmt.Errorf("ent: Node not found with id: %v", nuo.id)
case n > 1:
return nil, fmt.Errorf("ent: more than one Node with the same id: %v", nuo.id)
}
tx, err := nuo.driver.Tx(ctx)
if err != nil {
return nil, err
}
var (
update bool
res sql.Result
builder = sql.Update(node.Table).Where(sql.InInts(node.FieldID, ids...))
)
if nuo.value != nil {
update = true
builder.Set(node.FieldValue, *nuo.value)
n.Value = *nuo.value
}
if update {
query, args := builder.Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return nil, rollback(tx, err)
}
}
if nuo.clearedParent {
query, args := sql.Update(node.ParentTable).
SetNull(node.ParentColumn).
Where(sql.InInts(node.FieldID, ids...)).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return nil, rollback(tx, err)
}
}
if len(nuo.parent) > 0 {
for eid := range nuo.parent {
query, args := sql.Update(node.ParentTable).
Set(node.ParentColumn, eid).
Where(sql.InInts(node.FieldID, ids...)).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return nil, rollback(tx, err)
}
}
}
if len(nuo.removedChildren) > 0 {
eids := make([]int, len(nuo.removedChildren))
for eid := range nuo.removedChildren {
eids = append(eids, eid)
}
query, args := sql.Update(node.ChildrenTable).
SetNull(node.ChildrenColumn).
Where(sql.InInts(node.ChildrenColumn, ids...)).
Where(sql.InInts(node.FieldID, eids...)).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return nil, rollback(tx, err)
}
}
if len(nuo.children) > 0 {
for _, id := range ids {
p := sql.P()
for eid := range nuo.children {
p.Or().EQ(node.FieldID, eid)
}
query, args := sql.Update(node.ChildrenTable).
Set(node.ChildrenColumn, id).
Where(sql.And(p, sql.IsNull(node.ChildrenColumn))).
Query()
if err := tx.Exec(ctx, query, args, &res); err != nil {
return nil, rollback(tx, err)
}
affected, err := res.RowsAffected()
if err != nil {
return nil, rollback(tx, err)
}
if int(affected) < len(nuo.children) {
return nil, rollback(tx, &ErrConstraintFailed{msg: fmt.Sprintf("one of \"children\" %v already connected to a different \"Node\"", keys(nuo.children))})
}
}
}
if err = tx.Commit(); err != nil {
return nil, err
}
return n, nil
}

View File

@@ -0,0 +1,10 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package predicate
import (
"github.com/facebookincubator/ent/dialect/sql"
)
// Node is the predicate function for node builders.
type Node func(*sql.Selector)

View File

@@ -0,0 +1,28 @@
package schema
import (
"github.com/facebookincubator/ent"
"github.com/facebookincubator/ent/schema/edge"
"github.com/facebookincubator/ent/schema/field"
)
// Node holds the schema definition for the Node entity.
type Node struct {
ent.Schema
}
// Fields of the Node.
func (Node) Fields() []ent.Field {
return []ent.Field{
field.Int("value"),
}
}
// Edges of the Node.
func (Node) Edges() []ent.Edge {
return []ent.Edge{
edge.To("children", Node.Type).
From("parent").
Unique(),
}
}

View File

@@ -0,0 +1,93 @@
// Code generated (@generated) by entc, DO NOT EDIT.
package ent
import (
"context"
"github.com/facebookincubator/ent/dialect"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/migrate"
)
// Tx is a transactional client that is created by calling Client.Tx().
type Tx struct {
config
// Node is the client for interacting with the Node builders.
Node *NodeClient
}
// Commit commits the transaction.
func (tx *Tx) Commit() error {
return tx.config.driver.(*txDriver).tx.Commit()
}
// Rollback rollbacks the transaction.
func (tx *Tx) Rollback() error {
return tx.config.driver.(*txDriver).tx.Rollback()
}
// Client returns a Client that binds to current transaction.
func (tx *Tx) Client() *Client {
return &Client{
config: tx.config,
Schema: migrate.NewSchema(tx.driver),
Node: NewNodeClient(tx.config),
}
}
// txDriver wraps the given dialect.Tx with a nop dialect.Driver implementation.
// The idea is to support transactions without adding any extra code to the builders.
// When a builder calls to driver.Tx(), it gets the same dialect.Tx instance.
// Commit and Rollback are nop for the internal builders and the user must call one
// of them in order to commit or rollback the transaction.
//
// If a closed transaction is embedded in one of the generated entities, and the entity
// applies a query, for example: Node.QueryXXX(), the query will be executed
// through the driver which created this transaction.
//
// Note that txDriver is not goroutine safe.
type txDriver struct {
// the driver we started the transaction from.
drv dialect.Driver
// tx is the underlying transaction.
tx dialect.Tx
}
// newTx creates a new transactional driver.
func newTx(ctx context.Context, drv dialect.Driver) (*txDriver, error) {
tx, err := drv.Tx(ctx)
if err != nil {
return nil, err
}
return &txDriver{tx: tx, drv: drv}, nil
}
// Tx returns the transaction wrapper (txDriver) to avoid Commit or Rollback calls
// from the internal builders. Should be called only by the internal builders.
func (tx *txDriver) Tx(context.Context) (dialect.Tx, error) { return tx, nil }
// Dialect returns the dialect of the driver we started the transaction from.
func (tx *txDriver) Dialect() string { return tx.drv.Dialect() }
// Close is a nop close.
func (*txDriver) Close() error { return nil }
// Commit is a nop commit for the internal builders.
// User must call `Tx.Commit` in order to commit the transaction.
func (*txDriver) Commit() error { return nil }
// Rollback is a nop rollback for the internal builders.
// User must call `Tx.Rollback` in order to rollback the transaction.
func (*txDriver) Rollback() error { return nil }
// Exec calls tx.Exec.
func (tx *txDriver) Exec(ctx context.Context, query string, args, v interface{}) error {
return tx.tx.Exec(ctx, query, args, v)
}
// Query calls tx.Query.
func (tx *txDriver) Query(ctx context.Context, query string, args, v interface{}) error {
return tx.tx.Query(ctx, query, args, v)
}
var _ dialect.Driver = (*txDriver)(nil)

View File

@@ -0,0 +1,96 @@
package main
import (
"context"
"fmt"
"log"
"github.com/facebookincubator/ent/examples/o2mrecur/ent"
"github.com/facebookincubator/ent/examples/o2mrecur/ent/node"
"github.com/facebookincubator/ent/dialect/sql"
_ "github.com/mattn/go-sqlite3"
)
func main() {
db, err := sql.Open("sqlite3", "file:o2o2types?mode=memory&cache=shared&_fk=1")
if err != nil {
log.Fatalf("failed opening connection to sqlite: %v", err)
}
defer db.Close()
client := ent.NewClient(ent.Driver(db))
ctx := context.Background()
// run the auto migration tool.
if err := client.Schema.Create(ctx); err != nil {
log.Fatalf("failed creating schema resources: %v", err)
}
if err := Do(ctx, client); err != nil {
log.Fatal(err)
}
}
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)
}
// Add additional nodes to the tree:
//
// 2
// / \
// 1 4
// / \
// 3 5
//
// Unlike `Create`, `CreateX` panics if an error occurs.
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]
// Get all leafs (nodes without children).
// Unlike `Int`, `IntX` panics if an error occurs.
ints := client.Node.
Query(). // All nodes.
Where(node.Not(node.HasChildren())). // Only leafs.
Order(ent.Asc(node.FieldValue)). // Order by their `value` field.
GroupBy(node.FieldValue). // Extract only the `value` field.
IntsX(ctx)
fmt.Println(ints)
// Output: [1 3 5]
// Get orphan nodes (nodes without parent).
// Unlike `Only`, `OnlyX` panics if an error occurs.
orphan := client.Node.
Query().
Where(node.Not(node.HasParent())).
OnlyX(ctx)
fmt.Println(orphan)
// Output: Node(id=1, value=2)
return nil
}

View File

@@ -28,6 +28,7 @@ func main() {
log.Fatal(err)
}
}
func Do(ctx context.Context, client *ent.Client) error {
head, err := client.Node.
Create().