mirror of
https://github.com/mikefarah/yq.git
synced 2024-11-12 13:48:06 +00:00
added path operator!
This commit is contained in:
parent
fc3af441e5
commit
064cff1341
59
pkg/yqlib/doc/Path Operators.md
Normal file
59
pkg/yqlib/doc/Path Operators.md
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
The path operator can be used to find the traversal paths of matching nodes in an expression. The path is returned as an array, which if traversed in order will lead to the matching node.
|
||||||
|
## Examples
|
||||||
|
### Map path
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
b: cat
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a.b | path' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
- a
|
||||||
|
- b
|
||||||
|
```
|
||||||
|
|
||||||
|
### Array path
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
- cat
|
||||||
|
- dog
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a.[] | select(. == "dog") | path' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
- a
|
||||||
|
- 1
|
||||||
|
```
|
||||||
|
|
||||||
|
### Print path and value
|
||||||
|
Given a sample.yml file of:
|
||||||
|
```yaml
|
||||||
|
a:
|
||||||
|
- cat
|
||||||
|
- dog
|
||||||
|
- frog
|
||||||
|
```
|
||||||
|
then
|
||||||
|
```bash
|
||||||
|
yq eval '.a.[] | select(. == "*og") | [{"path":path, "value":.}]' sample.yml
|
||||||
|
```
|
||||||
|
will output
|
||||||
|
```yaml
|
||||||
|
- path:
|
||||||
|
- a
|
||||||
|
- 1
|
||||||
|
value: dog
|
||||||
|
- path:
|
||||||
|
- a
|
||||||
|
- 2
|
||||||
|
value: frog
|
||||||
|
```
|
||||||
|
|
1
pkg/yqlib/doc/headers/Path Operators.md
Normal file
1
pkg/yqlib/doc/headers/Path Operators.md
Normal file
@ -0,0 +1 @@
|
|||||||
|
The path operator can be used to find the traversal paths of matching nodes in an expression. The path is returned as an array, which if traversed in order will lead to the matching node.
|
@ -17,7 +17,6 @@ type OperationType struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// operators TODO:
|
// operators TODO:
|
||||||
// - get path operator (like doc index)
|
|
||||||
// - write in place
|
// - write in place
|
||||||
// - mergeAppend (merges and appends arrays)
|
// - mergeAppend (merges and appends arrays)
|
||||||
// - mergeEmpty (sets only if the document is empty, do I do that now?)
|
// - mergeEmpty (sets only if the document is empty, do I do that now?)
|
||||||
@ -51,6 +50,7 @@ var GetComment = &OperationType{Type: "GET_COMMENT", NumArgs: 0, Precedence: 50,
|
|||||||
var GetDocumentIndex = &OperationType{Type: "GET_DOCUMENT_INDEX", NumArgs: 0, Precedence: 50, Handler: GetDocumentIndexOperator}
|
var GetDocumentIndex = &OperationType{Type: "GET_DOCUMENT_INDEX", NumArgs: 0, Precedence: 50, Handler: GetDocumentIndexOperator}
|
||||||
var GetFilename = &OperationType{Type: "GET_FILENAME", NumArgs: 0, Precedence: 50, Handler: GetFilenameOperator}
|
var GetFilename = &OperationType{Type: "GET_FILENAME", NumArgs: 0, Precedence: 50, Handler: GetFilenameOperator}
|
||||||
var GetFileIndex = &OperationType{Type: "GET_FILE_INDEX", NumArgs: 0, Precedence: 50, Handler: GetFileIndexOperator}
|
var GetFileIndex = &OperationType{Type: "GET_FILE_INDEX", NumArgs: 0, Precedence: 50, Handler: GetFileIndexOperator}
|
||||||
|
var GetPath = &OperationType{Type: "GET_PATH", NumArgs: 0, Precedence: 50, Handler: GetPathOperator}
|
||||||
|
|
||||||
var Explode = &OperationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: ExplodeOperator}
|
var Explode = &OperationType{Type: "EXPLODE", NumArgs: 1, Precedence: 50, Handler: ExplodeOperator}
|
||||||
|
|
||||||
|
39
pkg/yqlib/operator_path.go
Normal file
39
pkg/yqlib/operator_path.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"container/list"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
yaml "gopkg.in/yaml.v3"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createPathNodeFor(pathElement interface{}) *yaml.Node {
|
||||||
|
switch pathElement := pathElement.(type) {
|
||||||
|
case string:
|
||||||
|
return &yaml.Node{Kind: yaml.ScalarNode, Value: pathElement, Tag: "!!str"}
|
||||||
|
default:
|
||||||
|
return &yaml.Node{Kind: yaml.ScalarNode, Value: fmt.Sprintf("%v", pathElement), Tag: "!!int"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPathOperator(d *dataTreeNavigator, matchingNodes *list.List, pathNode *PathTreeNode) (*list.List, error) {
|
||||||
|
log.Debugf("GetPath")
|
||||||
|
|
||||||
|
var results = list.New()
|
||||||
|
|
||||||
|
for el := matchingNodes.Front(); el != nil; el = el.Next() {
|
||||||
|
candidate := el.Value.(*CandidateNode)
|
||||||
|
node := &yaml.Node{Kind: yaml.SequenceNode, Tag: "!!seq"}
|
||||||
|
|
||||||
|
content := make([]*yaml.Node, len(candidate.Path))
|
||||||
|
for pathIndex := 0; pathIndex < len(candidate.Path); pathIndex++ {
|
||||||
|
path := candidate.Path[pathIndex]
|
||||||
|
content[pathIndex] = createPathNodeFor(path)
|
||||||
|
}
|
||||||
|
node.Content = content
|
||||||
|
lengthCand := &CandidateNode{Node: node, Document: candidate.Document, Path: candidate.Path}
|
||||||
|
results.PushBack(lengthCand)
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
45
pkg/yqlib/operator_path_test.go
Normal file
45
pkg/yqlib/operator_path_test.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package yqlib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
var pathOperatorScenarios = []expressionScenario{
|
||||||
|
{
|
||||||
|
description: "Map path",
|
||||||
|
document: `{a: {b: cat}}`,
|
||||||
|
expression: `.a.b | path`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a b], (!!seq)::- a\n- b\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Array path",
|
||||||
|
document: `{a: [cat, dog]}`,
|
||||||
|
expression: `.a.[] | select(. == "dog") | path`,
|
||||||
|
expected: []string{
|
||||||
|
"D0, P[a 1], (!!seq)::- a\n- 1\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
description: "Print path and value",
|
||||||
|
document: `{a: [cat, dog, frog]}`,
|
||||||
|
expression: `.a.[] | select(. == "*og") | [{"path":path, "value":.}]`,
|
||||||
|
expected: []string{`D0, P[], (!!seq)::- path:
|
||||||
|
- a
|
||||||
|
- 1
|
||||||
|
value: dog
|
||||||
|
- path:
|
||||||
|
- a
|
||||||
|
- 2
|
||||||
|
value: frog
|
||||||
|
`},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathOperatorsScenarios(t *testing.T) {
|
||||||
|
for _, tt := range pathOperatorScenarios {
|
||||||
|
testScenario(t, &tt)
|
||||||
|
}
|
||||||
|
documentScenarios(t, "Path Operators", pathOperatorScenarios)
|
||||||
|
}
|
@ -206,6 +206,7 @@ func initLexer() (*lex.Lexer, error) {
|
|||||||
lexer.Add([]byte(`tag`), opAssignableToken(GetTag, AssignTag))
|
lexer.Add([]byte(`tag`), opAssignableToken(GetTag, AssignTag))
|
||||||
lexer.Add([]byte(`filename`), opToken(GetFilename))
|
lexer.Add([]byte(`filename`), opToken(GetFilename))
|
||||||
lexer.Add([]byte(`fileIndex`), opToken(GetFileIndex))
|
lexer.Add([]byte(`fileIndex`), opToken(GetFileIndex))
|
||||||
|
lexer.Add([]byte(`path`), opToken(GetPath))
|
||||||
|
|
||||||
lexer.Add([]byte(`lineComment`), opTokenWithPrefs(GetComment, AssignComment, &CommentOpPreferences{LineComment: true}))
|
lexer.Add([]byte(`lineComment`), opTokenWithPrefs(GetComment, AssignComment, &CommentOpPreferences{LineComment: true}))
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user