Can specify parent levels #1970

This commit is contained in:
Mike Farah 2024-03-12 10:48:42 +11:00
parent 9e9cb65ec0
commit 8a07e3da3d
4 changed files with 97 additions and 4 deletions

View File

@ -37,6 +37,43 @@ fruit: banana
name: sam name: sam
``` ```
## N-th parent
You can optionally supply the number of levels to go up for the parent, the default being 1.
Given a sample.yml file of:
```yaml
a:
b:
c: cat
```
then
```bash
yq '.a.b.c | parent(2)' sample.yml
```
will output
```yaml
b:
c: cat
```
## N-th parent - another level
Given a sample.yml file of:
```yaml
a:
b:
c: cat
```
then
```bash
yq '.a.b.c | parent(3)' sample.yml
```
will output
```yaml
a:
b:
c: cat
```
## No parent ## No parent
Given a sample.yml file of: Given a sample.yml file of:
```yaml ```yaml

View File

@ -130,7 +130,9 @@ var participleYqRules = []*participleYqRule{
simpleOp("contains", containsOpType), simpleOp("contains", containsOpType),
simpleOp("split", splitStringOpType), simpleOp("split", splitStringOpType),
simpleOp("parent", getParentOpType),
{"ParentWithLevel", `parent\([0-9]+\)`, parentWithLevel(), 0},
{"ParentWithDefaultLevel", `parent`, parentWithDefaultLevel(), 0},
simpleOp("keys", keysOpType), simpleOp("keys", keysOpType),
simpleOp("key", getKeyOpType), simpleOp("key", getKeyOpType),
@ -501,6 +503,28 @@ func numberValue() yqAction {
} }
} }
func parentWithLevel() yqAction {
return func(rawToken lexer.Token) (*token, error) {
value := rawToken.Value
var level, errParsingInt = extractNumberParameter(value)
if errParsingInt != nil {
return nil, errParsingInt
}
prefs := parentOpPreferences{Level: level}
op := &Operation{OperationType: getParentOpType, Value: getParentOpType.Type, StringValue: value, Preferences: prefs}
return &token{TokenType: operationToken, Operation: op}, nil
}
}
func parentWithDefaultLevel() yqAction {
return func(rawToken lexer.Token) (*token, error) {
prefs := parentOpPreferences{Level: 1}
op := &Operation{OperationType: getParentOpType, Value: getParentOpType.Type, StringValue: getParentOpType.Type, Preferences: prefs}
return &token{TokenType: operationToken, Operation: op}, nil
}
}
func encodeParseIndent(outputFormat *Format) yqAction { func encodeParseIndent(outputFormat *Format) yqAction {
return func(rawToken lexer.Token) (*token, error) { return func(rawToken lexer.Token) (*token, error) {
value := rawToken.Value value := rawToken.Value

View File

@ -2,15 +2,30 @@ package yqlib
import "container/list" import "container/list"
func getParentOperator(_ *dataTreeNavigator, context Context, _ *ExpressionNode) (Context, error) { type parentOpPreferences struct {
Level int
}
func getParentOperator(_ *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) {
log.Debugf("getParentOperator") log.Debugf("getParentOperator")
var results = list.New() var results = list.New()
prefs := expressionNode.Operation.Preferences.(parentOpPreferences)
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() { for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode) candidate := el.Value.(*CandidateNode)
if candidate.Parent != nil { currentLevel := 0
results.PushBack(candidate.Parent) for currentLevel < prefs.Level && candidate != nil {
log.Debugf("currentLevel: %v, desired: %v", currentLevel, prefs.Level)
log.Debugf("candidate: %v", NodeToString(candidate))
candidate = candidate.Parent
currentLevel++
}
log.Debugf("found candidate: %v", NodeToString(candidate))
if candidate != nil {
results.PushBack(candidate)
} }
} }

View File

@ -21,6 +21,23 @@ var parentOperatorScenarios = []expressionScenario{
"D0, P[b], (!!map)::{fruit: banana, name: sam}\n", "D0, P[b], (!!map)::{fruit: banana, name: sam}\n",
}, },
}, },
{
description: "N-th parent",
subdescription: "You can optionally supply the number of levels to go up for the parent, the default being 1.",
document: "a:\n b:\n c: cat\n",
expression: `.a.b.c | parent(2)`,
expected: []string{
"D0, P[a], (!!map)::b:\n c: cat\n",
},
},
{
description: "N-th parent - another level",
document: "a:\n b:\n c: cat\n",
expression: `.a.b.c | parent(3)`,
expected: []string{
"D0, P[], (!!map)::a:\n b:\n c: cat\n",
},
},
{ {
description: "No parent", description: "No parent",
document: `{}`, document: `{}`,