mirror of
https://github.com/ent/ent.git
synced 2026-05-24 09:31:56 +03:00
schema/edge: add support for configuring foreign-key symbols
Fixed #1423
This commit is contained in:
committed by
Ariel Mashraki
parent
745afde770
commit
f3f03e1edd
@@ -977,9 +977,20 @@ func (User) Edges() []ent.Edge {
|
|||||||
edge.To("pets", Pet.Type).
|
edge.To("pets", Pet.Type).
|
||||||
// Set the column name in the "pets" table for O2M relationship.
|
// Set the column name in the "pets" table for O2M relationship.
|
||||||
StorageKey(edge.Column("owner_id")),
|
StorageKey(edge.Column("owner_id")),
|
||||||
|
edge.To("cars", Car.Type).
|
||||||
|
// Set the symbol of the foreign-key constraint for O2M relationship.
|
||||||
|
StorageKey(edge.Symbol("cars_owner_id")),
|
||||||
edge.To("friends", User.Type).
|
edge.To("friends", User.Type).
|
||||||
// Set the join-table and the column names for M2M relationship.
|
// Set the join-table, and the column names for a M2M relationship.
|
||||||
StorageKey(edge.Table("friends"), edge.Columns("user_id", "friend_id")),
|
StorageKey(edge.Table("friends"), edge.Columns("user_id", "friend_id")),
|
||||||
|
edge.To("groups", Group.Type).
|
||||||
|
// Set the join-table, its column names and the symbols
|
||||||
|
// of the foreign-key constraints for M2M relationship.
|
||||||
|
StorageKey(
|
||||||
|
edge.Table("groups"),
|
||||||
|
edge.Columns("user_id", "group_id"),
|
||||||
|
edge.Symbols("groups_id1", "groups_id2")
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -426,7 +426,7 @@ func (g *Graph) Tables() (all []*schema.Table) {
|
|||||||
OnDelete: deleteAction(e),
|
OnDelete: deleteAction(e),
|
||||||
Columns: []*schema.Column{column},
|
Columns: []*schema.Column{column},
|
||||||
RefColumns: []*schema.Column{ref.PrimaryKey[0]},
|
RefColumns: []*schema.Column{ref.PrimaryKey[0]},
|
||||||
Symbol: fmt.Sprintf("%s_%s_%s", owner.Name, ref.Name, e.Name),
|
Symbol: fkSymbol(e, owner, ref),
|
||||||
})
|
})
|
||||||
case M2O:
|
case M2O:
|
||||||
ref, owner := tables[e.Type.Table()], tables[e.Rel.Table]
|
ref, owner := tables[e.Type.Table()], tables[e.Rel.Table]
|
||||||
@@ -438,7 +438,7 @@ func (g *Graph) Tables() (all []*schema.Table) {
|
|||||||
OnDelete: deleteAction(e),
|
OnDelete: deleteAction(e),
|
||||||
Columns: []*schema.Column{column},
|
Columns: []*schema.Column{column},
|
||||||
RefColumns: []*schema.Column{ref.PrimaryKey[0]},
|
RefColumns: []*schema.Column{ref.PrimaryKey[0]},
|
||||||
Symbol: fmt.Sprintf("%s_%s_%s", owner.Name, ref.Name, e.Name),
|
Symbol: fkSymbol(e, owner, ref),
|
||||||
})
|
})
|
||||||
case M2M:
|
case M2M:
|
||||||
t1, t2 := tables[n.Table()], tables[e.Type.Table()]
|
t1, t2 := tables[n.Table()], tables[e.Type.Table()]
|
||||||
@@ -452,6 +452,7 @@ func (g *Graph) Tables() (all []*schema.Table) {
|
|||||||
c2.Type = ref.Type.Type
|
c2.Type = ref.Type.Type
|
||||||
c2.Size = ref.size()
|
c2.Size = ref.size()
|
||||||
}
|
}
|
||||||
|
s1, s2 := fkSymbols(e, c1, c2)
|
||||||
all = append(all, &schema.Table{
|
all = append(all, &schema.Table{
|
||||||
Name: e.Rel.Table,
|
Name: e.Rel.Table,
|
||||||
Columns: []*schema.Column{c1, c2},
|
Columns: []*schema.Column{c1, c2},
|
||||||
@@ -462,14 +463,14 @@ func (g *Graph) Tables() (all []*schema.Table) {
|
|||||||
OnDelete: schema.Cascade,
|
OnDelete: schema.Cascade,
|
||||||
Columns: []*schema.Column{c1},
|
Columns: []*schema.Column{c1},
|
||||||
RefColumns: []*schema.Column{t1.PrimaryKey[0]},
|
RefColumns: []*schema.Column{t1.PrimaryKey[0]},
|
||||||
Symbol: fmt.Sprintf("%s_%s", e.Rel.Table, c1.Name),
|
Symbol: s1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
RefTable: t2,
|
RefTable: t2,
|
||||||
OnDelete: schema.Cascade,
|
OnDelete: schema.Cascade,
|
||||||
Columns: []*schema.Column{c2},
|
Columns: []*schema.Column{c2},
|
||||||
RefColumns: []*schema.Column{t2.PrimaryKey[0]},
|
RefColumns: []*schema.Column{t2.PrimaryKey[0]},
|
||||||
Symbol: fmt.Sprintf("%s_%s", e.Rel.Table, c2.Name),
|
Symbol: s2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -493,6 +494,30 @@ func mayAddColumn(t *schema.Table, c *schema.Column) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fkSymbol returns the symbol of the foreign-key constraint for edges of type O2M, M2O and O2O.
|
||||||
|
// It returns the symbol of the storage-key if it was provided, and generate custom one otherwise.
|
||||||
|
func fkSymbol(e *Edge, ownerT, refT *schema.Table) string {
|
||||||
|
if k, _ := e.StorageKey(); k != nil && len(k.Symbols) == 1 {
|
||||||
|
return k.Symbols[0]
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s_%s_%s", ownerT.Name, refT.Name, e.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
// fkSymbols is like fkSymbol but for M2M edges.
|
||||||
|
func fkSymbols(e *Edge, c1, c2 *schema.Column) (string, string) {
|
||||||
|
s1 := fmt.Sprintf("%s_%s", e.Rel.Table, c1.Name)
|
||||||
|
s2 := fmt.Sprintf("%s_%s", e.Rel.Table, c2.Name)
|
||||||
|
if k, _ := e.StorageKey(); k != nil {
|
||||||
|
if len(k.Symbols) > 0 {
|
||||||
|
s1 = k.Symbols[0]
|
||||||
|
}
|
||||||
|
if len(k.Symbols) > 1 {
|
||||||
|
s2 = k.Symbols[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return s1, s2
|
||||||
|
}
|
||||||
|
|
||||||
// deleteAction returns the referential action for DELETE operations of the given edge.
|
// deleteAction returns the referential action for DELETE operations of the given edge.
|
||||||
func deleteAction(e *Edge) schema.ReferenceOption {
|
func deleteAction(e *Edge) schema.ReferenceOption {
|
||||||
action := schema.SetNull
|
action := schema.SetNull
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ var (
|
|||||||
PrimaryKey: []*schema.Column{PetsColumns[0]},
|
PrimaryKey: []*schema.Column{PetsColumns[0]},
|
||||||
ForeignKeys: []*schema.ForeignKey{
|
ForeignKeys: []*schema.ForeignKey{
|
||||||
{
|
{
|
||||||
Symbol: "pets_users_pets",
|
Symbol: "user_pet_id",
|
||||||
Columns: []*schema.Column{PetsColumns[1]},
|
Columns: []*schema.Column{PetsColumns[1]},
|
||||||
RefColumns: []*schema.Column{UsersColumns[0]},
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
||||||
OnDelete: schema.SetNull,
|
OnDelete: schema.SetNull,
|
||||||
@@ -157,13 +157,13 @@ var (
|
|||||||
PrimaryKey: []*schema.Column{FriendsColumns[0], FriendsColumns[1]},
|
PrimaryKey: []*schema.Column{FriendsColumns[0], FriendsColumns[1]},
|
||||||
ForeignKeys: []*schema.ForeignKey{
|
ForeignKeys: []*schema.ForeignKey{
|
||||||
{
|
{
|
||||||
Symbol: "friends_user",
|
Symbol: "user_friend_id1",
|
||||||
Columns: []*schema.Column{FriendsColumns[0]},
|
Columns: []*schema.Column{FriendsColumns[0]},
|
||||||
RefColumns: []*schema.Column{UsersColumns[0]},
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
||||||
OnDelete: schema.Cascade,
|
OnDelete: schema.Cascade,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Symbol: "friends_friend",
|
Symbol: "user_friend_id2",
|
||||||
Columns: []*schema.Column{FriendsColumns[1]},
|
Columns: []*schema.Column{FriendsColumns[1]},
|
||||||
RefColumns: []*schema.Column{UsersColumns[0]},
|
RefColumns: []*schema.Column{UsersColumns[0]},
|
||||||
OnDelete: schema.Cascade,
|
OnDelete: schema.Cascade,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ func (Media) Fields() []ent.Field {
|
|||||||
// Indexes of the Media.
|
// Indexes of the Media.
|
||||||
func (Media) Indexes() []ent.Index {
|
func (Media) Indexes() []ent.Index {
|
||||||
return []ent.Index{
|
return []ent.Index{
|
||||||
index.Fields("source", "source_uri").Unique(),
|
index.Fields("source", "source_uri").
|
||||||
|
Unique(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,10 +100,14 @@ func (User) Edges() []ent.Edge {
|
|||||||
edge.To("car", Car.Type),
|
edge.To("car", Car.Type),
|
||||||
// New edges to added.
|
// New edges to added.
|
||||||
edge.To("pets", Pet.Type).
|
edge.To("pets", Pet.Type).
|
||||||
StorageKey(edge.Column("owner_id")).
|
StorageKey(edge.Column("owner_id"), edge.Symbol("user_pet_id")).
|
||||||
Unique(),
|
Unique(),
|
||||||
edge.To("friends", User.Type).
|
edge.To("friends", User.Type).
|
||||||
StorageKey(edge.Table("friends"), edge.Columns("user", "friend")),
|
StorageKey(
|
||||||
|
edge.Table("friends"),
|
||||||
|
edge.Columns("user", "friend"),
|
||||||
|
edge.Symbols("user_friend_id1", "user_friend_id2"),
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,7 +126,6 @@ type Car struct {
|
|||||||
|
|
||||||
func (Car) Edges() []ent.Edge {
|
func (Car) Edges() []ent.Edge {
|
||||||
return []ent.Edge{
|
return []ent.Edge{
|
||||||
// Car now can have more than 1 owner (not unique anymore).
|
|
||||||
edge.From("owner", User.Type).
|
edge.From("owner", User.Type).
|
||||||
Ref("car").
|
Ref("car").
|
||||||
Unique(),
|
Unique(),
|
||||||
|
|||||||
@@ -103,6 +103,12 @@ func TestSQLite(t *testing.T) {
|
|||||||
ContainsFold(t, client)
|
ContainsFold(t, client)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStorageKey(t *testing.T) {
|
||||||
|
require.Equal(t, "user_pet_id", migratev2.PetsTable.ForeignKeys[0].Symbol)
|
||||||
|
require.Equal(t, "user_friend_id1", migratev2.FriendsTable.ForeignKeys[0].Symbol)
|
||||||
|
require.Equal(t, "user_friend_id2", migratev2.FriendsTable.ForeignKeys[1].Symbol)
|
||||||
|
}
|
||||||
|
|
||||||
func V1ToV2(t *testing.T, dialect string, clientv1 *entv1.Client, clientv2 *entv2.Client) {
|
func V1ToV2(t *testing.T, dialect string, clientv1 *entv1.Client, clientv2 *entv2.Client) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|||||||
@@ -194,6 +194,7 @@ func (b *inverseBuilder) Descriptor() *Descriptor {
|
|||||||
// StorageKey holds the configuration for edge storage-key.
|
// StorageKey holds the configuration for edge storage-key.
|
||||||
type StorageKey struct {
|
type StorageKey struct {
|
||||||
Table string // Table or label.
|
Table string // Table or label.
|
||||||
|
Symbols []string // Symbols/names of the foreign-key constraints.
|
||||||
Columns []string // Foreign-key columns.
|
Columns []string // Foreign-key columns.
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,6 +208,24 @@ func Table(name string) StorageOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Symbol sets the symbol/name of the foreign-key constraint for O2O, O2M and M2O edges.
|
||||||
|
// Note that, for M2M edges (2 columns and 2 constraints), use the edge.Symbols option.
|
||||||
|
func Symbol(symbol string) StorageOption {
|
||||||
|
return func(key *StorageKey) {
|
||||||
|
key.Symbols = []string{symbol}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Symbols sets the symbol/name of the foreign-key constraints for M2M edges.
|
||||||
|
// The 1st column defines the name of the "To" edge, and the 2nd defines
|
||||||
|
// the name of the "From" edge (inverse edge).
|
||||||
|
// Note that, for O2O, O2M and M2O edges, use the edge.Symbol option.
|
||||||
|
func Symbols(to, from string) StorageOption {
|
||||||
|
return func(key *StorageKey) {
|
||||||
|
key.Symbols = []string{to, from}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Column sets the foreign-key column name option for O2O, O2M and M2O edges.
|
// Column sets the foreign-key column name option for O2O, O2M and M2O edges.
|
||||||
// Note that, for M2M edges (2 columns), use the edge.Columns option.
|
// Note that, for M2M edges (2 columns), use the edge.Columns option.
|
||||||
func Column(name string) StorageOption {
|
func Column(name string) StorageOption {
|
||||||
|
|||||||
@@ -86,13 +86,13 @@ func TestEdge(t *testing.T) {
|
|||||||
|
|
||||||
from = edge.To("following", User.Type).
|
from = edge.To("following", User.Type).
|
||||||
StructTag("following").
|
StructTag("following").
|
||||||
StorageKey(edge.Table("user_followers"), edge.Columns("following_id", "followers_id")).
|
StorageKey(edge.Table("user_followers"), edge.Columns("following_id", "followers_id"), edge.Symbol("users_followers")).
|
||||||
From("followers").
|
From("followers").
|
||||||
StructTag("followers").
|
StructTag("followers").
|
||||||
Descriptor()
|
Descriptor()
|
||||||
assert.Equal("followers", from.Tag)
|
assert.Equal("followers", from.Tag)
|
||||||
assert.Equal("following", from.Ref.Tag)
|
assert.Equal("following", from.Ref.Tag)
|
||||||
assert.Equal(edge.StorageKey{Table: "user_followers", Columns: []string{"following_id", "followers_id"}}, *from.Ref.StorageKey)
|
assert.Equal(edge.StorageKey{Table: "user_followers", Symbols: []string{"users_followers"}, Columns: []string{"following_id", "followers_id"}}, *from.Ref.StorageKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
type GQL struct {
|
type GQL struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user