Skip to content

Commit

Permalink
wip - ratios
Browse files Browse the repository at this point in the history
  • Loading branch information
jsimnz committed Aug 3, 2022
1 parent 2ef12bd commit 9569b3b
Show file tree
Hide file tree
Showing 6 changed files with 182 additions and 70 deletions.
14 changes: 12 additions & 2 deletions query/graphql/planner/limit.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
package planner

import (
"math"

"github.com/sourcenetwork/defradb/core"
"github.com/sourcenetwork/defradb/query/graphql/mapper"
parserTypes "github.com/sourcenetwork/defradb/query/graphql/parser/types"
Expand All @@ -34,9 +36,13 @@ func (p *Planner) HardLimit(parsed *mapper.Select, n *mapper.Limit) (*hardLimitN
if n == nil {
return nil, nil // nothing to do
}
limit := int64(math.MaxInt64)
if n.Limit > 0 {
limit = n.Limit
}
return &hardLimitNode{
p: p,
limit: n.Limit,
limit: limit,
offset: n.Offset,
rowIndex: 0,
docMapper: docMapper{&parsed.DocumentMapping},
Expand Down Expand Up @@ -109,9 +115,13 @@ func (p *Planner) RenderLimit(docMap *core.DocumentMapping, n *parserTypes.Limit
if n == nil {
return nil, nil // nothing to do
}
limit := int64(math.MaxInt64)
if n.Limit > 0 {
limit = n.Limit
}
return &renderLimitNode{
p: p,
limit: n.Limit,
limit: limit,
offset: n.Offset,
rowIndex: 0,
docMapper: docMapper{docMap},
Expand Down
41 changes: 15 additions & 26 deletions tests/bench/fixtures/dependancy.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,47 +3,46 @@ package fixtures
import (
"fmt"
"reflect"
"strings"
)

// return dependants of a type
//
// one-to-one: A->B | A (primary) depends on B (secondary)
// one-to-many: A->[]B | A (one) depends on []B (many)
func dependants(val reflect.Type) ([]string, error) {
deps := make([]string, 0)
func dependants(val reflect.Type) ([]*Node, error) {
deps := make([]*Node, 0)

for i := 0; i < val.NumField(); i++ {
f := val.Field(i)
if !isRelationDependantField(f) {
if !isRelationDependantField(f.Type) {
continue
}

// get fixture tag
// if its empty, skip
tag := f.Tag.Get(fixtureTag)
if tag == "" {
return []string{}, fmt.Errorf("field %s is missing 'fixture' tag", f.Name)
tg, err := parseTag(tag)
if err != nil {
return nil, err
}

tags := strings.Split(tag, ",")
switch tags[0] {
case string(oneToOne):
switch tg.rel {
case oneToOne:
// the second entry in the tags comma seperated list
// has to be primary or empty
if len(tags) < 2 || tags[1] != "primary" {
if !tg.isPrimary {
// secondary, no dependancy
// next field
continue
}

// primary, dependancy
deps = append(deps, f.Type.Elem().Name())
case string(oneToMany):
deps = append(deps, f.Type.Elem().Name())
deps = append(deps, NewNode(f.Type.Elem().Name(), f.Type.Elem()))
case oneToMany:
deps = append(deps, NewNode(f.Type.Elem().Name(), f.Type.Elem()))

default:
return []string{}, fmt.Errorf("invalid 'fixture' tag format, missing relation type: %s", tag)
return nil, fmt.Errorf("invalid 'fixture' tag format, missing relation type: %s", tag)
}
}

Expand All @@ -57,18 +56,8 @@ func dependants(val reflect.Type) ([]string, error) {
// 1) It is a pointer to a struct
// or
// 2) It is a slice of struct pointers
func isRelationDependantField(field reflect.StructField) bool {
t := field.Type
for {
switch t.Kind() {
case reflect.Struct:
return true
case reflect.Pointer:
t = t.Elem()
default:
return false
}
}
func isRelationDependantField(field reflect.Type) bool {
return isStructPointer(field) || isSliceStructPointer(field)
}

func isStructPointer(t reflect.Type) bool {
Expand Down
16 changes: 9 additions & 7 deletions tests/bench/fixtures/dependancy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,18 +19,18 @@ type tUser struct {
type tBook struct {
Name string `faker:"title"`
Rating float32 `faker:"amount"`
Author *tAuthor `fixture:"one-to-one"`
Publisher *tPublisher `fixture:"one-to-many"`
PublisherId ID
Author *tAuthor `fixture:"one-to-one" faker:"-"`
Publisher *tPublisher `fixture:"one-to-many" faker:"-"`
PublisherId ID `faker:"-"`
}

// #3
type tAuthor struct {
Name string `faker:"name"`
Age int
Verified bool
Wrote *tBook `fixture:"one-to-one,primary,0.8"`
WorteId ID
Wrote *tBook `fixture:"one-to-one,primary,0.8" faker:"-"`
WorteId ID `faker:"-"`
}

// #1
Expand All @@ -43,7 +43,7 @@ type tPublisher struct {
// Rate: 50%
// Min:1
// Max:10
Published []*tBook `fixture:"one-to-many,0.5,1,10"`
Published []*tBook `fixture:"one-to-many,0.5,1,10" faker:"-"`
}

func TestDependantsOneToOneSecondaryWithOneToManyPrimary(t *testing.T) {
Expand Down Expand Up @@ -87,12 +87,14 @@ func TestDependantsGraph(t *testing.T) {
require.NoError(t, err)

name := typ.Name()
basicGraph = append(basicGraph, NewNode(name, deps...))
basicGraph = append(basicGraph, NewNode(name, v, deps...))
}

resolvedGraph, err := resolveGraph(basicGraph)
require.NoError(t, err)

require.Equal(t, []string{"tPublisher", "tBook", "tAuthor"}, resolvedGraph.toStrings())

fmt.Println("--- ORDER ---")
for _, n := range resolvedGraph {
fmt.Println(n.name)
Expand Down
105 changes: 101 additions & 4 deletions tests/bench/fixtures/fixtures.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,80 @@ type Generator struct {

schema string
types []interface{}
tags []tag

// an array of ratios.
ratios []float64
}

func ForSchema(ctx context.Context, schemaName string) Generator {
return Generator{
func ForSchema(ctx context.Context, schemaName string) (Generator, error) {
types := registeredFixtures[schemaName]
g := Generator{
ctx: ctx,
schema: schemaName,
types: registeredFixtures[schemaName],
}

// dependancy graph for related types
var depGraph Graph
for _, t := range types {
v := reflect.TypeOf(t)
deps, err := dependants(v)
if err != nil {
return g, fmt.Errorf("couldn't get dependants for %s: %w", v.Name(), err)
}
depGraph = append(depGraph, NewNode(v.Name(), t, deps...))
}

var err error
depGraph, err = resolveGraph(depGraph)
if err != nil {
return g, fmt.Errorf("dependancy graph resolution: %w", err)
}

// reorder types array based on dependancy graph
depTypes := make([]interface{}, len(types))
for i, t := range types {
for _, n := range depGraph {
typeName := reflect.TypeOf(t).Name()
if typeName == n.name {
depTypes[i] = t
break
}
}
}

// compute ratios
for i, t := range depTypes {
typeOf := reflect.TypeOf(t)
for j := 0; j < typeOf.NumField(); j++ {
f := typeOf.Field(j)

// if we have a related type, we need to
if isStructPointer(f.Type) || isSliceStructPointer(f.Type) {
fixtureTagStr, ok := f.Tag.Lookup(fixtureTag)
if !ok {
return g, fmt.Errorf("missing fixture tag for field %d", j)
}

tg, err := parseTag(fixtureTagStr)
if err != nil {
return g, err
}
g.tags[i] = tg

// start with average. Todo: min/max
// r, err := tg.avgRatio()
// if err != nil {
// return g, fmt.Errorf("average ratio for %s: %w", f.Type.Name(), err)
// }

}
}
}

g.types = depTypes

return g, nil
}

// Types returns the defined types for this fixture set
Expand All @@ -85,6 +151,21 @@ func (g Generator) TypeName(index int) string {
return reflect.TypeOf(g.types[index]).Name()
}

// HasRelatedTypes returns if any of the types defined
// in the generator are related to one another
func (g Generator) HasRelatedTypes() bool {
for _, t := range g.types {
typeOf := reflect.TypeOf(t)
for i := 0; i < typeOf.NumField(); i++ {
f := typeOf.Field(i)
if isStructPointer(f.Type) || isSliceStructPointer(f.Type) {
return true
}
}
}
return false
}

// GenerateFixtureDocs uses the faker fixture system to
// randomly generate a new set of documents matching the defined
// struct types within the context.
Expand Down Expand Up @@ -223,7 +304,10 @@ func parseTag(t string) (tag, error) {
return tag{}, fmt.Errorf("empty fixture tag")
}

tg := tag{}
tg := tag{
minObjects: 1, // default is 1 not 0
maxObjects: 1, // default is 1 not 0
}
parts := strings.Split(t, ",")
remainderIndex := 1
switch parts[0] {
Expand Down Expand Up @@ -279,3 +363,16 @@ func parseTag(t string) (tag, error) {

return tg, nil
}

func (t tag) avgRatio() (ratio, error) {
avg := float64(t.maxObjects+t.minObjects) / float64(2)
return newRatio(1, avg/t.fillRate)
}

func (t tag) minRatio() (ratio, error) {
return newRatio(1, float64(t.minObjects)/t.fillRate)
}

func (t tag) maxRatio() (ratio, error) {
return newRatio(1, float64(t.maxObjects)/t.fillRate)
}
32 changes: 23 additions & 9 deletions tests/bench/fixtures/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ package fixtures

import (
"fmt"
"strings"

mapset "github.com/deckarep/golang-set"
)
Expand All @@ -37,14 +38,19 @@ type Node struct {
// Name of the node
name string

typ interface{}

// Dependencies of the node
deps []string
deps []*Node

tag tag
}

// NewNode creates a new node
func NewNode(name string, deps ...string) *Node {
func NewNode(name string, typ interface{}, deps ...*Node) *Node {
n := &Node{
name: name,
typ: typ,
deps: deps,
}

Expand All @@ -67,7 +73,7 @@ func resolveGraph(graph Graph) (Graph, error) {

dependencySet := mapset.NewSet()
for _, dep := range node.deps {
dependencySet.Add(dep)
dependencySet.Add(dep.name)
}
nodeDependencies[node.name] = dependencySet
}
Expand Down Expand Up @@ -112,19 +118,27 @@ func resolveGraph(graph Graph) (Graph, error) {
return resolved, nil
}

// func (g Graph) String() string {
// return strings.Join(g.toStrings(), ",")
// }
// func (g Graph) index(name string) bool {

// func (g Graph) toStrings() []string {
// for _, node := range
// }

func (g Graph) String() string {
return strings.Join(g.toStrings(), ",")
}

func (g Graph) toStrings() []string {
s := make([]string, len(g))
for i, node := range g {
s[i] = node.name
}
return s
}

// Displays the dependency graph
func displayGraph(graph Graph) {
for _, node := range graph {
for _, dep := range node.deps {
fmt.Printf("%s -> %s\n", node.name, dep)
fmt.Printf("%s -> %s\n", node.name, dep.name)
}
}
}
Loading

0 comments on commit 9569b3b

Please sign in to comment.