From a6d4dbb8b8ae7be10456e2645fed00503dad3ba2 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Fri, 9 Oct 2020 15:05:45 +1100 Subject: [PATCH] equal! --- pkg/yqlib/treeops/data_tree_navigator.go | 57 ++++++++++++++--- pkg/yqlib/treeops/data_tree_navigator_test.go | 61 +++++++++++++++++++ pkg/yqlib/treeops/path_postfix.go | 3 +- pkg/yqlib/treeops/traverse.go | 4 +- 4 files changed, 114 insertions(+), 11 deletions(-) diff --git a/pkg/yqlib/treeops/data_tree_navigator.go b/pkg/yqlib/treeops/data_tree_navigator.go index 4d76e083..61ccad25 100644 --- a/pkg/yqlib/treeops/data_tree_navigator.go +++ b/pkg/yqlib/treeops/data_tree_navigator.go @@ -40,6 +40,54 @@ func (d *dataTreeNavigator) traverse(matchMap *orderedmap.OrderedMap, pathNode * return matchingNodeMap, nil } +func (d *dataTreeNavigator) equalsOperation(matchMap *orderedmap.OrderedMap, pathNode *PathTreeNode) (*orderedmap.OrderedMap, error) { + log.Debugf("-- equalsOperation") + var results = orderedmap.NewOrderedMap() + + for el := matchMap.Front(); el != nil; el = el.Next() { + elMap := orderedmap.NewOrderedMap() + elMap.Set(el.Key, el.Value) + //need to splat matching nodes, then search through them + splatter := &PathTreeNode{PathElement: &PathElement{ + PathElementType: PathKey, + Value: "*", + StringValue: "*", + }} + children, err := d.getMatchingNodes(elMap, splatter) + log.Debugf("-- splatted matches, ") + if err != nil { + return nil, err + } + for childEl := children.Front(); childEl != nil; childEl = childEl.Next() { + childMap := orderedmap.NewOrderedMap() + childMap.Set(childEl.Key, childEl.Value) + childMatches, errChild := d.getMatchingNodes(childMap, pathNode.Lhs) + if errChild != nil { + return nil, errChild + } + + if d.containsMatchingValue(childMatches, pathNode.Rhs.PathElement.StringValue) { + results.Set(childEl.Key, childEl.Value) + } + } + } + + return results, nil +} + +func (d *dataTreeNavigator) containsMatchingValue(matchMap *orderedmap.OrderedMap, valuePattern string) bool { + log.Debugf("-- findMatchingValues") + + for el := matchMap.Front(); el != nil; el = el.Next() { + node := el.Value.(*CandidateNode) + if Match(node.Node.Value, valuePattern) { + return true + } + } + + return false +} + func (d *dataTreeNavigator) setFunction(op OperationType, lhs *orderedmap.OrderedMap, rhs *orderedmap.OrderedMap) *orderedmap.OrderedMap { if op == Or { @@ -57,7 +105,6 @@ func (d *dataTreeNavigator) setFunction(op OperationType, lhs *orderedmap.Ordere } } return matchingNodeMap - } func (d *dataTreeNavigator) GetMatchingNodes(matchingNodes []*CandidateNode, pathNode *PathTreeNode) ([]*CandidateNode, error) { @@ -104,12 +151,8 @@ func (d *dataTreeNavigator) getMatchingNodes(matchingNodes *orderedmap.OrderedMa return nil, err } return d.setFunction(pathNode.PathElement.OperationType, lhs, rhs), nil - // case Equals: - // lhs, err = d.getMatchingNodes(matchingNodes, pathNode.Lhs) - // if err != nil { - // return nil, err - // } - // return d.findMatchingValues(lhs, pathNode.Rhs) + case Equals: + return d.equalsOperation(matchingNodes, pathNode) // case EqualsSelf: // return d.findMatchingValues(matchingNodes, pathNode.Rhs) default: diff --git a/pkg/yqlib/treeops/data_tree_navigator_test.go b/pkg/yqlib/treeops/data_tree_navigator_test.go index 65611b81..c3c3165c 100644 --- a/pkg/yqlib/treeops/data_tree_navigator_test.go +++ b/pkg/yqlib/treeops/data_tree_navigator_test.go @@ -277,3 +277,64 @@ func TestDataTreeNavigatorAnd(t *testing.T) { test.AssertResult(t, expected, resultsToString(results)) } + +func TestDataTreeNavigatorEquals(t *testing.T) { + + nodes := readDoc(t, `a: + cat: {b: apple, c: yes} + pat: {b: banana} +`) + + path, errPath := treeCreator.ParsePath("a.(b == apple)") + if errPath != nil { + t.Error(errPath) + } + results, errNav := treeNavigator.GetMatchingNodes(nodes, path) + + if errNav != nil { + t.Error(errNav) + } + + expected := ` +-- Node -- + Document 0, path: [a cat] + Tag: !!map, Kind: MappingNode, Anchor: + {b: apple, c: yes} +` + + test.AssertResult(t, expected, resultsToString(results)) +} + +func TestDataTreeNavigatorEqualsTrickey(t *testing.T) { + + nodes := readDoc(t, `a: + cat: {b: apso, c: {d : yes}} + pat: {b: apple, c: {d : no}} + sat: {b: apsy, c: {d : yes}} + fat: {b: apple} +`) + + path, errPath := treeCreator.ParsePath("a.(b == ap* and c.d == yes)") + if errPath != nil { + t.Error(errPath) + } + results, errNav := treeNavigator.GetMatchingNodes(nodes, path) + + if errNav != nil { + t.Error(errNav) + } + + expected := ` +-- Node -- + Document 0, path: [a cat] + Tag: !!map, Kind: MappingNode, Anchor: + {b: apso, c: {d: yes}} + +-- Node -- + Document 0, path: [a sat] + Tag: !!map, Kind: MappingNode, Anchor: + {b: apsy, c: {d: yes}} +` + + test.AssertResult(t, expected, resultsToString(results)) +} diff --git a/pkg/yqlib/treeops/path_postfix.go b/pkg/yqlib/treeops/path_postfix.go index 3dd4af69..ba41f9a0 100644 --- a/pkg/yqlib/treeops/path_postfix.go +++ b/pkg/yqlib/treeops/path_postfix.go @@ -32,6 +32,7 @@ type PathElement struct { PathElementType PathElementType OperationType OperationType Value interface{} + StringValue string } // debugging purposes only @@ -117,7 +118,7 @@ func (p *pathPostFixer) ConvertToPostfix(infixTokens []*lex.Token) ([]*PathEleme for _, token := range tokens { switch token.Type { case TokenIds["PATH_KEY"], TokenIds["ARRAY_INDEX"], TokenIds["[+]"], TokenIds["[*]"], TokenIds["**"]: - var pathElement = PathElement{PathElementType: PathKey, Value: token.Value} + var pathElement = PathElement{PathElementType: PathKey, Value: token.Value, StringValue: fmt.Sprintf("%v", token.Value)} result = append(result, &pathElement) case TokenIds["("]: opStack = append(opStack, token) diff --git a/pkg/yqlib/treeops/traverse.go b/pkg/yqlib/treeops/traverse.go index df9928c5..45200e78 100644 --- a/pkg/yqlib/treeops/traverse.go +++ b/pkg/yqlib/treeops/traverse.go @@ -1,8 +1,6 @@ package treeops import ( - "fmt" - "gopkg.in/yaml.v3" ) @@ -19,7 +17,7 @@ func NewTraverser(navigationPrefs NavigationPrefs) Traverser { } func (t *traverser) keyMatches(key *yaml.Node, pathNode *PathElement) bool { - return Match(key.Value, fmt.Sprintf("%v", pathNode.Value)) + return Match(key.Value, pathNode.StringValue) } func (t *traverser) traverseMap(candidate *CandidateNode, pathNode *PathElement) ([]*CandidateNode, error) {