From c8630fe4f31b3113d6eb4563acb00233faa67cfe Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Sun, 25 Apr 2021 12:05:56 +1000 Subject: [PATCH] Fixes delete issue #793 --- pkg/yqlib/candidate_node.go | 8 ++-- pkg/yqlib/lib.go | 1 - pkg/yqlib/operator_delete.go | 43 +++++---------------- pkg/yqlib/operator_delete_test.go | 64 +++++++++++++++++++++++++++++++ 4 files changed, 78 insertions(+), 38 deletions(-) diff --git a/pkg/yqlib/candidate_node.go b/pkg/yqlib/candidate_node.go index 87941c31..41bac01d 100644 --- a/pkg/yqlib/candidate_node.go +++ b/pkg/yqlib/candidate_node.go @@ -8,9 +8,10 @@ import ( ) type CandidateNode struct { - Node *yaml.Node // the actual node - Path []interface{} /// the path we took to get to this node - Document uint // the document index of this node + Node *yaml.Node // the actual node + Parent *CandidateNode // parent node + Path []interface{} /// the path we took to get to this node + Document uint // the document index of this node Filename string FileIndex int // when performing op against all nodes given, this will treat all the nodes as one @@ -31,6 +32,7 @@ func (n *CandidateNode) CreateChild(path interface{}, node *yaml.Node) *Candidat return &CandidateNode{ Node: node, Path: n.createChildPath(path), + Parent: n, Document: n.Document, Filename: n.Filename, FileIndex: n.FileIndex, diff --git a/pkg/yqlib/lib.go b/pkg/yqlib/lib.go index c668cbeb..39fc2610 100644 --- a/pkg/yqlib/lib.go +++ b/pkg/yqlib/lib.go @@ -95,7 +95,6 @@ var recursiveDescentOpType = &operationType{Type: "RECURSIVE_DESCENT", NumArgs: var selectOpType = &operationType{Type: "SELECT", NumArgs: 1, Precedence: 50, Handler: selectOperator} var hasOpType = &operationType{Type: "HAS", NumArgs: 1, Precedence: 50, Handler: hasOperator} var deleteChildOpType = &operationType{Type: "DELETE", NumArgs: 1, Precedence: 40, Handler: deleteChildOperator} -var deleteImmediateChildOpType = &operationType{Type: "DELETE_IMMEDIATE_CHILD", NumArgs: 1, Precedence: 40, Handler: deleteImmediateChildOperator} type Operation struct { OperationType *operationType diff --git a/pkg/yqlib/operator_delete.go b/pkg/yqlib/operator_delete.go index d517de2a..cb6b94dd 100644 --- a/pkg/yqlib/operator_delete.go +++ b/pkg/yqlib/operator_delete.go @@ -18,48 +18,23 @@ func deleteChildOperator(d *dataTreeNavigator, context Context, expressionNode * for el := nodesToDelete.MatchingNodes.Back(); el != nil; el = el.Prev() { candidate := el.Value.(*CandidateNode) - if len(candidate.Path) > 0 { - deleteImmediateChildOp := &Operation{ - OperationType: deleteImmediateChildOpType, - Value: candidate.Path[len(candidate.Path)-1], - } - - deleteImmediateChildOpNode := &ExpressionNode{ - Operation: deleteImmediateChildOp, - Rhs: createTraversalTree(candidate.Path[0:len(candidate.Path)-1], traversePreferences{}, false), - } - - _, err := d.GetMatchingNodes(contextToUse, deleteImmediateChildOpNode) - if err != nil { - return Context{}, err - } + //problem: context may already be '.a' and then I pass in '.a.a2'. + // should pass in .a2. + if candidate.Parent == nil { + log.Info("Could not find parent of %v", candidate.GetKey()) + return context, nil } - } - return context, nil -} -func deleteImmediateChildOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { - parents, err := d.GetMatchingNodes(context, expressionNode.Rhs) + parentNode := candidate.Parent.Node + childPath := candidate.Path[len(candidate.Path)-1] - if err != nil { - return Context{}, err - } - - childPath := expressionNode.Operation.Value - - log.Debug("childPath to remove %v", childPath) - - for el := parents.MatchingNodes.Front(); el != nil; el = el.Next() { - parent := el.Value.(*CandidateNode) - parentNode := unwrapDoc(parent.Node) if parentNode.Kind == yaml.MappingNode { - deleteFromMap(parent, childPath) + deleteFromMap(candidate.Parent, childPath) } else if parentNode.Kind == yaml.SequenceNode { - deleteFromArray(parent, childPath) + deleteFromArray(candidate.Parent, childPath) } else { return Context{}, fmt.Errorf("Cannot delete nodes from parent of tag %v", parentNode.Tag) } - } return context, nil } diff --git a/pkg/yqlib/operator_delete_test.go b/pkg/yqlib/operator_delete_test.go index 109bc2d8..78c44cb7 100644 --- a/pkg/yqlib/operator_delete_test.go +++ b/pkg/yqlib/operator_delete_test.go @@ -21,6 +21,70 @@ var deleteOperatorScenarios = []expressionScenario{ "D0, P[], (doc)::{a: {a2: frood}}\n", }, }, + { + skipDoc: true, + document: `{a: {a1: fred, a2: frood}}`, + expression: `.a | del(.a1)`, + expected: []string{ + "D0, P[a], (!!map)::{a2: frood}\n", + }, + }, + { + skipDoc: true, + document: `a: [1,2,3]`, + expression: `.a | del(.[1])`, + expected: []string{ + "D0, P[a], (!!seq)::[1, 3]\n", + }, + }, + { + skipDoc: true, + document: `[0, {a: cat, b: dog}]`, + expression: `.[1] | del(.a)`, + expected: []string{ + "D0, P[1], (!!map)::{b: dog}\n", + }, + }, + { + skipDoc: true, + document: `[{a: cat, b: dog}]`, + expression: `.[0] | del(.a)`, + expected: []string{ + "D0, P[0], (!!map)::{b: dog}\n", + }, + }, + { + skipDoc: true, + document: `[{a: {b: thing, c: frog}}]`, + expression: `.[0].a | del(.b)`, + expected: []string{ + "D0, P[0 a], (!!map)::{c: frog}\n", + }, + }, + { + skipDoc: true, + document: `[{a: {b: thing, c: frog}}]`, + expression: `.[0] | del(.a.b)`, + expected: []string{ + "D0, P[0], (!!map)::{a: {c: frog}}\n", + }, + }, + { + skipDoc: true, + document: `{a: [0, {b: thing, c: frog}]}`, + expression: `.a[1] | del(.b)`, + expected: []string{ + "D0, P[a 1], (!!map)::{c: frog}\n", + }, + }, + { + skipDoc: true, + document: `{a: [0, {b: thing, c: frog}]}`, + expression: `.a | del(.[1].b)`, + expected: []string{ + "D0, P[a], (!!seq)::[0, {c: frog}]\n", + }, + }, { skipDoc: true, document: `{a: {a1: fred, a2: frood}}`,