A sloppy PhraseQuery in which two different terms each repeat (for example "a b a b"~1) misses documents of a segment, exact-phrase matches included, when the segment's first candidate document has two of those terms stacked at one position, because SloppyPhraseMatcher builds its repetition groups once, from that document, by position instead of by term
Finding LFV-187Area: search / phrase queries (SloppyPhraseMatcher repetition groups: gatherRptGroups, non-multi-term branch)Priority: lowImpact: silent wrong resultsNovelty: known upstream
AI-assisted finding, confirmed by automated checks, not yet triaged by a human Lucene committer. This site files nothing upstream.
What it shows
You index text into a field whose analysis can put two words at one position (for example an index-time synonym) and search with a sloppy phrase such as "a b a b"~1, in which two different words each repeat; you expect every document within the slop, and certainly every document that holds the exact phrase, to match, whatever other documents share its segment. SloppyPhraseMatcher works out which phrase positions repeat each other only once per segment, on the first candidate document, and groups them by equal position in that document rather than by term, so when that document has a and b stacked at one position both words land in one group and the matcher then asks for more occurrences than exist. That document and later documents of the segment are missed with no error: in the two-document repro the plain document 'a b a b' matches with slop 0 but not with slop 1, while the same document indexed alone matches with slop 1.
Expected vs actual
- Expected: You expect every document within the slop, and certainly every document that holds the exact phrase, to match, whatever other documents share its segment.
- Actual: While the reported behaviour is present the test fails; the failure message states expected against actual.
Files
Run it
- Tested on Lucene main e1faeba08bde (2026-11-02): still reproduces.
- Tested on Lucene main ea57360912ed (2026-10-24): reproduced.
- How to read the outcome: a failing test is the finding reproducing (the failure message states expected against actual).
- Seeds: a rerun with the same seed replays the same random choices. Assertions off (-da -dsa -Dtests.asserts=false) is the production-like setting.
# Lucene checkout at the tested commit, built with: ./gradlew --no-daemon assemble (JDK 25)
LUCENE=/path/to/lucene; V=12.0.0-SNAPSHOT
CP=$LUCENE/lucene/core/build/libs/lucene-core-$V.jar
CP=$CP:$LUCENE/lucene/codecs/build/libs/lucene-codecs-$V.jar
CP=$CP:$LUCENE/lucene/test-framework/build/libs/lucene-test-framework-$V.jar
CP=$CP:randomizedtesting-runner-2.9.2.jar:junit-4.13.2.jar:hamcrest-3.0.jar # test-framework dependencies (Maven Central)
CP=$CP:junit-platform-console-standalone-6.1.3.jar # JUnit Platform console launcher (Maven Central)
javac -d classes -cp "$CP" $(find src -name '*.java')
# run 1: assertions on, seed DEADBEEF
java -ea -esa --add-modules jdk.incubator.vector --enable-native-access=ALL-UNNAMED -Dtests.seed=DEADBEEF \
-cp "classes:$CP" org.junit.platform.console.ConsoleLauncher \
execute --select-class org.apache.lucene.search.TestSloppyPhraseRepeatGroupsFirstDoc
Gradle alternative: copy the test into the matching module's src/test/ tree of the checkout and run ./gradlew -p lucene/<module> test --tests <class> -Ptests.seed=<seed> (add -Ptests.asserts=false for a run without assertions).
Feedback
Open an issue in lucene-ai-bug-hunt. Please do not open issues or pull requests in apache/lucene about this finding unless a Lucene committer agrees it should be filed.
Snapshot of 2026-11-04. AI-assisted; not yet triaged by a human Lucene committer. Feedback: open an issue in lucene-ai-bug-hunt.