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/cartesian.go b/cartesian.go new file mode 100644 index 0000000..e2b0c76 --- /dev/null +++ b/cartesian.go @@ -0,0 +1,53 @@ +package opencypher + +// CartesianProuductPaths builds the product of all the resultpaths +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) + for i := range product[column] { + indexes[column] = i + next(column + 1) + } + } + } + + capture := func(int) { + row := make([]ResultPath, 0, numItems) + for i, x := range indexes { + row = append(row, product[i][x]) + } + if filter(row) { + result = append(result, row) + } + } + + next := columnProcessor(capture) + for column := numItems - 2; column >= 0; column-- { + next = columnProcessor(next) + } + next(0) + return result +} + +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 new file mode 100644 index 0000000..d4bbeb3 --- /dev/null +++ b/cartesian_test.go @@ -0,0 +1,129 @@ +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...) + { + tests := []struct { + rp [][]ResultPath + expLen int + }{ + { + 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, + }, + { + rp: [][]ResultPath{ + { + ResultPath{ + Result: path, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + }, + }, + expLen: 3, + }, + { + rp: [][]ResultPath{ + { + ResultPath{ + Result: path, + }, + }, + { + ResultPath{ + Result: &lpg.Path{}, + }, + }, + }, + expLen: 1, + }, + } + + 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/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..fbd965d 100644 --- a/patterneval.go +++ b/patterneval.go @@ -36,7 +36,7 @@ func (properties Properties) AsLiteral(ctx *EvalContext) ([]mapKeyValue, error) type matchResultAccumulator struct { evalCtx *EvalContext - result *ResultSet + result []ResultPath err error } @@ -45,82 +45,114 @@ 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 + // 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() - symbols, err := BuildPatternSymbols(newContext, pat[0]) - if err != nil { - return err - } + // CartesianProductPaths() + 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: NewResultSet(), + 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.Rows { - for k, v := range row { - newContext.SetVar(k, v) - } - currentRow[index] = row - if len(pat) > 1 { - if err := nextPattern(newContext, pat[1:], index+1); err != nil { - return err + for _, row := range results.result { + 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() - } - } else { - addRow() + 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 ResultSet{}, err - } - return *results, nil + 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 } // BuildPatternSymbols copies all the symbols referenced in the 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" } + ] + } + ] +} 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 }