feat: add support for polymorphic database relationships

This commit is contained in:
Vikram Rangnekar
2020-06-15 03:06:50 -04:00
parent 7b5548a2c6
commit 06214a3850
15 changed files with 443 additions and 217 deletions

View File

@ -71,6 +71,7 @@ type Field struct {
argsA [5]Arg
Children []int32
childrenA [5]int32
Union bool
}
type Arg struct {
@ -155,7 +156,7 @@ func Parse(gql []byte) (*Operation, error) {
if p.peek(itemFragment) {
p.ignore()
if f, err := p.parseFragment(false); err != nil {
if f, err := p.parseFragment(); err != nil {
fragPool.Put(f)
return nil, err
}
@ -181,7 +182,7 @@ func Parse(gql []byte) (*Operation, error) {
return op, nil
}
func (p *Parser) parseFragment(inline bool) (*Fragment, error) {
func (p *Parser) parseFragment() (*Fragment, error) {
var err error
frag := fragPool.Get().(*Fragment)
@ -190,7 +191,7 @@ func (p *Parser) parseFragment(inline bool) (*Fragment, error) {
if p.peek(itemName) {
frag.Name = p.val(p.next())
} else if !inline {
} else {
return frag, errors.New("fragment: missing name")
}
@ -217,18 +218,16 @@ func (p *Parser) parseFragment(inline bool) (*Fragment, error) {
return frag, fmt.Errorf("fragment: %v", err)
}
if !inline {
if p.frags == nil {
p.frags = make(map[uint64]*Fragment)
}
_, _ = p.h.WriteString(frag.Name)
k := p.h.Sum64()
p.h.Reset()
p.frags[k] = frag
if p.frags == nil {
p.frags = make(map[uint64]*Fragment)
}
_, _ = p.h.WriteString(frag.Name)
k := p.h.Sum64()
p.h.Reset()
p.frags[k] = frag
return frag, nil
}
@ -405,18 +404,27 @@ func (p *Parser) parseNormalFields(st *Stack, fields []Field) ([]Field, error) {
}
func (p *Parser) parseFragmentFields(st *Stack, fields []Field) ([]Field, error) {
var fr *Fragment
var err error
pid := st.Peek()
if p.peek(itemOn) {
if fr, err = p.parseFragment(true); err != nil {
p.ignore()
fields[pid].Union = true
if fields, err = p.parseNormalFields(st, fields); err != nil {
return nil, err
}
defer fragPool.Put(fr)
// If parent is a union selector than copy over args from the parent
// to the first child which is the root selector for each union type.
for i := pid + 1; i < int32(len(fields)); i++ {
f := &fields[i]
if f.ParentID == pid {
f.Args = fields[pid].Args
}
}
} else {
var ok bool
if !p.peek(itemName) {
return nil, fmt.Errorf("expecting a fragment name, got: %s", p.next())
}
@ -426,41 +434,47 @@ func (p *Parser) parseFragmentFields(st *Stack, fields []Field) ([]Field, error)
id := p.h.Sum64()
p.h.Reset()
if fr, ok = p.frags[id]; !ok {
fr, ok := p.frags[id]
if !ok {
return nil, fmt.Errorf("no fragment named '%s' defined", name)
}
}
ff := fr.Fields
n := int32(len(fields))
fields = append(fields, fr.Fields...)
parent := &fields[pid]
for i := 0; i < len(fr.Fields); i++ {
k := (n + int32(i))
f := &fields[k]
f.ID = int32(k)
n := int32(len(fields))
fields = append(fields, ff...)
// If this is the top-level point the parent to the parent of the
// previous field.
if f.ParentID == -1 {
pid := st.Peek()
f.ParentID = pid
if f.ParentID != -1 {
fields[pid].Children = append(fields[f.ParentID].Children, f.ID)
for i := 0; i < len(ff); i++ {
k := (n + int32(i))
f := &fields[k]
f.ID = int32(k)
// If this is the top-level point the parent to the parent of the
// previous field.
if f.ParentID == -1 {
f.ParentID = pid
if f.ParentID != -1 {
parent.Children = append(parent.Children, f.ID)
}
// Update all the other parents id's by our new place in this new array
} else {
f.ParentID += n
}
// Update all the other parents id's by our new place in this new array
} else {
f.ParentID += n
}
f.Children = make([]int32, len(f.Children))
copy(f.Children, fr.Fields[i].Children)
// Copy over children since fields append is not a deep copy
f.Children = make([]int32, len(f.Children))
copy(f.Children, ff[i].Children)
f.Args = make([]Arg, len(f.Args))
copy(f.Args, fr.Fields[i].Args)
// Copy over args since args append is not a deep copy
f.Args = make([]Arg, len(f.Args))
copy(f.Args, ff[i].Args)
// Update all the children which is needed.
for j := range f.Children {
f.Children[j] += n
// Update all the children which is needed.
for j := range f.Children {
f.Children[j] += n
}
}
}

View File

@ -12,6 +12,7 @@ import (
)
type QType int
type SType int
type Action int
const (
@ -19,7 +20,8 @@ const (
)
const (
QTQuery QType = iota + 1
QTUnknown QType = iota
QTQuery
QTMutation
QTInsert
QTUpdate
@ -27,6 +29,12 @@ const (
QTUpsert
)
const (
STNone SType = iota
STUnion
STMember
)
type QCode struct {
Type QType
ActionVar string
@ -38,6 +46,8 @@ type QCode struct {
type Select struct {
ID int32
ParentID int32
UParentID int32
Type SType
Args map[string]*Node
Name string
FieldName string
@ -372,6 +382,10 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
})
s := &selects[(len(selects) - 1)]
if field.Union {
s.Type = STUnion
}
if len(field.Alias) != 0 {
s.FieldName = field.Alias
} else {
@ -383,6 +397,11 @@ func (com *Compiler) compileQuery(qc *QCode, op *Operation, role string) error {
} else {
p := &selects[s.ParentID]
p.Children = append(p.Children, s.ID)
if p.Type == STUnion {
s.Type = STMember
s.UParentID = p.ParentID
}
}
if skipRender {

View File

@ -29,6 +29,8 @@ func al(b byte) bool {
func (qt QType) String() string {
switch qt {
case QTUnknown:
return "unknown"
case QTQuery:
return "query"
case QTMutation: