From 874d54a63a4ecc20429c6a9aa55b9e710b81ffb3 Mon Sep 17 00:00:00 2001 From: pascal Date: Sun, 28 Jun 2026 12:35:37 +0200 Subject: [PATCH] Remove union wrapping for single-terms search-benchmark-game shows TOP_100_COUNT regression on queries tagged intersection_union. The regression came from allowing single-term boolean unions to become TermUnion for Block-WAND. https://github.com/quickwit-oss/tantivy/pull/2915 When such a scorer is used as the optional side of RequiredOptionalScorer, boxing converted the lone term into BufferedUnionScorer. Keep the TermUnion representation available for pruning collection, but unwrap one-term unions when boxing or doing non-pruning iteration. --- src/query/boolean_query/boolean_weight.rs | 38 ++++++++++++++++------- 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/src/query/boolean_query/boolean_weight.rs b/src/query/boolean_query/boolean_weight.rs index f62cffb57..baef640db 100644 --- a/src/query/boolean_query/boolean_weight.rs +++ b/src/query/boolean_query/boolean_weight.rs @@ -91,10 +91,14 @@ fn into_box_scorer( num_docs: u32, ) -> Box { match scorer { - SpecializedScorer::TermUnion(term_scorers) => { - let union_scorer = - BufferedUnionScorer::build(term_scorers, score_combiner_fn, num_docs); - Box::new(union_scorer) + SpecializedScorer::TermUnion(mut term_scorers) => { + if term_scorers.len() == 1 { + Box::new(term_scorers.pop().unwrap()) + } else { + let union_scorer = + BufferedUnionScorer::build(term_scorers, score_combiner_fn, num_docs); + Box::new(union_scorer) + } } SpecializedScorer::TermIntersection(term_scorers) => { let boxed_scorers: Vec> = term_scorers @@ -504,10 +508,15 @@ impl Weight for BooleanWeight { - let mut union_scorer = - BufferedUnionScorer::build(term_scorers, &self.score_combiner_fn, num_docs); - for_each_scorer(&mut union_scorer, callback); + SpecializedScorer::TermUnion(mut term_scorers) => { + if term_scorers.len() == 1 { + let mut term_scorer = term_scorers.pop().unwrap(); + for_each_scorer(&mut term_scorer, callback); + } else { + let mut union_scorer = + BufferedUnionScorer::build(term_scorers, &self.score_combiner_fn, num_docs); + for_each_scorer(&mut union_scorer, callback); + } } SpecializedScorer::TermIntersection(term_scorers) => { let boxed_scorers: Vec> = term_scorers @@ -534,10 +543,15 @@ impl Weight for BooleanWeight { - let mut union_scorer = - BufferedUnionScorer::build(term_scorers, &self.score_combiner_fn, num_docs); - for_each_docset_buffered(&mut union_scorer, &mut buffer, callback); + SpecializedScorer::TermUnion(mut term_scorers) => { + if term_scorers.len() == 1 { + let mut term_scorer = term_scorers.pop().unwrap(); + for_each_docset_buffered(&mut term_scorer, &mut buffer, callback); + } else { + let mut union_scorer = + BufferedUnionScorer::build(term_scorers, &self.score_combiner_fn, num_docs); + for_each_docset_buffered(&mut union_scorer, &mut buffer, callback); + } } SpecializedScorer::TermIntersection(term_scorers) => { let boxed_scorers: Vec> = term_scorers