From 90c974bc3854923fe179ac765bcf54a5e0274856 Mon Sep 17 00:00:00 2001 From: Binlong Gao Date: Thu, 26 Mar 2026 14:50:55 +0800 Subject: [PATCH] Optimize TopGroups.merge() maxScore computation for relevance sorting Signed-off-by: Binlong Gao --- lucene/CHANGES.txt | 2 ++ .../apache/lucene/search/grouping/TopGroups.java | 15 +++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index ea86c4421975..8118c53c5fb8 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -258,6 +258,8 @@ Optimizations * GITHUB#15779: Improve BytesRefHash.add performance by optimize rehash operation (tyronecai) +* GITHUB#15779: Optimize TopGroups.merge() maxScore computation for relevance sorting (Binlong Gao) + Bug Fixes --------------------- * GITHUB#15754: Fix HTMLStripCharFilter to prevent tags from incorrectly consuming subsequent diff --git a/lucene/grouping/src/java/org/apache/lucene/search/grouping/TopGroups.java b/lucene/grouping/src/java/org/apache/lucene/search/grouping/TopGroups.java index 0a1c607ab78b..3b75ccdeaa25 100644 --- a/lucene/grouping/src/java/org/apache/lucene/search/grouping/TopGroups.java +++ b/lucene/grouping/src/java/org/apache/lucene/search/grouping/TopGroups.java @@ -153,7 +153,8 @@ public static TopGroups merge( final GroupDocs[] mergedGroupDocs = new GroupDocs[numGroups]; final TopDocs[] shardTopDocs; - if (docSort.equals(Sort.RELEVANCE)) { + final boolean sortByRelevance = docSort.equals(Sort.RELEVANCE); + if (sortByRelevance) { shardTopDocs = new TopDocs[shardGroups.length]; } else { shardTopDocs = new TopFieldDocs[shardGroups.length]; @@ -187,7 +188,7 @@ public static TopGroups merge( } */ - if (docSort.equals(Sort.RELEVANCE)) { + if (sortByRelevance) { shardTopDocs[shardIDX] = new TopDocs(shardGroupDocs.totalHits(), shardGroupDocs.scoreDocs()); } else { @@ -200,15 +201,21 @@ public static TopGroups merge( shardTopDocs[shardIDX].scoreDocs[i].shardIndex = shardIDX; } - maxScore = nonNANmax(maxScore, shardGroupDocs.maxScore()); + if (!sortByRelevance) { + maxScore = nonNANmax(maxScore, shardGroupDocs.maxScore()); + } assert shardGroupDocs.totalHits().relation() == Relation.EQUAL_TO; totalHits += shardGroupDocs.totalHits().value(); scoreSum += shardGroupDocs.score(); } final TopDocs mergedTopDocs; - if (docSort.equals(Sort.RELEVANCE)) { + if (sortByRelevance) { mergedTopDocs = TopDocs.merge(docOffset + docTopN, shardTopDocs); + // When sorting by relevance, the highest-scoring doc is first, so we can + // derive maxScore directly instead of accumulating across shards. + maxScore = + mergedTopDocs.scoreDocs.length == 0 ? Float.NaN : mergedTopDocs.scoreDocs[0].score; } else { mergedTopDocs = TopDocs.merge(docSort, docOffset + docTopN, (TopFieldDocs[]) shardTopDocs); }