fix(first): return the first value, not key, for a map

The no-filter branch returned Content[0]; for a map that is the first key. Splat to get the first value with its correct path (matches the filtered path and first.md).
This commit is contained in:
max 2026-07-06 03:55:07 +02:00
parent e2f1d5ccf7
commit ccc0b1d660
2 changed files with 22 additions and 2 deletions

View File

@ -8,9 +8,20 @@ func firstOperator(d *dataTreeNavigator, context Context, expressionNode *Expres
for el := context.MatchingNodes.Front(); el != nil; el = el.Next() {
candidate := el.Value.(*CandidateNode)
// If no RHS expression is provided, simply return the first entry in candidate.Content
// If no RHS expression is provided, simply return the first entry
if expressionNode == nil || expressionNode.RHS == nil {
if len(candidate.Content) > 0 {
if candidate.Kind == MappingNode {
// candidate.Content is [key0, value0, ...]; the first entry of a
// map is its first VALUE, not the key (first.md: "first matching
// value in a map"). Splat to get map values with correct paths.
splatted, err := splat(context.SingleChildContext(candidate), traversePreferences{})
if err != nil {
return Context{}, err
}
if splatted.MatchingNodes.Front() != nil {
results.PushBack(splatted.MatchingNodes.Front().Value.(*CandidateNode))
}
} else if len(candidate.Content) > 0 {
results.PushBack(candidate.Content[0])
}
continue

View File

@ -176,6 +176,15 @@ var firstOperatorScenarios = []expressionScenario{
expression: `first`,
expected: []string{},
},
{
description: "First value with no filter from a map",
skipDoc: true,
document: "{x: {a: banana}, y: {a: cat}}",
expression: `first`,
expected: []string{
"D0, P[x], (!!map)::{a: banana}\n",
},
},
}
func TestFirstOperatorScenarios(t *testing.T) {