From d0c88af4b14aaf1ba2144b220efadabfa484f252 Mon Sep 17 00:00:00 2001 From: Jason Cruz Date: Wed, 19 Apr 2023 08:21:49 -0700 Subject: [PATCH 1/8] replace return type of GetResults to ResultPath slice --- ast.go | 37 +++++++++++++++---------- eval.go | 25 ++++++++++------- lang.go | 8 +++--- patterneval.go | 67 ++++++++++++++++++++++++++------------------- update.go | 73 +++++++++++++++++++++++++------------------------- 5 files changed, 119 insertions(+), 91 deletions(-) diff --git a/ast.go b/ast.go index 0da0b8a..78d8269 100644 --- a/ast.go +++ b/ast.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/antlr/antlr4/runtime/Go/antlr" + "github.com/cloudprivacylabs/lpg/v2" "github.com/cloudprivacylabs/opencypher/parser" ) @@ -12,6 +13,11 @@ type Evaluatable interface { Evaluate(*EvalContext) (Value, error) } +type ResultPath struct { + Result *lpg.Path + Symbols map[string]Value +} + type regularQuery struct { singleQuery Evaluatable unions []union @@ -81,11 +87,11 @@ type multiPartQuery struct { } type ReadingClause interface { - GetResults(*EvalContext) (ResultSet, error) + GetResults(*EvalContext) ([]ResultPath, error) } type UpdatingClause interface { - Update(*EvalContext, ResultSet) (Value, error) + Update(*EvalContext, []ResultPath) (Value, error) TopLevelUpdate(*EvalContext) (Value, error) } @@ -445,8 +451,9 @@ func oC_SinglePartQuery(ctx *parser.OC_SinglePartQueryContext) singlePartQuery { return ret } -//oC_MultiPartQuery -// : ( ( oC_ReadingClause SP? )* ( oC_UpdatingClause SP? )* oC_With SP? )+ oC_SinglePartQuery ; +// oC_MultiPartQuery +// +// : ( ( oC_ReadingClause SP? )* ( oC_UpdatingClause SP? )* oC_With SP? )+ oC_SinglePartQuery ; func oC_MultiPartQuery(ctx *parser.OC_MultiPartQueryContext) multiPartQuery { ret := multiPartQuery{parts: []multiPartQueryPart{}} count := ctx.GetChildCount() @@ -663,11 +670,11 @@ func oC_ComparisonExpression(ctx *parser.OC_ComparisonExpressionContext) Express } // oC_AddOrSubtractExpression : -// oC_MultiplyDivideModuloExpression ( -// ( SP? '+' SP? oC_MultiplyDivideModuloExpression ) | -// ( SP? '-' SP? oC_MultiplyDivideModuloExpression ) -// )* // +// oC_MultiplyDivideModuloExpression ( +// ( SP? '+' SP? oC_MultiplyDivideModuloExpression ) | +// ( SP? '-' SP? oC_MultiplyDivideModuloExpression ) +// )* func oC_AddOrSubtractExpression(ctx *parser.OC_AddOrSubtractExpressionContext) Expression { ret := &addOrSubtractExpression{} target := &ret.add @@ -695,10 +702,11 @@ func oC_AddOrSubtractExpression(ctx *parser.OC_AddOrSubtractExpressionContext) E } // oC_MultiplyDivideModuloExpression : -// oC_PowerOfExpression ( -// ( SP? '*' SP? oC_PowerOfExpression ) | -// ( SP? '/' SP? oC_PowerOfExpression ) | -// ( SP? '%' SP? oC_PowerOfExpression ) )* ; +// +// oC_PowerOfExpression ( +// ( SP? '*' SP? oC_PowerOfExpression ) | +// ( SP? '/' SP? oC_PowerOfExpression ) | +// ( SP? '%' SP? oC_PowerOfExpression ) )* ; func oC_MultiplyDivideModuloExpression(ctx *parser.OC_MultiplyDivideModuloExpressionContext) Expression { ret := &multiplyDivideModuloExpression{} count := ctx.GetChildCount() @@ -728,7 +736,8 @@ func oC_MultiplyDivideModuloExpression(ctx *parser.OC_MultiplyDivideModuloExpres } // oC_PowerOfExpression : -// oC_UnaryAddOrSubtractExpression ( SP? '^' SP? oC_UnaryAddOrSubtractExpression )* ; +// +// oC_UnaryAddOrSubtractExpression ( SP? '^' SP? oC_UnaryAddOrSubtractExpression )* ; func oC_PowerOfExpression(ctx *parser.OC_PowerOfExpressionContext) Evaluatable { ret := powerOfExpression{} for _, x := range ctx.AllOC_UnaryAddOrSubtractExpression() { @@ -1170,7 +1179,7 @@ func oC_FilterExpression(ctx *parser.OC_FilterExpressionContext) filterExpressio return ret } -//oC_RelationshipsPattern : oC_NodePattern ( SP? oC_PatternElementChain )+ ; +// oC_RelationshipsPattern : oC_NodePattern ( SP? oC_PatternElementChain )+ ; // oC_PatternElementChain : oC_RelationshipPattern SP? oC_NodePattern ; func oC_RelationshipsPattern(ctx *parser.OC_RelationshipsPatternContext) relationshipsPattern { ret := relationshipsPattern{ diff --git a/eval.go b/eval.go index b13ff57..5a283a1 100644 --- a/eval.go +++ b/eval.go @@ -456,14 +456,17 @@ func (query singlePartQuery) Evaluate(ctx *EvalContext) (Value, error) { } return nil } - results := *NewResultSet() + results := make([]ResultPath, 0) + var rs ResultSet if len(query.read) > 0 { - for _, r := range query.read { - rs, err := r.GetResults(ctx) + for i, r := range query.read { + rp, err := r.GetResults(ctx) if err != nil { return nil, err } - results.Add(rs) + if len(rp) != 0 { + rs.Rows = append(rs.Rows, rp[i].Symbols) + } } for _, upd := range query.update { @@ -471,12 +474,14 @@ func (query singlePartQuery) Evaluate(ctx *EvalContext) (Value, error) { if err != nil { return nil, err } - results = v.Get().(ResultSet) + results = v.Get().([]ResultPath) } if query.ret == nil { return RValue{Value: *NewResultSet()}, nil } - err := project(results.Rows) + + // rs = ResultPathToResultSet(results) + err := project(rs.Rows) if err != nil { return nil, err } @@ -489,15 +494,15 @@ func (query singlePartQuery) Evaluate(ctx *EvalContext) (Value, error) { return nil, err } if v != nil && v.Get() != nil { - results = v.Get().(ResultSet) + results = v.Get().([]ResultPath) } } if query.ret == nil { return RValue{Value: *NewResultSet()}, nil } - if len(results.Rows) > 0 { - for _, row := range results.Rows { + if len(rs.Rows) > 0 { + for _, row := range rs.Rows { val, err := query.ret.projection.items.Project(ctx, row) if err != nil { return nil, err @@ -593,7 +598,7 @@ func (pe propertyExpression) Evaluate(ctx *EvalContext) (Value, error) { return val, nil } -func (unwind unwind) GetResults(ctx *EvalContext) (ResultSet, error) { panic("Unimplemented") } +func (unwind unwind) GetResults(ctx *EvalContext) ([]ResultPath, error) { panic("Unimplemented") } func (ls listComprehension) Evaluate(ctx *EvalContext) (Value, error) { panic("Unimplemented") } func (p patternComprehension) Evaluate(ctx *EvalContext) (Value, error) { panic("Unimplemented") } func (flt filterAtom) Evaluate(ctx *EvalContext) (Value, error) { panic("Unimplemented") } diff --git a/lang.go b/lang.go index e4a400b..2957680 100644 --- a/lang.go +++ b/lang.go @@ -100,15 +100,15 @@ func (p PatternPart) FindRelative(this *lpg.Node) ([]*lpg.Node, error) { resultAccumulator := matchResultAccumulator{ evalCtx: ctx, - result: NewResultSet(), + result: []ResultPath{}, } err = pattern.Run(ctx.graph, symbols, &resultAccumulator) if err != nil { return nil, err } - - ret := make([]*lpg.Node, 0, len(resultAccumulator.result.Rows)) - for _, row := range resultAccumulator.result.Rows { + rs := ResultPathToResultSet(resultAccumulator.result) + ret := make([]*lpg.Node, 0, len(rs.Rows)) + for _, row := range rs.Rows { t, ok := row["target"] if !ok { continue diff --git a/patterneval.go b/patterneval.go index 356f4d9..966cb36 100644 --- a/patterneval.go +++ b/patterneval.go @@ -12,6 +12,14 @@ func (e ErrInvalidValueReferenceInPattern) Error() string { return "Invalid value reference in pattern: " + e.Symbol } +func ResultPathToResultSet(resultPath []ResultPath) ResultSet { + rs := ResultSet{} + for _, rp := range resultPath { + rs.Rows = append(rs.Rows, rp.Symbols) + } + return rs +} + func (properties Properties) AsLiteral(ctx *EvalContext) ([]mapKeyValue, error) { if properties.Param != nil { param, err := ctx.GetParameter(string(*properties.Param)) @@ -36,7 +44,7 @@ func (properties Properties) AsLiteral(ctx *EvalContext) ([]mapKeyValue, error) type matchResultAccumulator struct { evalCtx *EvalContext - result *ResultSet + result []ResultPath err error } @@ -45,38 +53,36 @@ func (acc *matchResultAccumulator) StoreResult(ctx *lpg.MatchContext, path *lpg. return } // Record results in the context - result := make(map[string]Value) + acc.result = append(acc.result, ResultPath{Result: path, Symbols: make(map[string]Value)}) for k, v := range symbols { - result[k] = RValue{Value: v} - acc.evalCtx.SetVar(k, RValue{Value: v}) + acc.result[len(acc.result)-1].Symbols[k] = ValueOf(v) } - acc.result.Append(result) } -func (match Match) GetResults(ctx *EvalContext) (ResultSet, error) { +func (match Match) GetResults(ctx *EvalContext) ([]ResultPath, error) { patterns := make([]lpg.Pattern, 0, len(match.Pattern.Parts)) for i := range match.Pattern.Parts { p, err := match.Pattern.Parts[i].getPattern(ctx) if err != nil { - return *NewResultSet(), err + return []ResultPath{}, err } patterns = append(patterns, p) } var nextPattern func(*EvalContext, []lpg.Pattern, int) error - results := NewResultSet() - currentRow := make([]map[string]Value, len(patterns)) + returnResults := []ResultPath{} + // currentRow := make([]map[string]Value, len(patterns)) - addRow := func() { - newRow := make(map[string]Value) - for _, x := range currentRow { - for k, v := range x { - newRow[k] = v - } - } - results.Rows = append(results.Rows, newRow) - } + // addRow := func() { + // newRow := make(map[string]Value) + // for _, x := range currentRow { + // for k, v := range x { + // newRow[k] = v + // } + // } + // results.Rows = append(results.Rows, newRow) + // } nextPattern = func(prevContext *EvalContext, pat []lpg.Pattern, index int) error { newContext := prevContext.SubContext() @@ -86,17 +92,17 @@ func (match Match) GetResults(ctx *EvalContext) (ResultSet, error) { } results := matchResultAccumulator{ evalCtx: newContext, - result: NewResultSet(), + result: []ResultPath{}, } err = pat[0].Run(newContext.graph, symbols, &results) if err != nil { return err } - for _, row := range results.result.Rows { - for k, v := range row { - newContext.SetVar(k, v) + for _, row := range results.result { + for k, v := range row.Symbols { + newContext.SetVar(k, RValue{Value: v}) } - currentRow[index] = row + // currentRow[index] = row if len(pat) > 1 { if err := nextPattern(newContext, pat[1:], index+1); err != nil { return err @@ -108,19 +114,20 @@ func (match Match) GetResults(ctx *EvalContext) (ResultSet, error) { return err } if b, _ := ValueAsBool(rs); b { - addRow() + // addRow() + returnResults = append(returnResults, row) } } else { - addRow() + returnResults = append(returnResults, row) } } } return nil } if err := nextPattern(ctx, patterns, 0); err != nil { - return ResultSet{}, err + return []ResultPath{}, err } - return *results, nil + return returnResults, nil } // BuildPatternSymbols copies all the symbols referenced in the @@ -145,7 +152,13 @@ func BuildPatternSymbols(ctx *EvalContext, pattern lpg.Pattern) (map[string]*lpg ps.AddNode(val) case *lpg.Path: ps.AddPath(val) + case RValue: + switch rv := val.Value.(type) { + case *lpg.Node: + ps.AddNode(rv) + } default: + // v RValue{Value: *lpg.Node} return nil, ErrInvalidValueReferenceInPattern{Symbol: symbol} } symbols[symbol] = ps diff --git a/update.go b/update.go index 52b5824..2948243 100644 --- a/update.go +++ b/update.go @@ -6,11 +6,11 @@ import ( "github.com/cloudprivacylabs/lpg/v2" ) -func (s *set) Update(ctx *EvalContext, result ResultSet) (Value, error) { +func (s *set) Update(ctx *EvalContext, result []ResultPath) (Value, error) { // Work on the cartesian product of result columns var err error subctx := ctx.SubContext() - result.CartesianProduct(func(data map[string]Value) bool { + ResultPathToResultSet(result).CartesianProduct(func(data map[string]Value) bool { subctx.SetVars(data) for i := range s.items { if err = s.items[i].update(subctx, data, result); err != nil { @@ -29,7 +29,7 @@ func (s set) TopLevelUpdate(ctx *EvalContext) (Value, error) { return nil, fmt.Errorf("Cannot use SET at top level") } -func (s *setItem) update(ctx *EvalContext, data map[string]Value, result ResultSet) (err error) { +func (s *setItem) update(ctx *EvalContext, data map[string]Value, result []ResultPath) (err error) { var exprResult Value if s.expression != nil { @@ -130,9 +130,9 @@ func (deleteClause) TopLevelUpdate(ctx *EvalContext) (Value, error) { return nil, fmt.Errorf("Cannot use DELETE at top level") } -func (d deleteClause) Update(ctx *EvalContext, result ResultSet) (Value, error) { +func (d deleteClause) Update(ctx *EvalContext, result []ResultPath) (Value, error) { subctx := ctx.SubContext() - for _, row := range result.Rows { + for _, row := range ResultPathToResultSet(result).Rows { subctx.SetVars(row) for _, expr := range d.exprs { v, err := expr.Evaluate(subctx) @@ -166,9 +166,9 @@ func (remove) TopLevelUpdate(ctx *EvalContext) (Value, error) { return nil, fmt.Errorf("Cannot use REMOVE at top level") } -func (r remove) Update(ctx *EvalContext, result ResultSet) (Value, error) { +func (r remove) Update(ctx *EvalContext, result []ResultPath) (Value, error) { subctx := ctx.SubContext() - for _, row := range result.Rows { + for _, row := range ResultPathToResultSet(result).Rows { subctx.SetVars(row) for _, item := range r.items { if item.property != nil { @@ -213,8 +213,8 @@ func (c create) TopLevelUpdate(ctx *EvalContext) (Value, error) { return nil, nil } -func (c create) Update(ctx *EvalContext, result ResultSet) (Value, error) { - for _, row := range result.Rows { +func (c create) Update(ctx *EvalContext, result []ResultPath) (Value, error) { + for _, row := range ResultPathToResultSet(result).Rows { ctx.SetVars(row) if _, err := c.TopLevelUpdate(ctx); err != nil { return nil, err @@ -343,10 +343,10 @@ func (rel relationshipPattern) Create(ctx *EvalContext, from, to *lpg.Node) (lpg return pathElement, nil } -func (m merge) getResults(ctx *EvalContext) (map[string]struct{}, ResultSet, error) { +func (m merge) getResults(ctx *EvalContext) (map[string]struct{}, []ResultPath, error) { pattern, err := m.pattern.getPattern(ctx) if err != nil { - return nil, *NewResultSet(), err + return nil, []ResultPath{}, err } unbound := make(map[string]struct{}) @@ -362,24 +362,25 @@ func (m merge) getResults(ctx *EvalContext) (map[string]struct{}, ResultSet, err results := matchResultAccumulator{ evalCtx: ctx, - result: NewResultSet(), + result: []ResultPath{}, } symbols, err := BuildPatternSymbols(ctx, pattern) if err != nil { - return nil, *NewResultSet(), err + return nil, []ResultPath{}, err } err = pattern.Run(ctx.graph, symbols, &results) if err != nil { - return nil, *NewResultSet(), err + return nil, []ResultPath{}, err } - return unbound, *results.result, nil + return unbound, results.result, nil } -func (m merge) resultsToCtx(ctx *EvalContext, results ResultSet) { - if len(results.Rows) > 0 { - for k := range results.Rows[0] { +func (m merge) resultsToCtx(ctx *EvalContext, result []ResultPath) { + rs := ResultPathToResultSet(result) + if len(rs.Rows) > 0 { + for k := range rs.Rows[0] { if IsNamedVar(k) { - col := results.Column(k) + col := rs.Column(k) if len(col) == 1 { ctx.SetVar(k, col[0]) } else { @@ -390,12 +391,13 @@ func (m merge) resultsToCtx(ctx *EvalContext, results ResultSet) { } } -func (m merge) doMerge(ctx *EvalContext) (created bool, matched bool, result ResultSet, err error) { +func (m merge) doMerge(ctx *EvalContext) (created bool, matched bool, result []ResultPath, err error) { _, result, err = m.getResults(ctx) if err != nil { return } - if len(result.Rows) == 0 { + rs := ResultPathToResultSet(result) + if len(rs.Rows) == 0 { // Nothing found subctx := ctx.SubContext() _, _, err = m.pattern.Create(subctx) @@ -407,8 +409,7 @@ func (m merge) doMerge(ctx *EvalContext) (created bool, matched bool, result Res for k, v := range vars { row[k] = v } - result = *NewResultSet() - result.Append(row) + rs.Append(row) created = true return } @@ -417,7 +418,7 @@ func (m merge) doMerge(ctx *EvalContext) (created bool, matched bool, result Res return } -func (m merge) processActions(ctx *EvalContext, created, matched bool, rs ResultSet) error { +func (m merge) processActions(ctx *EvalContext, created, matched bool, rs []ResultPath) error { for _, action := range m.actions { if (created && action.on == mergeActionOnCreate) || (matched && action.on == mergeActionOnMatch) { @@ -430,20 +431,20 @@ func (m merge) processActions(ctx *EvalContext, created, matched bool, rs Result return nil } -func (m merge) Update(ctx *EvalContext, rs ResultSet) (Value, error) { - results := *NewResultSet() +func (m merge) Update(ctx *EvalContext, result []ResultPath) (Value, error) { + rs := ResultPathToResultSet(result) for _, row := range rs.Rows { subctx := ctx.SubContext() subctx.SetVars(row) - created, matched, rs, err := m.doMerge(subctx) + created, matched, rp, err := m.doMerge(subctx) if err != nil { return nil, err } - if err := m.processActions(subctx, created, matched, rs); err != nil { + if err := m.processActions(subctx, created, matched, rp); err != nil { return nil, err } if len(rs.Rows) == 0 { - results.Append(row) + rs.Append(row) } else { for _, r := range rs.Rows { newRow := make(map[string]Value) @@ -453,28 +454,28 @@ func (m merge) Update(ctx *EvalContext, rs ResultSet) (Value, error) { for k, v := range r { newRow[k] = v } - results.Append(newRow) + rs.Append(newRow) } } } - return RValue{Value: results}, nil + return RValue{Value: rs}, nil } func (m merge) TopLevelUpdate(ctx *EvalContext) (Value, error) { - created, matched, rs, err := m.doMerge(ctx) + created, matched, rp, err := m.doMerge(ctx) if err != nil { return nil, err } - if err := m.processActions(ctx, created, matched, rs); err != nil { + if err := m.processActions(ctx, created, matched, rp); err != nil { return nil, err } - results := *NewResultSet() + rs := ResultPathToResultSet(rp) for _, r := range rs.Rows { newRow := make(map[string]Value) for k, v := range r { newRow[k] = v } - results.Append(newRow) + rs.Append(newRow) } - return RValue{Value: results}, nil + return RValue{Value: rs}, nil } From 049c3dc400a6230ba27ff37a9be1461c67eb1eb2 Mon Sep 17 00:00:00 2001 From: Jason Cruz Date: Fri, 21 Apr 2023 11:57:00 -0700 Subject: [PATCH 2/8] add cartesian product for ResultPath --- cartesian.go | 25 +++++++++++++++++ cartesian_test.go | 71 +++++++++++++++++++++++++++++++++++++++++++++++ testdata/g1.json | 60 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 cartesian.go create mode 100644 cartesian_test.go create mode 100644 testdata/g1.json diff --git a/cartesian.go b/cartesian.go new file mode 100644 index 0000000..91a65bc --- /dev/null +++ b/cartesian.go @@ -0,0 +1,25 @@ +package opencypher + +// CartesianProuductPaths builds the product of all the resultpaths +func CartesianProductPaths(paths [][]ResultPath, filter func() []ResultPath) [][]ResultPath { + // nextIndex retrieves the next index for the next set + nextIndex := func(ix []int, lengthsFunc func(i int) int) { + for j := len(ix) - 1; j >= 0; j-- { + ix[j]++ + if j == 0 || ix[j] < lengthsFunc(j) { + return + } + ix[j] = 0 + } + } + lengths := func(i int) int { return len(paths[i]) } + product := make([][]ResultPath, 0) + for ix := make([]int, len(paths)); ix[0] < lengths(0); nextIndex(ix, lengths) { + var r []ResultPath + for j, k := range ix { + r = append(r, paths[j][k]) + } + product = append(product, r) + } + return product +} diff --git a/cartesian_test.go b/cartesian_test.go new file mode 100644 index 0000000..5a5b168 --- /dev/null +++ b/cartesian_test.go @@ -0,0 +1,71 @@ +package opencypher + +import ( + "encoding/json" + "fmt" + "os" + "testing" + + "github.com/cloudprivacylabs/lpg/v2" +) + +func TestCartesianProductPaths(t *testing.T) { + f, err := os.Open("testdata/g1.json") + if err != nil { + t.Error(err) + return + } + target := lpg.NewGraph() + err = lpg.JSON{}.Decode(target, json.NewDecoder(f)) + if err != nil { + t.Error(err) + return + } + path := &lpg.Path{} + pe := make([]lpg.PathElement, 0) + for itr := target.GetEdges(); itr.Next(); { + // path.Append(lpg.PathElement{ + // Edge: itr.Edge(), + // }) + pe = append(pe, lpg.PathElement{ + Edge: itr.Edge(), + }) + } + path.Append(pe...) + { + i := [][]ResultPath{ + { + ResultPath{ + Result: path, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + }, + } + // j := [][]ResultPath{3, 2, 1} + // k := [][]ResultPath{2, 1, 3} + + prod := CartesianProductPaths(i, func() []ResultPath { return []ResultPath{} }) + n := len(prod) + e := len(i) + fmt.Println(prod) + if n != e { + t.Fatalf("there should be %d but there were %d groups\n", e, n) + } + + } +} diff --git a/testdata/g1.json b/testdata/g1.json new file mode 100644 index 0000000..9a6f88f --- /dev/null +++ b/testdata/g1.json @@ -0,0 +1,60 @@ +{ + "nodes": [ + { + "n": 0, + "properties": { + "id": "0" + }, + "edges": [ + { "to": 1, "label": "a" } + ] + }, + { + "n": 1, + "properties": { + "id": "1" + }, + "edges": [ + { "to": 2, "label": "b" }, + { "to": 3, "label": "c" } + ] + }, + { + "n": 2, + "properties": { + "id": "2" + }, + "edges": [ + { "to":0, "label": "d"} + ] + }, + { + "n": 3, + "properties": { + "id": "3" + }, + "edges": [ + { "to": 4, "label": "d"}, + { "to": 2, "label": "f"} + ] + }, + { + "n": 4, + "properties": { + "id": "4" + }, + "edges": [ + { "to": 5, "label":"g"} + ] + }, + { + "n": 5, + "properties": { + "id": "5" + }, + "edges": [ + { "to": 3, "label": "h" } + ] + } + ] +} From 150a98a267b78fb9e05a1c8ee45efd012ee7fbe8 Mon Sep 17 00:00:00 2001 From: Jason Cruz Date: Tue, 25 Apr 2023 08:33:46 -0700 Subject: [PATCH 3/8] add cartesian product for ResultPath --- cartesian.go | 38 ++++++++++++++++++++++++++++++-------- cartesian_test.go | 2 +- patterneval.go | 18 +++++------------- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/cartesian.go b/cartesian.go index 91a65bc..c06c8a9 100644 --- a/cartesian.go +++ b/cartesian.go @@ -1,25 +1,47 @@ package opencypher // CartesianProuductPaths builds the product of all the resultpaths -func CartesianProductPaths(paths [][]ResultPath, filter func() []ResultPath) [][]ResultPath { +func CartesianProductPaths(paths [][]ResultPath, filter func(rp []ResultPath) bool) [][]ResultPath { // nextIndex retrieves the next index for the next set - nextIndex := func(ix []int, lengthsFunc func(i int) int) { + nextIndex := func(ix []int) { for j := len(ix) - 1; j >= 0; j-- { ix[j]++ - if j == 0 || ix[j] < lengthsFunc(j) { + if j == 0 || ix[j] < len(paths[j]) { return } ix[j] = 0 } } - lengths := func(i int) int { return len(paths[i]) } - product := make([][]ResultPath, 0) - for ix := make([]int, len(paths)); ix[0] < lengths(0); nextIndex(ix, lengths) { - var r []ResultPath + if len(paths) == 1 { + return paths + } + product := make([][]ResultPath, 0, len(paths)) + for ix := make([]int, len(paths)); ix[0] < len(paths[0]); nextIndex(ix) { + r := make([]ResultPath, 0, len(ix)) for j, k := range ix { r = append(r, paths[j][k]) } - product = append(product, r) + if filter(r) { + product = append(product, r) + } } return product } + +func AllPathsToResultSets(paths [][]ResultPath) []ResultSet { + res := make([]ResultSet, len(paths)) + for i, path := range paths { + for j := range path { + res[i].Rows[i] = paths[i][j].Symbols + } + } + return res +} + +func ResultPathToResultSet(resultPath []ResultPath) ResultSet { + rs := ResultSet{Rows: make([]map[string]Value, 0, len(resultPath))} + for _, rp := range resultPath { + rs.Rows = append(rs.Rows, rp.Symbols) + } + return rs +} diff --git a/cartesian_test.go b/cartesian_test.go index 5a5b168..e69d1b0 100644 --- a/cartesian_test.go +++ b/cartesian_test.go @@ -59,7 +59,7 @@ func TestCartesianProductPaths(t *testing.T) { // j := [][]ResultPath{3, 2, 1} // k := [][]ResultPath{2, 1, 3} - prod := CartesianProductPaths(i, func() []ResultPath { return []ResultPath{} }) + prod := CartesianProductPaths(i, func(_ []ResultPath) bool { return true }) n := len(prod) e := len(i) fmt.Println(prod) diff --git a/patterneval.go b/patterneval.go index 966cb36..28787a5 100644 --- a/patterneval.go +++ b/patterneval.go @@ -12,14 +12,6 @@ func (e ErrInvalidValueReferenceInPattern) Error() string { return "Invalid value reference in pattern: " + e.Symbol } -func ResultPathToResultSet(resultPath []ResultPath) ResultSet { - rs := ResultSet{} - for _, rp := range resultPath { - rs.Rows = append(rs.Rows, rp.Symbols) - } - return rs -} - func (properties Properties) AsLiteral(ctx *EvalContext) ([]mapKeyValue, error) { if properties.Param != nil { param, err := ctx.GetParameter(string(*properties.Param)) @@ -152,11 +144,11 @@ func BuildPatternSymbols(ctx *EvalContext, pattern lpg.Pattern) (map[string]*lpg ps.AddNode(val) case *lpg.Path: ps.AddPath(val) - case RValue: - switch rv := val.Value.(type) { - case *lpg.Node: - ps.AddNode(rv) - } + // case RValue: + // switch rv := val.Value.(type) { + // case *lpg.Node: + // ps.AddNode(rv) + // } default: // v RValue{Value: *lpg.Node} return nil, ErrInvalidValueReferenceInPattern{Symbol: symbol} From 0ad77f3ee3d7a945d417e1ee8cd04f3b3e8e096c Mon Sep 17 00:00:00 2001 From: Burak Serdar Date: Tue, 25 Apr 2023 09:45:19 -0600 Subject: [PATCH 4/8] Fix double-boxing --- patterneval.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patterneval.go b/patterneval.go index 28787a5..7384c0a 100644 --- a/patterneval.go +++ b/patterneval.go @@ -92,7 +92,7 @@ func (match Match) GetResults(ctx *EvalContext) ([]ResultPath, error) { } for _, row := range results.result { for k, v := range row.Symbols { - newContext.SetVar(k, RValue{Value: v}) + newContext.SetVar(k, v) } // currentRow[index] = row if len(pat) > 1 { From c69b58981378bbd100ea08a03fa3eb52e314503c Mon Sep 17 00:00:00 2001 From: Jason Cruz Date: Wed, 26 Apr 2023 08:30:32 -0700 Subject: [PATCH 5/8] add cartesian product for ResultPath --- cartesian.go | 17 +++++++++-------- cartesian_test.go | 10 +++++++--- patterneval.go | 1 + 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/cartesian.go b/cartesian.go index c06c8a9..e7b8b0f 100644 --- a/cartesian.go +++ b/cartesian.go @@ -1,25 +1,26 @@ package opencypher // CartesianProuductPaths builds the product of all the resultpaths -func CartesianProductPaths(paths [][]ResultPath, filter func(rp []ResultPath) bool) [][]ResultPath { +func CartesianProductPaths(ctx *EvalContext, numItems int, getItem func(int, *EvalContext) []ResultPath, filter func([]ResultPath) bool) [][]ResultPath { // nextIndex retrieves the next index for the next set nextIndex := func(ix []int) { for j := len(ix) - 1; j >= 0; j-- { ix[j]++ - if j == 0 || ix[j] < len(paths[j]) { + if j == 0 || ix[j] < len(getItem(j, ctx)) { return } ix[j] = 0 } } - if len(paths) == 1 { - return paths + if numItems == 1 { + } - product := make([][]ResultPath, 0, len(paths)) - for ix := make([]int, len(paths)); ix[0] < len(paths[0]); nextIndex(ix) { + product := make([][]ResultPath, 0, numItems) + basePath := getItem(0, ctx) + for ix := make([]int, numItems); ix[0] < len(basePath); nextIndex(ix) { r := make([]ResultPath, 0, len(ix)) - for j, k := range ix { - r = append(r, paths[j][k]) + for j := range ix { + r = append(r, getItem(j, ctx)...) } if filter(r) { product = append(product, r) diff --git a/cartesian_test.go b/cartesian_test.go index e69d1b0..8fcfc28 100644 --- a/cartesian_test.go +++ b/cartesian_test.go @@ -33,7 +33,7 @@ func TestCartesianProductPaths(t *testing.T) { } path.Append(pe...) { - i := [][]ResultPath{ + x := [][]ResultPath{ { ResultPath{ Result: path, @@ -59,9 +59,13 @@ func TestCartesianProductPaths(t *testing.T) { // j := [][]ResultPath{3, 2, 1} // k := [][]ResultPath{2, 1, 3} - prod := CartesianProductPaths(i, func(_ []ResultPath) bool { return true }) + prod := CartesianProductPaths(NewEvalContext(target), len(x), func(i int, ec *EvalContext) []ResultPath { + return x[i] + }, func([]ResultPath) bool { + return true + }) n := len(prod) - e := len(i) + e := len(x) fmt.Println(prod) if n != e { t.Fatalf("there should be %d but there were %d groups\n", e, n) diff --git a/patterneval.go b/patterneval.go index 7384c0a..447d723 100644 --- a/patterneval.go +++ b/patterneval.go @@ -76,6 +76,7 @@ func (match Match) GetResults(ctx *EvalContext) ([]ResultPath, error) { // results.Rows = append(results.Rows, newRow) // } + // CartesianProductPaths() nextPattern = func(prevContext *EvalContext, pat []lpg.Pattern, index int) error { newContext := prevContext.SubContext() symbols, err := BuildPatternSymbols(newContext, pat[0]) From 5933c2624117ce17f5f1d75264172b8c1f2f6b20 Mon Sep 17 00:00:00 2001 From: Jason Cruz Date: Thu, 27 Apr 2023 08:31:10 -0700 Subject: [PATCH 6/8] add cartesian product for ResultPath --- cartesian.go | 109 +++++++++++++++++++++++++++++++++++++--------- cartesian_test.go | 2 +- 2 files changed, 90 insertions(+), 21 deletions(-) diff --git a/cartesian.go b/cartesian.go index e7b8b0f..feb895b 100644 --- a/cartesian.go +++ b/cartesian.go @@ -1,34 +1,103 @@ package opencypher // CartesianProuductPaths builds the product of all the resultpaths -func CartesianProductPaths(ctx *EvalContext, numItems int, getItem func(int, *EvalContext) []ResultPath, filter func([]ResultPath) bool) [][]ResultPath { - // nextIndex retrieves the next index for the next set - nextIndex := func(ix []int) { - for j := len(ix) - 1; j >= 0; j-- { - ix[j]++ - if j == 0 || ix[j] < len(getItem(j, ctx)) { - return - } - ix[j] = 0 - } - } +func CartesianProductPaths(ctx *EvalContext, numItems int, all bool, getItem func(int, *EvalContext) []ResultPath, filter func([]ResultPath) bool) [][]ResultPath { + product := make([][]ResultPath, numItems) + product[0] = getItem(0, ctx) if numItems == 1 { - + return product } - product := make([][]ResultPath, 0, numItems) - basePath := getItem(0, ctx) - for ix := make([]int, numItems); ix[0] < len(basePath); nextIndex(ix) { - r := make([]ResultPath, 0, len(ix)) - for j := range ix { - r = append(r, getItem(j, ctx)...) + + indexes := make([]int, numItems) + for { + var path []ResultPath + for i := range indexes { + path = product[i] + if path == nil { + path = getItem(i, ctx) + } + if indexes[i] >= len(path) { + if !all { + return product + } + if filter(path) { + product = append(product, path) + } + product[i] = nil + } } - if filter(r) { - product = append(product, r) + carry := false + for i := range indexes { + indexes[i]++ + if indexes[i] >= len(path) { + indexes[i] = 0 + carry = true + continue + } + carry = false + break + } + if carry { + break } } return product } +// nextIndex retrieves the next index for the next set +// nextIndex := func(ix []int) { +// for j := len(ix) - 1; j >= 0; j-- { +// ix[j]++ +// if j == 0 || ix[j] < len(getItem(j, ctx)) { +// return +// } +// ix[j] = 0 +// } +// } +// result := make([][]ResultPath, 0) +// indexes := make([]int, numItems) +// level := 1 + +// // Match Q1, Q2, Q3 +// // +// // There are 3 levels of queries +// // +// // +// // Q1 Q2 Q3 +// // ----------------- +// // p11 p11_21 p11_21_31 +// // p11_22 p11_21_32 +// // +// // p12 +// // ... + +// // getItems(0) +// // for each items[0] { +// // getItems(1) +// // for each items[1] { +// // getItems(2) +// // for each items [2] +// // capture + +// for { +// product[level] = getItem(level, ctx) +// for indexes[level], path = range product[level] { +// if level+1 == numItems { +// if filter(something) { +// captureResult() +// } +// } else { +// level++ +// } +// } +// if level+1 == numItems { +// level-- +// if indexes[level]+1>=len(product[level]) { +// } +// } +// return product +// } + func AllPathsToResultSets(paths [][]ResultPath) []ResultSet { res := make([]ResultSet, len(paths)) for i, path := range paths { diff --git a/cartesian_test.go b/cartesian_test.go index 8fcfc28..15a2b5f 100644 --- a/cartesian_test.go +++ b/cartesian_test.go @@ -59,7 +59,7 @@ func TestCartesianProductPaths(t *testing.T) { // j := [][]ResultPath{3, 2, 1} // k := [][]ResultPath{2, 1, 3} - prod := CartesianProductPaths(NewEvalContext(target), len(x), func(i int, ec *EvalContext) []ResultPath { + prod := CartesianProductPaths(NewEvalContext(target), len(x), true, func(i int, ec *EvalContext) []ResultPath { return x[i] }, func([]ResultPath) bool { return true From 2fb8bfbb2768265dc9f636b4587b883b1814e85b Mon Sep 17 00:00:00 2001 From: Burak Serdar Date: Thu, 27 Apr 2023 10:01:09 -0600 Subject: [PATCH 7/8] Refactor cartesian product --- cartesian.go | 58 +++++++++++++++----------------------- cartesian_test.go | 72 ++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 36 deletions(-) diff --git a/cartesian.go b/cartesian.go index feb895b..8879347 100644 --- a/cartesian.go +++ b/cartesian.go @@ -1,47 +1,35 @@ package opencypher // CartesianProuductPaths builds the product of all the resultpaths -func CartesianProductPaths(ctx *EvalContext, numItems int, all bool, getItem func(int, *EvalContext) []ResultPath, filter func([]ResultPath) bool) [][]ResultPath { +func CartesianProductPaths(ctx *EvalContext, numItems int, getItem func(int, *EvalContext) []ResultPath, filter func([]ResultPath) bool) [][]ResultPath { product := make([][]ResultPath, numItems) - product[0] = getItem(0, ctx) - if numItems == 1 { - return product - } - indexes := make([]int, numItems) - for { - var path []ResultPath - for i := range indexes { - path = product[i] - if path == nil { - path = getItem(i, ctx) - } - if indexes[i] >= len(path) { - if !all { - return product - } - if filter(path) { - product = append(product, path) - } - product[i] = nil - } - } - carry := false - for i := range indexes { - indexes[i]++ - if indexes[i] >= len(path) { - indexes[i] = 0 - carry = true - continue + columnProcessor := func(next func(int)) func(int) { + return func(column int) { + product[column] = getItem(column, ctx) + for i := range product[column] { + indexes[column] = i + next(column + 1) } - carry = false - break } - if carry { - break + } + + result := make([][]ResultPath, 0) + + capture := func(int) { + row := make([]ResultPath, 0, numItems) + for i, x := range indexes { + row = append(row, product[i][x]) } + result = append(result, row) + } + + next := columnProcessor(capture) + for column := numItems - 2; column >= 0; column-- { + next = columnProcessor(next) } - return product + next(0) + return result } // nextIndex retrieves the next index for the next set diff --git a/cartesian_test.go b/cartesian_test.go index 15a2b5f..3c526d2 100644 --- a/cartesian_test.go +++ b/cartesian_test.go @@ -59,7 +59,7 @@ func TestCartesianProductPaths(t *testing.T) { // j := [][]ResultPath{3, 2, 1} // k := [][]ResultPath{2, 1, 3} - prod := CartesianProductPaths(NewEvalContext(target), len(x), true, func(i int, ec *EvalContext) []ResultPath { + prod := CartesianProductPaths(NewEvalContext(target), len(x), func(i int, ec *EvalContext) []ResultPath { return x[i] }, func([]ResultPath) bool { return true @@ -73,3 +73,73 @@ func TestCartesianProductPaths(t *testing.T) { } } + +func TestCartesianProductPaths2(t *testing.T) { + f, err := os.Open("testdata/g1.json") + if err != nil { + t.Error(err) + return + } + target := lpg.NewGraph() + err = lpg.JSON{}.Decode(target, json.NewDecoder(f)) + if err != nil { + t.Error(err) + return + } + path := &lpg.Path{} + pe := make([]lpg.PathElement, 0) + for itr := target.GetEdges(); itr.Next(); { + // path.Append(lpg.PathElement{ + // Edge: itr.Edge(), + // }) + pe = append(pe, lpg.PathElement{ + Edge: itr.Edge(), + }) + } + path.Append(pe...) + { + x := [][]ResultPath{ + { + ResultPath{ + Result: path, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + }, + } + // j := [][]ResultPath{3, 2, 1} + // k := [][]ResultPath{2, 1, 3} + + prod := CartesianProductPaths(NewEvalContext(target), len(x), func(i int, ec *EvalContext) []ResultPath { + return x[i] + }, func([]ResultPath) bool { + return true + }) + if len(prod) != 12 { + t.Errorf("Got %d", len(prod)) + for _, x := range prod { + fmt.Println("test2:", x) + } + } + } +} From 14dff5f351ec92f101804e4ce4ceb4fb06d68f71 Mon Sep 17 00:00:00 2001 From: Jason Cruz Date: Fri, 28 Apr 2023 10:07:56 -0700 Subject: [PATCH 8/8] add cartesian product for getresults --- cartesian.go | 66 ++---------------- cartesian_test.go | 174 +++++++++++++++++++++------------------------- patterneval.go | 104 ++++++++++++++++----------- 3 files changed, 151 insertions(+), 193 deletions(-) diff --git a/cartesian.go b/cartesian.go index 8879347..e2b0c76 100644 --- a/cartesian.go +++ b/cartesian.go @@ -1,12 +1,14 @@ package opencypher // CartesianProuductPaths builds the product of all the resultpaths -func CartesianProductPaths(ctx *EvalContext, numItems int, getItem func(int, *EvalContext) []ResultPath, filter func([]ResultPath) bool) [][]ResultPath { +func CartesianProductPaths(ctx *EvalContext, numItems int, getItem func(int, *EvalContext) ([]ResultPath, error), filter func([]ResultPath) bool) [][]ResultPath { + result := make([][]ResultPath, 0) product := make([][]ResultPath, numItems) indexes := make([]int, numItems) + columnProcessor := func(next func(int)) func(int) { return func(column int) { - product[column] = getItem(column, ctx) + product[column], _ = getItem(column, ctx) for i := range product[column] { indexes[column] = i next(column + 1) @@ -14,14 +16,14 @@ func CartesianProductPaths(ctx *EvalContext, numItems int, getItem func(int, *Ev } } - result := make([][]ResultPath, 0) - capture := func(int) { row := make([]ResultPath, 0, numItems) for i, x := range indexes { row = append(row, product[i][x]) } - result = append(result, row) + if filter(row) { + result = append(result, row) + } } next := columnProcessor(capture) @@ -32,60 +34,6 @@ func CartesianProductPaths(ctx *EvalContext, numItems int, getItem func(int, *Ev return result } -// nextIndex retrieves the next index for the next set -// nextIndex := func(ix []int) { -// for j := len(ix) - 1; j >= 0; j-- { -// ix[j]++ -// if j == 0 || ix[j] < len(getItem(j, ctx)) { -// return -// } -// ix[j] = 0 -// } -// } -// result := make([][]ResultPath, 0) -// indexes := make([]int, numItems) -// level := 1 - -// // Match Q1, Q2, Q3 -// // -// // There are 3 levels of queries -// // -// // -// // Q1 Q2 Q3 -// // ----------------- -// // p11 p11_21 p11_21_31 -// // p11_22 p11_21_32 -// // -// // p12 -// // ... - -// // getItems(0) -// // for each items[0] { -// // getItems(1) -// // for each items[1] { -// // getItems(2) -// // for each items [2] -// // capture - -// for { -// product[level] = getItem(level, ctx) -// for indexes[level], path = range product[level] { -// if level+1 == numItems { -// if filter(something) { -// captureResult() -// } -// } else { -// level++ -// } -// } -// if level+1 == numItems { -// level-- -// if indexes[level]+1>=len(product[level]) { -// } -// } -// return product -// } - func AllPathsToResultSets(paths [][]ResultPath) []ResultSet { res := make([]ResultSet, len(paths)) for i, path := range paths { diff --git a/cartesian_test.go b/cartesian_test.go index 3c526d2..d4bbeb3 100644 --- a/cartesian_test.go +++ b/cartesian_test.go @@ -33,112 +33,96 @@ func TestCartesianProductPaths(t *testing.T) { } path.Append(pe...) { - x := [][]ResultPath{ + tests := []struct { + rp [][]ResultPath + expLen int + }{ { - ResultPath{ - Result: path, - }, - ResultPath{ - Result: &lpg.Path{}, - }, - ResultPath{ - Result: &lpg.Path{}, + rp: [][]ResultPath{ + { + ResultPath{ + Result: path, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + }, }, + expLen: 12, }, { - ResultPath{ - Result: &lpg.Path{}, + rp: [][]ResultPath{ + { + ResultPath{ + Result: path, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + }, }, + expLen: 3, }, { - ResultPath{ - Result: &lpg.Path{}, + rp: [][]ResultPath{ + { + ResultPath{ + Result: path, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + }, }, + expLen: 1, }, } - // j := [][]ResultPath{3, 2, 1} - // k := [][]ResultPath{2, 1, 3} - - prod := CartesianProductPaths(NewEvalContext(target), len(x), func(i int, ec *EvalContext) []ResultPath { - return x[i] - }, func([]ResultPath) bool { - return true - }) - n := len(prod) - e := len(x) - fmt.Println(prod) - if n != e { - t.Fatalf("there should be %d but there were %d groups\n", e, n) - } - - } -} -func TestCartesianProductPaths2(t *testing.T) { - f, err := os.Open("testdata/g1.json") - if err != nil { - t.Error(err) - return - } - target := lpg.NewGraph() - err = lpg.JSON{}.Decode(target, json.NewDecoder(f)) - if err != nil { - t.Error(err) - return - } - path := &lpg.Path{} - pe := make([]lpg.PathElement, 0) - for itr := target.GetEdges(); itr.Next(); { - // path.Append(lpg.PathElement{ - // Edge: itr.Edge(), - // }) - pe = append(pe, lpg.PathElement{ - Edge: itr.Edge(), - }) - } - path.Append(pe...) - { - x := [][]ResultPath{ - { - ResultPath{ - Result: path, - }, - ResultPath{ - Result: &lpg.Path{}, - }, - ResultPath{ - Result: &lpg.Path{}, - }, - }, - { - ResultPath{ - Result: &lpg.Path{}, - }, - ResultPath{ - Result: &lpg.Path{}, - }, - }, - { - ResultPath{ - Result: &lpg.Path{}, - }, - ResultPath{ - Result: &lpg.Path{}, - }, - }, - } - // j := [][]ResultPath{3, 2, 1} - // k := [][]ResultPath{2, 1, 3} - - prod := CartesianProductPaths(NewEvalContext(target), len(x), func(i int, ec *EvalContext) []ResultPath { - return x[i] - }, func([]ResultPath) bool { - return true - }) - if len(prod) != 12 { - t.Errorf("Got %d", len(prod)) - for _, x := range prod { - fmt.Println("test2:", x) + for _, test := range tests { + prod := CartesianProductPaths(NewEvalContext(target), len(test.rp), func(i int, ec *EvalContext) ([]ResultPath, error) { + return test.rp[i], nil + }, func([]ResultPath) bool { + return true + }) + if len(prod) != test.expLen { + t.Errorf("Got %d", len(prod)) + for _, x := range prod { + fmt.Println("test2:", x) + } } } } diff --git a/patterneval.go b/patterneval.go index 447d723..fbd965d 100644 --- a/patterneval.go +++ b/patterneval.go @@ -61,7 +61,7 @@ func (match Match) GetResults(ctx *EvalContext) ([]ResultPath, error) { patterns = append(patterns, p) } - var nextPattern func(*EvalContext, []lpg.Pattern, int) error + // var nextPattern func(*EvalContext, []lpg.Pattern, int) error returnResults := []ResultPath{} // currentRow := make([]map[string]Value, len(patterns)) @@ -77,49 +77,81 @@ func (match Match) GetResults(ctx *EvalContext) ([]ResultPath, error) { // } // CartesianProductPaths() - nextPattern = func(prevContext *EvalContext, pat []lpg.Pattern, index int) error { - newContext := prevContext.SubContext() - symbols, err := BuildPatternSymbols(newContext, pat[0]) - if err != nil { - return err - } + allPaths := CartesianProductPaths(ctx, len(patterns), func(ix int, ctx *EvalContext) ([]ResultPath, error) { + newContext := ctx.SubContext() + symbols, _ := BuildPatternSymbols(ctx, patterns[ix]) + product := make([]ResultPath, 0) results := matchResultAccumulator{ evalCtx: newContext, result: []ResultPath{}, } - err = pat[0].Run(newContext.graph, symbols, &results) + err := patterns[0].Run(newContext.graph, symbols, &results) if err != nil { - return err + return nil, err } for _, row := range results.result { - for k, v := range row.Symbols { - newContext.SetVar(k, v) - } - // currentRow[index] = row - if len(pat) > 1 { - if err := nextPattern(newContext, pat[1:], index+1); err != nil { - return err + if match.Where != nil { + rs, err := match.Where.Evaluate(newContext) + if err != nil { + return nil, err } - } else { - if match.Where != nil { - rs, err := match.Where.Evaluate(newContext) - if err != nil { - return err - } - if b, _ := ValueAsBool(rs); b { - // addRow() - returnResults = append(returnResults, row) - } - } else { - returnResults = append(returnResults, row) + if b, _ := ValueAsBool(rs); b { + // addRow() + product = append(product, row) } + } else { + product = append(product, row) } } - return nil - } - if err := nextPattern(ctx, patterns, 0); err != nil { - return []ResultPath{}, err - } + return product, nil + + }, func(filterPaths []ResultPath) bool { + return true + }) + + // nextPattern = func(prevContext *EvalContext, pat []lpg.Pattern, index int) error { + // newContext := prevContext.SubContext() + // symbols, err := BuildPatternSymbols(newContext, pat[0]) + // if err != nil { + // return err + // } + // results := matchResultAccumulator{ + // evalCtx: newContext, + // result: []ResultPath{}, + // } + // err = pat[0].Run(newContext.graph, symbols, &results) + // if err != nil { + // return err + // } + // for _, row := range results.result { + // for k, v := range row.Symbols { + // newContext.SetVar(k, v) + // } + // // currentRow[index] = row + // if len(pat) > 1 { + // if err := nextPattern(newContext, pat[1:], index+1); err != nil { + // return err + // } + // } else { + // if match.Where != nil { + // rs, err := match.Where.Evaluate(newContext) + // if err != nil { + // return err + // } + // if b, _ := ValueAsBool(rs); b { + // // addRow() + // product = append(product, row) + // } + // } else { + // product = append(product, row) + // } + // } + // } + // return nil + // } + // if err := nextPattern(ctx, patterns, 0); err != nil { + // return []ResultPath{}, err + // } return returnResults, nil } @@ -145,13 +177,7 @@ func BuildPatternSymbols(ctx *EvalContext, pattern lpg.Pattern) (map[string]*lpg ps.AddNode(val) case *lpg.Path: ps.AddPath(val) - // case RValue: - // switch rv := val.Value.(type) { - // case *lpg.Node: - // ps.AddNode(rv) - // } default: - // v RValue{Value: *lpg.Node} return nil, ErrInvalidValueReferenceInPattern{Symbol: symbol} } symbols[symbol] = ps