This commit is contained in:
Joeseph Grey 2026-06-16 12:10:58 -06:00 committed by GitHub
commit 493a020d18
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 53 additions and 11 deletions

View File

@ -111,10 +111,10 @@ func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode
return Context{}, err
}
// rhs is a collect expression that will yield indices to retrieve of the arrays
// rhs is a collect expression that yields the indices to retrieve. It is
// evaluated over the whole context, producing one index set per incoming
// candidate.
rhs, err := d.GetMatchingNodes(context.ReadOnlyClone(), expressionNode.RHS)
if err != nil {
return Context{}, err
}
@ -123,16 +123,37 @@ func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode
if expressionNode.Operation.Preferences != nil {
prefs = expressionNode.Operation.Preferences.(traversePreferences)
}
var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Content
log.Debugf("indicesToTraverse %v", len(indicesToTraverse))
//now we traverse the result of the lhs against the indices we found
result, err := traverseNodesWithArrayIndices(lhs, indicesToTraverse, prefs)
if err != nil {
return Context{}, err
results := list.New()
if lhs.MatchingNodes.Len() == rhs.MatchingNodes.Len() {
// One index set per LHS node (both derive from the same context):
// traverse each LHS node with its own index set. Previously only the
// first index set was used, so a context-dependent index like `$o[.]`
// over a `keys[]` stream dropped every match but the first (#2593).
rhsEl := rhs.MatchingNodes.Front()
for lhsEl := lhs.MatchingNodes.Front(); lhsEl != nil; lhsEl = lhsEl.Next() {
indicesToTraverse := rhsEl.Value.(*CandidateNode).Content
result, err := traverseNodesWithArrayIndices(context.SingleChildContext(lhsEl.Value.(*CandidateNode)), indicesToTraverse, prefs)
if err != nil {
return Context{}, err
}
results.PushBackList(result.MatchingNodes)
rhsEl = rhsEl.Next()
}
} else {
// LHS collapsed to a single node (e.g. a variable) while the index
// varies per candidate: traverse the LHS against every index set.
for rhsEl := rhs.MatchingNodes.Front(); rhsEl != nil; rhsEl = rhsEl.Next() {
indicesToTraverse := rhsEl.Value.(*CandidateNode).Content
result, err := traverseNodesWithArrayIndices(lhs, indicesToTraverse, prefs)
if err != nil {
return Context{}, err
}
results.PushBackList(result.MatchingNodes)
}
}
return context.ChildContext(result.MatchingNodes), nil
return context.ChildContext(results), nil
}
func traverseNodesWithArrayIndices(context Context, indicesToTraverse []*CandidateNode, prefs traversePreferences) (Context, error) {

View File

@ -675,6 +675,27 @@ var traversePathOperatorScenarios = []expressionScenario{
expression: ". = (.x = 1)",
expectedError: "alias cycle detected",
},
{
// Regression test for https://github.com/mikefarah/yq/issues/2593
// A context-dependent index (here the streamed key) must be applied
// per candidate; previously only the first index was used.
skipDoc: true,
document: `["a","b"]`,
expression: `. as $o | keys[] | $o[.]`,
expected: []string{
"D0, P[0], (!!str)::a\n",
"D0, P[1], (!!str)::b\n",
},
},
{
skipDoc: true,
document: `{"x": 1, "y": 2}`,
expression: `. as $o | keys[] | $o[.]`,
expected: []string{
"D0, P[x], (!!int)::1\n",
"D0, P[y], (!!int)::2\n",
},
},
}
func TestTraversePathOperatorScenarios(t *testing.T) {