fix: traverse array with streaming indices from variable binding

When streaming values (e.g. from keys[]) pipe into an array
traversal like $o[.], only the first streaming index was used.

The RHS collect expression produces a separate SequenceNode per
streaming value when EvaluateTogether is false. The existing code
at line 126 only took the first SequenceNode, discarding the rest.

When the LHS is a single bound variable and the RHS has multiple
index sets, traverse each set separately.

Fixes #2593
This commit is contained in:
lawrence3699 2026-04-09 19:23:57 +10:00
parent 3b2423e871
commit f4f77089ff
No known key found for this signature in database
2 changed files with 53 additions and 0 deletions

View File

@ -123,6 +123,27 @@ func traverseArrayOperator(d *dataTreeNavigator, context Context, expressionNode
if expressionNode.Operation.Preferences != nil {
prefs = expressionNode.Operation.Preferences.(traversePreferences)
}
// When streaming values produce per-value index sets in the RHS and the LHS
// is a single node (e.g. a bound variable), traverse the LHS with each
// index set separately. Without this, only the first set of indices is used
// and the rest are silently dropped.
if rhs.MatchingNodes.Len() > 1 && lhs.MatchingNodes.Len() < rhs.MatchingNodes.Len() {
var allResults = list.New()
for el := rhs.MatchingNodes.Front(); el != nil; el = el.Next() {
seqNode := el.Value.(*CandidateNode)
if seqNode.Kind != SequenceNode {
continue
}
result, err := traverseNodesWithArrayIndices(lhs, seqNode.Content, prefs)
if err != nil {
return Context{}, err
}
allResults.PushBackList(result.MatchingNodes)
}
return context.ChildContext(allResults), nil
}
var indicesToTraverse = rhs.MatchingNodes.Front().Value.(*CandidateNode).Content
log.Debugf("indicesToTraverse %v", len(indicesToTraverse))

View File

@ -132,6 +132,38 @@ var badTraversePathOperatorScenarios = []expressionScenario{
}
var traversePathOperatorScenarios = []expressionScenario{
{
skipDoc: true,
description: "traverse array with streaming indices from keys",
document: `["a","b"]`,
expression: `. as $o | keys[] | $o[.]`,
expected: []string{
"D0, P[0], (!!str)::a\n",
"D0, P[1], (!!str)::b\n",
},
},
{
skipDoc: true,
description: "traverse map with streaming indices from keys",
document: `{x: "a", y: "b"}`,
expression: `. as $o | keys[] | $o[.]`,
expected: []string{
"D0, P[x], (!!str)::a\n",
"D0, P[y], (!!str)::b\n",
},
},
{
skipDoc: true,
description: "traverse longer array with streaming indices from keys",
document: `["a","b","c","d"]`,
expression: `. as $o | keys[] | $o[.]`,
expected: []string{
"D0, P[0], (!!str)::a\n",
"D0, P[1], (!!str)::b\n",
"D0, P[2], (!!str)::c\n",
"D0, P[3], (!!str)::d\n",
},
},
{
skipDoc: true,
description: "strange map with key but no value",