Use hash's as ids for table relationships

This commit is contained in:
Vikram Rangnekar 2019-05-14 22:32:12 -04:00
parent e1d3bb9055
commit 58408eadc1
7 changed files with 116 additions and 41 deletions

View File

@ -30,7 +30,7 @@ func NewCompiler(conf Config) *Compiler {
return &Compiler{conf.Schema, conf.Vars, conf.TableMap} return &Compiler{conf.Schema, conf.Vars, conf.TableMap}
} }
func (c *Compiler) AddRelationship(key TTKey, val *DBRel) { func (c *Compiler) AddRelationship(key uint64, val *DBRel) {
c.schema.RelMap[key] = val c.schema.RelMap[key] = val
} }
@ -139,9 +139,8 @@ func (v *selectBlock) processChildren() (uint32, []*qcode.Column) {
for _, id := range v.sel.Children { for _, id := range v.sel.Children {
child := &v.qc.Query.Selects[id] child := &v.qc.Query.Selects[id]
k := TTKey{child.Table, v.sel.Table}
rel, ok := v.schema.RelMap[k] rel, ok := v.schema.RelMap[child.RelID]
if !ok { if !ok {
skipped |= (1 << uint(id)) skipped |= (1 << uint(id))
continue continue
@ -290,8 +289,7 @@ func (v *joinClose) render(w io.Writer) error {
} }
func (v *selectBlock) renderJoinTable(w io.Writer) { func (v *selectBlock) renderJoinTable(w io.Writer) {
k := TTKey{v.sel.Table, v.parent.Table} rel, ok := v.schema.RelMap[v.sel.RelID]
rel, ok := v.schema.RelMap[k]
if !ok { if !ok {
panic(errors.New("no relationship found")) panic(errors.New("no relationship found"))
} }
@ -315,14 +313,12 @@ func (v *selectBlock) renderColumns(w io.Writer) {
} }
func (v *selectBlock) renderRemoteRelColumns(w io.Writer) { func (v *selectBlock) renderRemoteRelColumns(w io.Writer) {
k := TTKey{Table2: v.sel.Table}
i := 0 i := 0
for _, id := range v.sel.Children { for _, id := range v.sel.Children {
child := &v.qc.Query.Selects[id] child := &v.qc.Query.Selects[id]
k.Table1 = child.Table
rel, ok := v.schema.RelMap[k] rel, ok := v.schema.RelMap[child.RelID]
if !ok || rel.Type != RelRemote { if !ok || rel.Type != RelRemote {
continue continue
} }
@ -489,8 +485,7 @@ func (v *selectBlock) renderOrderByColumns(w io.Writer) {
} }
func (v *selectBlock) renderRelationship(w io.Writer) { func (v *selectBlock) renderRelationship(w io.Writer) {
k := TTKey{v.sel.Table, v.parent.Table} rel, ok := v.schema.RelMap[v.sel.RelID]
rel, ok := v.schema.RelMap[k]
if !ok { if !ok {
panic(errors.New("no relationship found")) panic(errors.New("no relationship found"))
} }

View File

@ -100,7 +100,7 @@ func TestMain(m *testing.M) {
schema := &DBSchema{ schema := &DBSchema{
Tables: make(map[string]*DBTableInfo), Tables: make(map[string]*DBTableInfo),
RelMap: make(map[TTKey]*DBRel), RelMap: make(map[uint64]*DBRel),
} }
for i, t := range tables { for i, t := range tables {

View File

@ -4,20 +4,13 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/cespare/xxhash/v2"
"github.com/go-pg/pg" "github.com/go-pg/pg"
) )
type TCKey struct {
Table, Column string
}
type TTKey struct {
Table1, Table2 string
}
type DBSchema struct { type DBSchema struct {
Tables map[string]*DBTableInfo Tables map[string]*DBTableInfo
RelMap map[TTKey]*DBRel RelMap map[uint64]*DBRel
} }
type DBTableInfo struct { type DBTableInfo struct {
@ -47,7 +40,7 @@ type DBRel struct {
func NewDBSchema(db *pg.DB) (*DBSchema, error) { func NewDBSchema(db *pg.DB) (*DBSchema, error) {
schema := &DBSchema{ schema := &DBSchema{
Tables: make(map[string]*DBTableInfo), Tables: make(map[string]*DBTableInfo),
RelMap: make(map[TTKey]*DBRel), RelMap: make(map[uint64]*DBRel),
} }
tables, err := GetTables(db) tables, err := GetTables(db)
@ -87,6 +80,8 @@ func (s *DBSchema) updateSchema(t *DBTable, cols []*DBColumn) {
ct := strings.ToLower(t.Name) ct := strings.ToLower(t.Name)
s.Tables[ct] = ti s.Tables[ct] = ti
h := xxhash.New()
for _, c := range cols { for _, c := range cols {
switch { switch {
case c.Type == "tsvector": case c.Type == "tsvector":
@ -110,12 +105,12 @@ func (s *DBSchema) updateSchema(t *DBTable, cols []*DBColumn) {
// Belongs-to relation between current table and the // Belongs-to relation between current table and the
// table in the foreign key // table in the foreign key
rel1 := &DBRel{RelBelongTo, "", "", c.Name, fc.Name} rel1 := &DBRel{RelBelongTo, "", "", c.Name, fc.Name}
s.RelMap[TTKey{ct, ft}] = rel1 s.RelMap[relID(h, ct, ft)] = rel1
// One-to-many relation between the foreign key table and the // One-to-many relation between the foreign key table and the
// the current table // the current table
rel2 := &DBRel{RelOneToMany, "", "", fc.Name, c.Name} rel2 := &DBRel{RelOneToMany, "", "", fc.Name, c.Name}
s.RelMap[TTKey{ft, ct}] = rel2 s.RelMap[relID(h, ft, ct)] = rel2
jcols = append(jcols, c) jcols = append(jcols, c)
} }
@ -131,7 +126,7 @@ func (s *DBSchema) updateSchema(t *DBTable, cols []*DBColumn) {
for i := range jcols { for i := range jcols {
for n := range jcols { for n := range jcols {
if n != i { if n != i {
s.updateSchemaOTMT(ct, jcols[i], jcols[n], colByID) s.updateSchemaOTMT(h, ct, jcols[i], jcols[n], colByID)
} }
} }
} }
@ -139,7 +134,10 @@ func (s *DBSchema) updateSchema(t *DBTable, cols []*DBColumn) {
} }
func (s *DBSchema) updateSchemaOTMT( func (s *DBSchema) updateSchemaOTMT(
ct string, col1, col2 *DBColumn, colByID map[int]*DBColumn) { h *xxhash.Digest,
ct string,
col1, col2 *DBColumn,
colByID map[int]*DBColumn) {
t1 := strings.ToLower(col1.FKeyTable) t1 := strings.ToLower(col1.FKeyTable)
t2 := strings.ToLower(col2.FKeyTable) t2 := strings.ToLower(col2.FKeyTable)
@ -157,13 +155,13 @@ func (s *DBSchema) updateSchemaOTMT(
// 2nd foreign key table // 2nd foreign key table
//rel1 := &DBRel{RelOneToManyThrough, ct, fc1.Name, col1.Name} //rel1 := &DBRel{RelOneToManyThrough, ct, fc1.Name, col1.Name}
rel1 := &DBRel{RelOneToManyThrough, ct, col2.Name, fc2.Name, col1.Name} rel1 := &DBRel{RelOneToManyThrough, ct, col2.Name, fc2.Name, col1.Name}
s.RelMap[TTKey{t1, t2}] = rel1 s.RelMap[relID(h, t1, t2)] = rel1
// One-to-many-through relation between 2nd foreign key table and the // One-to-many-through relation between 2nd foreign key table and the
// 1nd foreign key table // 1nd foreign key table
//rel2 := &DBRel{RelOneToManyThrough, ct, col2.Name, fc2.Name} //rel2 := &DBRel{RelOneToManyThrough, ct, col2.Name, fc2.Name}
rel2 := &DBRel{RelOneToManyThrough, ct, col1.Name, fc1.Name, col2.Name} rel2 := &DBRel{RelOneToManyThrough, ct, col1.Name, fc1.Name, col2.Name}
s.RelMap[TTKey{t2, t1}] = rel2 s.RelMap[relID(h, t2, t1)] = rel2
} }
type DBTable struct { type DBTable struct {
@ -264,3 +262,11 @@ func (s *DBSchema) GetTable(table string) (*DBTableInfo, error) {
} }
return t, nil return t, nil
} }
func relID(h *xxhash.Digest, child, parent string) uint64 {
h.WriteString(child)
h.WriteString(parent)
v := h.Sum64()
h.Reset()
return v
}

View File

@ -73,3 +73,57 @@ func TestEmptyCompile(t *testing.T) {
t.Fatal(errors.New("expecting an error")) t.Fatal(errors.New("expecting an error"))
} }
} }
func BenchmarkQCompile(b *testing.B) {
qcompile, _ := NewCompiler(Config{})
val := `query {
products(
where: {
and: {
not: { id: { is_null: true } },
price: { gt: 10 }
}}) {
id
name
price
}
}`
b.ResetTimer()
b.ReportAllocs()
for n := 0; n < b.N; n++ {
_, err := qcompile.CompileQuery(val)
if err != nil {
b.Fatal(err)
}
}
}
func BenchmarkLex(b *testing.B) {
val := `query {
products(
where: {
and: {
not: { id: { is_null: true } },
price: { gt: 10 }
}}) {
id
name
price
}
}`
b.ResetTimer()
b.ReportAllocs()
for n := 0; n < b.N; n++ {
_, err := lex(val)
if err != nil {
b.Fatal(err)
}
}
}

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/cespare/xxhash/v2"
"github.com/dosco/super-graph/util" "github.com/dosco/super-graph/util"
"github.com/gobuffalo/flect" "github.com/gobuffalo/flect"
) )
@ -30,6 +31,7 @@ type Column struct {
type Select struct { type Select struct {
ID uint16 ID uint16
ParentID uint16 ParentID uint16
RelID uint64
Args map[string]*Node Args map[string]*Node
AsList bool AsList bool
Table string Table string
@ -255,6 +257,7 @@ func (com *Compiler) compileQuery(op *Operation) (*Query, error) {
selects := make([]Select, 0, 5) selects := make([]Select, 0, 5)
st := util.NewStack() st := util.NewStack()
h := xxhash.New()
if len(op.Fields) == 0 { if len(op.Fields) == 0 {
return nil, errors.New("empty query") return nil, errors.New("empty query")
@ -294,6 +297,7 @@ func (com *Compiler) compileQuery(op *Operation) (*Query, error) {
if s.ID != 0 { if s.ID != 0 {
p := &selects[s.ParentID] p := &selects[s.ParentID]
p.Children = append(p.Children, s.ID) p.Children = append(p.Children, s.ID)
s.RelID = relID(h, tn, p.Table)
} }
if fn == tn { if fn == tn {
@ -875,3 +879,11 @@ func buildPath(a []string) string {
} }
return b.String() return b.String()
} }
func relID(h *xxhash.Digest, child, parent string) uint64 {
h.WriteString(child)
h.WriteString(parent)
v := h.Sum64()
h.Reset()
return v
}

View File

@ -34,8 +34,6 @@ type coreContext struct {
func (c *coreContext) handleReq(w io.Writer, req *http.Request) error { func (c *coreContext) handleReq(w io.Writer, req *http.Request) error {
var err error var err error
//cacheEnabled := (conf.EnableTracing == false)
qc, err := qcompile.CompileQuery(c.req.Query) qc, err := qcompile.CompileQuery(c.req.Query)
if err != nil { if err != nil {
return err return err
@ -130,17 +128,12 @@ func (c *coreContext) handleReq(w io.Writer, req *http.Request) error {
return err return err
} }
// if cacheEnabled {
// if err = cache.Set(key, []byte(finalSQL)); err != nil {
// return err
// }
// }
return c.render(w, ob.Bytes()) return c.render(w, ob.Bytes())
} }
func (c *coreContext) resolveSQL(qc *qcode.QCode, vars variables) ( func (c *coreContext) resolveSQL(qc *qcode.QCode, vars variables) (
[]byte, uint32, error) { []byte, uint32, error) {
// var entry []byte // var entry []byte
// var key string // var key string
@ -180,13 +173,23 @@ func (c *coreContext) resolveSQL(qc *qcode.QCode, vars variables) (
return nil, 0, err return nil, 0, err
} }
finalSQL := sqlStmt.String() finalSQL := sqlStmt.Bytes()
if conf.DebugLevel > 0 { if conf.DebugLevel > 0 {
fmt.Println(finalSQL) fmt.Println(finalSQL)
} }
st := time.Now() // if cacheEnabled {
// if err = cache.Set(key, finalSQL); err != nil {
// return err
// }
// }
var st time.Time
if conf.EnableTracing {
st = time.Now()
}
var root json.RawMessage var root json.RawMessage
_, err = db.Query(pg.Scan(&root), finalSQL) _, err = db.Query(pg.Scan(&root), finalSQL)

View File

@ -45,7 +45,12 @@ func initRemotes(t configTable) {
// register a relationship between the remote data // register a relationship between the remote data
// and the database table // and the database table
key := psql.TTKey{strings.ToLower(r.Name), t.Name}
h.WriteString(strings.ToLower(r.Name))
h.WriteString(t.Name)
key := h.Sum64()
h.Reset()
val := &psql.DBRel{ val := &psql.DBRel{
Type: psql.RelRemote, Type: psql.RelRemote,
Col1: idcol, Col1: idcol,