From 82f717226c0f3feebcc3f9b1991707e1b5cc55f6 Mon Sep 17 00:00:00 2001 From: Mike Farah Date: Tue, 30 May 2023 08:46:51 +1000 Subject: [PATCH] Fixed traverse issue --- pkg/yqlib/candidate_node.go | 3 +- pkg/yqlib/doc/operators/add.md | 50 +++++++++ pkg/yqlib/operator_add_test.go | 152 ++++++++++++++-------------- pkg/yqlib/operator_traverse_path.go | 5 +- 4 files changed, 130 insertions(+), 80 deletions(-) diff --git a/pkg/yqlib/candidate_node.go b/pkg/yqlib/candidate_node.go index 0bfeebe0..bfaadd7b 100644 --- a/pkg/yqlib/candidate_node.go +++ b/pkg/yqlib/candidate_node.go @@ -211,7 +211,7 @@ func (n *CandidateNode) SetParent(parent *CandidateNode) { n.Parent = parent } -func (n *CandidateNode) AddKeyValueChild(rawKey *CandidateNode, rawValue *CandidateNode) { +func (n *CandidateNode) AddKeyValueChild(rawKey *CandidateNode, rawValue *CandidateNode) (*CandidateNode, *CandidateNode) { key := rawKey.unwrapDocument().Copy() key.SetParent(n) key.IsMapKey = true @@ -221,6 +221,7 @@ func (n *CandidateNode) AddKeyValueChild(rawKey *CandidateNode, rawValue *Candid value.Key = key n.Content = append(n.Content, key, value) + return key, value } func (n *CandidateNode) AddChild(rawChild *CandidateNode) { diff --git a/pkg/yqlib/doc/operators/add.md b/pkg/yqlib/doc/operators/add.md index 6263bfb5..b5b6ec50 100644 --- a/pkg/yqlib/doc/operators/add.md +++ b/pkg/yqlib/doc/operators/add.md @@ -9,6 +9,56 @@ Add behaves differently according to the type of the LHS: Use `+=` as a relative append assign for things like increment. Note that `.a += .x` is equivalent to running `.a = .a + .x`. +## Concatenate arrays +Given a sample.yml file of: +```yaml +{a: [1, 2], b: [3, 4]} +``` +then +```bash +yq '.a + .b' sample.yml +``` +will output +```yaml +[1, 2, 3, 4] +``` + +## Concatenate to existing array +Note that the styling of `a` is kept. + +Given a sample.yml file of: +```yaml +a: [1,2] +b: + - 3 + - 4 +``` +then +```bash +yq '.a += .b' sample.yml +``` +will output +```yaml +a: [1, 2, 3, 4] +b: + - 3 + - 4 +``` + +## Concatenate null to array +Given a sample.yml file of: +```yaml +{a: [1, 2]} +``` +then +```bash +yq '.a + null' sample.yml +``` +will output +```yaml +[1, 2, null] +``` + ## Append to existing array Note that the styling is copied from existing array elements diff --git a/pkg/yqlib/operator_add_test.go b/pkg/yqlib/operator_add_test.go index 265de9e5..eeabffde 100644 --- a/pkg/yqlib/operator_add_test.go +++ b/pkg/yqlib/operator_add_test.go @@ -5,83 +5,83 @@ import ( ) var addOperatorScenarios = []expressionScenario{ - // { - // skipDoc: true, - // document: `[{a: foo, b: bar}, {a: 1, b: 2}]`, - // expression: ".[] | .a + .b", - // expected: []string{ - // "D0, P[0 a], (!!str)::foobar\n", - // "D0, P[1 a], (!!int)::3\n", - // }, - // }, - // { - // skipDoc: true, - // document: `a: key`, - // expression: `. += {"key": "b"}`, - // expected: []string{ - // "D0, P[], (!!map)::a: key\nkey: b\n", - // }, - // }, - // { - // skipDoc: true, - // document: `[[c], [b]]`, - // expression: `.[] | . += "a"`, - // expected: []string{ - // "D0, P[0], (!!seq)::[c, a]\n", - // "D0, P[1], (!!seq)::[b, a]\n", - // }, - // }, - // { - // skipDoc: true, - // document: `{}`, - // expression: "(.a + .b) as $x | .", - // expected: []string{ - // "D0, P[], (doc)::{}\n", - // }, - // }, - // { - // skipDoc: true, - // document: `a: 0`, - // expression: ".a += .b.c", - // expected: []string{ - // "D0, P[], (doc)::a: 0\n", - // }, - // }, + { + skipDoc: true, + document: `[{a: foo, b: bar}, {a: 1, b: 2}]`, + expression: ".[] | .a + .b", + expected: []string{ + "D0, P[0 a], (!!str)::foobar\n", + "D0, P[1 a], (!!int)::3\n", + }, + }, + { + skipDoc: true, + document: `a: key`, + expression: `. += {"key": "b"}`, + expected: []string{ + "D0, P[], (!!map)::a: key\nkey: b\n", + }, + }, + { + skipDoc: true, + document: `[[c], [b]]`, + expression: `.[] | . += "a"`, + expected: []string{ + "D0, P[0], (!!seq)::[c, a]\n", + "D0, P[1], (!!seq)::[b, a]\n", + }, + }, + { + skipDoc: true, + document: `{}`, + expression: "(.a + .b) as $x | .", + expected: []string{ + "D0, P[], (doc)::{}\n", + }, + }, + { + skipDoc: true, + document: `a: 0`, + expression: ".a += .b.c", + expected: []string{ + "D0, P[], (doc)::a: 0\n", + }, + }, - // { - // description: "Concatenate arrays", - // document: `{a: [1,2], b: [3,4]}`, - // expression: `.a + .b`, - // expected: []string{ - // "D0, P[a], (!!seq)::[1, 2, 3, 4]\n", - // }, - // }, - // { - // description: "Concatenate to existing array", - // subdescription: "Note that the styling of `a` is kept.", - // document: "a: [1,2]\nb:\n - 3\n - 4", - // dontFormatInputForDoc: true, - // expression: `.a += .b`, - // expected: []string{ - // "D0, P[], (doc)::a: [1, 2, 3, 4]\nb:\n - 3\n - 4\n", - // }, - // }, - // { - // skipDoc: true, - // expression: `[1] + ([2], [3])`, - // expected: []string{ - // "D0, P[], (!!seq)::- 1\n- 2\n", - // "D0, P[], (!!seq)::- 1\n- 3\n", - // }, - // }, - // { - // description: "Concatenate null to array", - // document: `{a: [1,2]}`, - // expression: `.a + null`, - // expected: []string{ - // "D0, P[a], (!!seq)::[1, 2]\n", - // }, - // }, + { + description: "Concatenate arrays", + document: `{a: [1,2], b: [3,4]}`, + expression: `.a + .b`, + expected: []string{ + "D0, P[a], (!!seq)::[1, 2, 3, 4]\n", + }, + }, + { + description: "Concatenate to existing array", + subdescription: "Note that the styling of `a` is kept.", + document: "a: [1,2]\nb:\n - 3\n - 4", + dontFormatInputForDoc: true, + expression: `.a += .b`, + expected: []string{ + "D0, P[], (doc)::a: [1, 2, 3, 4]\nb:\n - 3\n - 4\n", + }, + }, + { + skipDoc: true, + expression: `[1] + ([2], [3])`, + expected: []string{ + "D0, P[], (!!seq)::- 1\n- 2\n", + "D0, P[], (!!seq)::- 1\n- 3\n", + }, + }, + { + description: "Concatenate null to array", + document: `{a: [1,2]}`, + expression: `.a + null`, + expected: []string{ + "D0, P[a], (!!seq)::[1, 2]\n", + }, + }, { skipDoc: true, description: "Concatenate to empty array", diff --git a/pkg/yqlib/operator_traverse_path.go b/pkg/yqlib/operator_traverse_path.go index 3f7810ce..0a10690c 100644 --- a/pkg/yqlib/operator_traverse_path.go +++ b/pkg/yqlib/operator_traverse_path.go @@ -234,19 +234,18 @@ func traverseMap(context Context, matchingNode *CandidateNode, keyNode *Candidat } if !splat && !prefs.DontAutoCreate && !context.DontAutoCreate && newMatches.Len() == 0 { - log.Debugf("no matches, creating one") + log.Debugf("no matches, creating one for %v", NodeToString(keyNode)) //no matches, create one automagically valueNode := matchingNode.CreateChild() valueNode.Kind = ScalarNode valueNode.Tag = "!!null" valueNode.Value = "null" - valueNode.Key = keyNode if len(matchingNode.Content) == 0 { matchingNode.Style = 0 } - matchingNode.AddKeyValueChild(keyNode, valueNode) + keyNode, valueNode = matchingNode.AddKeyValueChild(keyNode, valueNode) if prefs.IncludeMapKeys { newMatches.Set(keyNode.GetKey(), keyNode)