From 4d88d51b1b5d7d3e9513ac424fac5ef870f47e81 Mon Sep 17 00:00:00 2001 From: Steven WdV Date: Mon, 16 Jun 2025 13:36:42 +0200 Subject: [PATCH] Fix precedence of merge anchor sequence for traverse (explode was already correct) --- pkg/yqlib/doc/operators/traverse-read.md | 8 ++++---- pkg/yqlib/operator_traverse_path.go | 14 ++++++++------ pkg/yqlib/operator_traverse_path_test.go | 8 ++++---- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/pkg/yqlib/doc/operators/traverse-read.md b/pkg/yqlib/doc/operators/traverse-read.md index fefe24df..af5c965b 100644 --- a/pkg/yqlib/doc/operators/traverse-read.md +++ b/pkg/yqlib/doc/operators/traverse-read.md @@ -399,7 +399,7 @@ foobar_thing ``` ## Traversing merge anchor lists -Note that the later merge anchors override previous +Note that the keys earlier in the merge anchors sequence override later ones Given a sample.yml file of: ```yaml @@ -428,7 +428,7 @@ yq '.foobarList.thing' sample.yml ``` will output ```yaml -bar_thing +foo_thing ``` ## Splatting merge anchor lists @@ -460,9 +460,9 @@ yq '.foobarList[]' sample.yml will output ```yaml bar_b -foo_a -bar_thing +foo_thing foobarList_c +foo_a ``` ## Select multiple indices diff --git a/pkg/yqlib/operator_traverse_path.go b/pkg/yqlib/operator_traverse_path.go index 88c8f31d..ecdeafbd 100644 --- a/pkg/yqlib/operator_traverse_path.go +++ b/pkg/yqlib/operator_traverse_path.go @@ -292,15 +292,17 @@ func doTraverseMap(newMatches *orderedmap.OrderedMap, node *CandidateNode, wante return nil } -func traverseMergeAnchor(newMatches *orderedmap.OrderedMap, value *CandidateNode, wantedKey string, prefs traversePreferences, splat bool) error { - if value.Kind == AliasNode { - value = value.Alias +func traverseMergeAnchor(newMatches *orderedmap.OrderedMap, merge *CandidateNode, wantedKey string, prefs traversePreferences, splat bool) error { + if merge.Kind == AliasNode { + merge = merge.Alias } - switch value.Kind { + switch merge.Kind { case MappingNode: - return doTraverseMap(newMatches, value, wantedKey, prefs, splat) + return doTraverseMap(newMatches, merge, wantedKey, prefs, splat) case SequenceNode: - for _, childValue := range value.Content { + // Earlier keys take precedence + for index := len(merge.Content) - 1; index >= 0; index = index - 1 { + childValue := merge.Content[index] if childValue.Kind == AliasNode { childValue = childValue.Alias } diff --git a/pkg/yqlib/operator_traverse_path_test.go b/pkg/yqlib/operator_traverse_path_test.go index bf1cad19..4b456d0e 100644 --- a/pkg/yqlib/operator_traverse_path_test.go +++ b/pkg/yqlib/operator_traverse_path_test.go @@ -449,11 +449,11 @@ var traversePathOperatorScenarios = []expressionScenario{ }, { description: "Traversing merge anchor lists", - subdescription: "Note that the later merge anchors override previous", + subdescription: "Note that the keys earlier in the merge anchors sequence override later ones", document: mergeDocSample, expression: `.foobarList.thing`, expected: []string{ - "D0, P[bar thing], (!!str)::bar_thing\n", + "D0, P[foo thing], (!!str)::foo_thing\n", }, }, { @@ -478,9 +478,9 @@ var traversePathOperatorScenarios = []expressionScenario{ expression: `.foobarList[]`, expected: []string{ "D0, P[bar b], (!!str)::bar_b\n", - "D0, P[foo a], (!!str)::foo_a\n", - "D0, P[bar thing], (!!str)::bar_thing\n", + "D0, P[foo thing], (!!str)::foo_thing\n", "D0, P[foobarList c], (!!str)::foobarList_c\n", + "D0, P[foo a], (!!str)::foo_a\n", }, }, {