From c92c3ebebbdd67f715161810f97ecfbcaa47921e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Fri, 13 Feb 2026 16:26:51 +0100 Subject: [PATCH] Fix last() predicate on grouped expressions When evaluating expressions like (//author)[last()], the last() function was counting sibling nodes per-parent instead of counting the total nodes in the grouped result set. This caused incorrect matches when group members came from different parent nodes. The fix ensures that b.firstInput is always set to the groupQuery in the nodeGroup case, and adds handling for *groupQuery in the lastFuncQuery conversion, using a cloned query to avoid consuming the shared input. Co-Authored-By: Claude Opus 4.6 --- build.go | 6 +++--- xpath_predicate_test.go | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/build.go b/build.go index a93c8eb..af2b1d2 100644 --- a/build.go +++ b/build.go @@ -208,6 +208,8 @@ func (b *builder) processFilter(root *filterNode, flags flag, props *builderProp switch qyFunc.Input.(type) { case *filterQuery: cond = &lastFuncQuery{Input: qyFunc.Input} + case *groupQuery: + cond = &lastFuncQuery{Input: qyFunc.Input.Clone()} } } } @@ -689,9 +691,7 @@ func (b *builder) processNode(root node, flags flag, props *builderProp) (q quer return } q = &groupQuery{Input: q} - if b.firstInput == nil { - b.firstInput = q - } + b.firstInput = q } b.parseDepth-- return diff --git a/xpath_predicate_test.go b/xpath_predicate_test.go index 30126e1..dd48d37 100644 --- a/xpath_predicate_test.go +++ b/xpath_predicate_test.go @@ -27,7 +27,11 @@ func TestPositions(t *testing.T) { test_xpath_elements(t, employee_example, `//employee[last()]`, 13) test_xpath_elements(t, employee_example, `//employee[position() = last()]`, 13) test_xpath_elements(t, book_example, `//book[@category = "web"][2]`, 25) + test_xpath_elements(t, book_example, `//book[@category = "web"][last()]`, 25) test_xpath_elements(t, book_example, `(//book[@category = "web"])[2]`, 25) + test_xpath_elements(t, book_example, `(//book[@category = "web"])[last()]`, 25) + test_xpath_elements(t, employee_example, `(//employee)[last()]`, 13) + test_xpath_elements(t, book_example, `(//author)[last()]`, 27) } func TestPredicates(t *testing.T) {