All findings · Lucene finding · LFV-776 · facet-range-getquery-never-rewrites-values-source

DoubleRange/LongRange#getQuery never rewrites its values source, so DoubleValuesSource.fromQuery throws "must be rewritten"

A range filter built with DoubleRange#getQuery(null, DoubleValuesSource.fromQuery(q)) throws UnsupportedOperationException: This DoubleValuesSource must be rewritten on the first search, because the query never calls rewrite(searcher) on its values source, as core's javadoc says Queries should. Seen on 10 releases from 7.2.0 to 10.6.0 and on main; field-based sources are not affected.

Summary

Priority
Low, rank 624The experimental DoubleRange/LongRange#getQuery fails loudly only for values sources that need rewriting (not the documented field or expression sources), and passing source.rewrite(searcher) works around it.
Impact
crash
Since
7.2.0 (LUCENE-7736, 2017)introduced 2017-11-15 · about 8 years
Affected
7.2.0 to 10.6.0 and main (10 releases run; the others read or inferred)
Status
documented
Found by
a cross-model critique of a test harness named it; confirmed by execution

AI-assisted page: this finding was first signalled by code reading or testing and then checked with AI tools; the checks it went through, and what each found, are under Prior reports, history and checks. Not yet triaged by a human. Snapshot of 2026-11-06.

The finding in 30 seconds

The range query skips that rewrite step: its createWeight hands the caller's source straight to getValues. A source that needs the searcher then either throws or computes its values without it.

Background: values sources and rewrite

  1. A DoubleValuesSource gives one number per document: a field's value, an expression, or a query's score (DoubleValuesSource.fromQuery).
  2. Some sources need the searcher before they can produce values. Core's protocol is: call source.rewrite(searcher), then getValues on the result. A query-score source throws if you skip the first step.
  3. Core's javadoc says Queries that use a DoubleValuesSource should call rewrite() in createWeight, where they get the searcher. FunctionMatchQuery does.
  4. DoubleRange#getQuery(fastMatch, source), and its LongRange twin, return the query a range facet uses to filter or drill down.
Summary card of the one-document example on main 645d4fe14a20 (f = "a", score = DoubleValuesSource.fromQuery(f:a), range score > 0): following the rule, as FunctionMatchQuery does (rewrite(searcher), then getValues), gives count 1; DoubleRange.ValueSourceQuery calls getValues without rewrite and throws 'This DoubleValuesSource must be rewritten' on every search; a legacy QueryValueSource view on a ClassicSimilarity searcher counts 0 instead of 1 with no error. Below: the code lines at DoubleRange.java:190 and LongRange.java:177, the rule at DoubleValuesSource.java:97-98, and the fix sketch that rewrites once at the start of createWeight.
The one-document example step by step: the rewrite rule, where the range query's createWeight skips it, the silent QueryValueSource variant, and a fix sketch. Select the still to open it at full size. Generated with AI, checked against the code.

The finding, step by step

  1. Index one document whose text field f is "a". Build score = DoubleValuesSource.fromQuery(new TermQuery(f:a)), a source whose value for each document is its score for that query (here 0.1308, BM25), and the range (0, +inf].
  2. Count range.getQuery(null, score). You expect 1: the document scores above 0, and FunctionMatchQuery over the same source counts 1.
  3. Instead the search throws UnsupportedOperationException: This DoubleValuesSource must be rewritten, with assertions on or off.
  4. Why: a query-score source works only after rewrite(searcher). Core's javadoc says Queries should call it in createWeight, and FunctionMatchQuery does; the range query calls getValues on the source exactly as it was passed in.
  5. The LongRange twin (over score.toLongValuesSource()) and IndexReaderFunctions sources fail the same way. A legacy QueryValueSource view does not throw but ignores the searcher: on a ClassicSimilarity searcher the document scores 1.0, the view computes 0.1308, and the range [1.0, 1.0] matches 0 documents instead of 1.

Who is affected

Applications that filter or drill down with DoubleRange/LongRange#getQuery over a source that needs rewriting: query scores (fromQuery), IndexReaderFunctions statistics, or legacy ValueSource views. Field and expression-of-fields sources, the documented use, are unaffected. Public API, default settings; it fails on the first search. Workaround: pass source.rewrite(searcher). Rare.

Mechanism, terms and numbers668 words

DoubleRange#getQuery(Query, DoubleValuesSource) and LongRange#getQuery(Query, LongValuesSource) return a private ValueSourceQuery whose rewrite rewrites only the fast-match query and whose createWeight calls valueSource.getValues(context, null) and valueSource.isCacheable(ctx) on the source exactly as given (DoubleRange.java:156-164, :190, :210; LongRange.java:143-150, :177, :197 on main 645d4fe14a20). Nothing in either class calls valueSource.rewrite(searcher), although DoubleValuesSource says "Queries that use DoubleValuesSource objects should call rewrite() during Query#createWeight" (DoubleValuesSource.java:97-99) and FunctionMatchQuery does (FunctionMatchQuery.java:88). So DoubleValuesSource.fromQuery (and its toLongValuesSource() view) and IndexReaderFunctions sources throw "must be rewritten" on every search that reaches a segment's values, and a legacy QueryValueSource view silently scores with its own default-Similarity searcher. Introduced in 7.2.0 by LUCENE-7736, which added the rewrite rule and updated FunctionMatchQuery and FunctionScoreQuery but not these queries; it was executed on 10 releases from 7.2.0 to 10.6.0 and on main, the other release tags read the same, and 8.8.2-8.11.3 (not tagged in the history clone) are inferred. Field and expression-of-fields sources, the documented use, are unaffected, and source.rewrite(searcher) works around it. Intent is contested: the facet module documents that its Multi* wrappers never call rewrite, and the wrapped overload builds this same query, so the doc offers a javadoc fix as well as a code fix. A fix that rewrites once in createWeight passes the repro and the facet module's checks; it must land with the fix for LFV-770, or sortable-long sources regress.

The mechanism (main 645d4fe14a20)

  1. The query never rewrites its source. getQuery(Query, DoubleValuesSource) returns new ValueSourceQuery(this, fastMatchQuery, valueSource) (DoubleRange.java:335-337). Its rewrite rewrites only fastMatchQuery (:156-164). Its createWeight (:166-213):
   final DoubleValues values = valueSource.getValues(context, null);   // :190, the source as given
   ...
   public boolean isCacheable(LeafReaderContext ctx) {
     return valueSource.isCacheable(ctx);                               // :210
   }

LongRange.java is the same (getQuery :322, rewrite :143-150, getValues :177, isCacheable :197).

  1. Core's rule. The class javadoc: "clients should call rewrite(IndexSearcher) against the top-level searcher, and then call getValues on the resulting DoubleValuesSource" (DoubleValuesSource.java:34-36; LongValuesSource.java:30-32). And on rewrite: "Queries that use DoubleValuesSource objects should call rewrite() during Query#createWeight(IndexSearcher, ScoreMode, float) rather than during Query#rewrite(IndexSearcher) to avoid IndexReader reference leakage" (:97-99). FunctionMatchQuery.java:88 does DoubleValuesSource vs = source.rewrite(searcher); in createWeight; so do FunctionScoreQuery.java:121 and the sandbox CoveringQuery.java:172 (for its LongValuesSource), while RescoreTopNQuery.java:68-69 rewrites in Query#rewrite, the place the rule says to avoid. The one other Query that holds a values source and skips the step, spatial-extras' private PointVectorStrategy.DistanceRangeQuery (PointVectorStrategy.java:277), only ever gets the strategy's own DistanceValueSource, whose rewrite returns this. The Query sentence is on DoubleValuesSource#rewrite only: LongValuesSource#rewrite (LongValuesSource.java:64-70) lacks it, so the LongRange half rests on the class protocol and the parallel design.
  2. Sources that throw. fromQuery returns a source whose getValues is throw new UnsupportedOperationException("This DoubleValuesSource must be rewritten") (DoubleValuesSource.java:741, :768); its rewrite builds a searcher-bound source (:777-779). Its toLongValuesSource() view delegates to it. The index-statistic sources of IndexReaderFunctions, such as maxDoc() and sumTotalTermFreq, throw "IndexReaderFunction must be rewritten before use" (IndexReaderFunctions.java:320, :99); its per-document termFreq does not.
  3. The silent legacy path. ValueSource#asDoubleValuesSource() returns new WrappedDoubleValuesSource(this, null) (ValueSource.java:184-186), so its values context holds a null searcher until rewrite(searcher) makes a copy that holds one (:199-200, :244-246). QueryValueSource with a null searcher builds new IndexSearcher(...) over the top-level reader (QueryValueSource.java:114-125), with the default Similarity.

The numbers (one document, f = "a")

  • range.getQuery(null, DoubleValuesSource.fromQuery(f:a)), range (0, +inf]: throws "must be rewritten" at DoubleRange.java:190; FunctionMatchQuery and getQuery(null, score.rewrite(searcher)) count 1. The document's BM25 score is 0.1308.
  • LongRange.getQuery(null, score.toLongValuesSource()): throws at LongRange.java:177. getQuery(null, IndexReaderFunctions.maxDoc()): "IndexReaderFunction must be rewritten before use".
  • ClassicSimilarity searcher, new QueryValueSource(f:a, 0f).asDoubleValuesSource(), range [1.0, 1.0] (the top Classic score): the unrewritten view computes 0.1308 (BM25), so the range query counts 0; FunctionMatchQuery and the rewritten view count 1. (The hunt's 5-document test: 0 instead of 3.)
  • Control: DoubleValuesSource.fromLongField("n") counts 1; its rewrite returns itself.
Exposure and likelihood315 words
  • Who can hit it. Applications that build a range filter or drill-down with DoubleRange#getQuery(Query, DoubleValuesSource) or LongRange#getQuery(Query, LongValuesSource) (both classes @lucene.experimental) over a source that needs rewriting: DoubleValuesSource.fromQuery or its toLongValuesSource() view, an IndexReaderFunctions statistic, or a searcher-dependent legacy ValueSource exposed with asDoubleValuesSource().
  • Not hit. Field sources and expressions over fields: the facet demo drills down with range.getQuery(boundingBox, vs) over a haversine expression of two fields (DistanceFacetsExample.java:239-243).
  • What it takes. Public API, default codec, IndexWriterConfig and searcher, one document. The silent variant also needs a non-default Similarity on the searcher.
  • Effect. Mostly a deterministic UnsupportedOperationException, whose message names the missing step, on every search that reaches the values (DoubleRange.java:190): any segment, or with a fast-match query any segment where that query matches a document. The silent variant gives a wrong filter result. No corruption or hang was seen in the cases run.
  • How likely. Rare. An application that also counts such ranges must already pre-rewrite the source for the range counters, which throw on it too, and would often reuse that source in the drill-down. The recorded searches found no public report since 7.2.0 (2017); their gaps are under Prior reports.
  • For searcher-bound sources the workaround binds the query to one searcher. getQuery(fastMatch, source.rewrite(searcher)) works. The result is "specialised for the given IndexSearcher" (DoubleValuesSource.java:92); for a source whose rewrite captures the searcher, such as fromQuery (its rewrite builds a Weight, :777-779), the query has to be rebuilt after a reopen; core's rule exists "to avoid IndexReader reference leakage". Field sources, whose rewrite returns this, have no such cost.
  • Out of scope: score-dependent sources. The query passes null scores to getValues today and with the fix (DoubleRange.java:190), so DoubleValuesSource.SCORES and similar sources ("callers must not pass null", DoubleValuesSource.java:350-354) are unsupported either way; neither the bug nor the fixes are about them.

Minimal repro

One JUnit test method in lucene/facet, public API: index one document with f = "a", then count DoubleRange#getQuery(null, DoubleValuesSource.fromQuery(f:a)) for the range (0, +inf] with a plain IndexSearcher. Deterministic and single-threaded; the same with assertions on and off.

TestDoubleRangeGetQueryRewritesSource.java (lucene/facet, one test)54 lines
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.lucene.facet.range;

import java.util.List;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.DoubleValuesSource;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.store.ByteBuffersDirectory;
import org.apache.lucene.store.Directory;
import org.apache.lucene.tests.util.LuceneTestCase;

public class TestDoubleRangeGetQueryRewritesSource extends LuceneTestCase {

  public void testGetQueryOverQueryScoreSource() throws Exception {
    try (Directory dir = new ByteBuffersDirectory()) {
      try (IndexWriter w = new IndexWriter(dir, new IndexWriterConfig())) {
        w.addDocument(List.of(new TextField("f", "a", Field.Store.NO)));
      }
      try (DirectoryReader reader = DirectoryReader.open(dir)) {
        DoubleValuesSource score = DoubleValuesSource.fromQuery(new TermQuery(new Term("f", "a")));
        DoubleRange range = new DoubleRange("score > 0", 0, false, Double.POSITIVE_INFINITY, true);
        int count;
        try {
          count = new IndexSearcher(reader).count(range.getQuery(null, score));
        } catch (UnsupportedOperationException e) {
          // Expected count 1 (the doc scores > 0); actual: the unrewritten source throws
          throw new AssertionError("expected count 1, got " + e, e);
        }
        assertEquals(1, count);
      }
    }
  }
}
Command
cp $REPRO/TestDoubleRangeGetQueryRewritesSource.java $LUCENE/lucene/facet/src/test/org/apache/lucene/facet/range/
cd $LUCENE && ./gradlew :lucene:facet:test --tests TestDoubleRangeGetQueryRewritesSource -Ptests.seed=DEADBEEF -Ptests.asserts=true
# also with -Ptests.asserts=false
Expected
Count 1: the one document scores above 0, so the range (0, +inf] matches it (FunctionMatchQuery over the same source counts 1).
Observed
UnsupportedOperationException: This DoubleValuesSource must be rewritten, thrown from DoubleRange.java:190 on the first search.

All files of this repro: repro/LFV-776/

More files, environment, full output255 words

The test catches the UnsupportedOperationException only to turn it into an assertion that states expected versus actual, with the exception as the cause. The body has one document, no threads and no random calls (ByteBuffersDirectory, default IndexWriterConfig, new IndexSearcher(reader)). The seed still drives the test framework's per-run settings; the result was the same in every recorded run (seeds DEADBEEF and 5EED1234, gradle and the standalone runner). The file carries the Apache license header; with the fix applied, :lucene:facet:check passes with it included unchanged, so it can go into a PR as is.

Left out on purpose: the LongRange view, IndexReaderFunctions, the silent QueryValueSource variant and the FunctionMatchQuery comparison; they are in the explanation above.

Environment

lucenemain 645d4fe14a20 (11.0.0-SNAPSHOT); also the 10.6.0 release jars
jdkOpenJDK 25.0.3 (build 25.0.3+9-LTS)
osLinux, aarch64 (the only platform run; the code path is pure Java)
Command (environment)
cp $REPRO/TestDoubleRangeGetQueryRewritesSource.java $LUCENE/lucene/facet/src/test/org/apache/lucene/facet/range/
cd $LUCENE && ./gradlew :lucene:facet:test --tests TestDoubleRangeGetQueryRewritesSource -Ptests.seed=DEADBEEF -Ptests.asserts=true
# also with -Ptests.asserts=false

Observed output

TestDoubleRangeGetQueryRewritesSource > testGetQueryOverQueryScoreSource FAILED
    java.lang.AssertionError: expected count 1, got java.lang.UnsupportedOperationException: This DoubleValuesSource must be rewritten
        at __randomizedtesting.SeedInfo.seed([DEADBEEF:31099228EEA9893F]:0)
        at org.apache.lucene.facet.range.TestDoubleRangeGetQueryRewritesSource.testGetQueryOverQueryScoreSource(TestDoubleRangeGetQueryRewritesSource.java:48)

        Caused by:
        java.lang.UnsupportedOperationException: This DoubleValuesSource must be rewritten
            at org.apache.lucene.search.DoubleValuesSource$QueryDoubleValuesSource.getValues(DoubleValuesSource.java:768)
            at org.apache.lucene.facet.range.DoubleRange$ValueSourceQuery$1.scorerSupplier(DoubleRange.java:190)
            ...
            at org.apache.lucene.search.IndexSearcher.count(IndexSearcher.java:528)
:lucene:facet:test (FAILURE): 1 test, 1  failure

# the same with -Ptests.asserts=false; the same on the 10.6.0 release jars (assertions on and off)
# with the fix: :lucene:facet:test (SUCCESS): 1 test (assertions on and off)
# abridged: gradle prints the core frames with an org.apache.lucene.core@11.0.0-SNAPSHOT/ module prefix, and some frames are left out (...)

Why it looks unintended

  • Core says Queries using a DoubleValuesSource "should call rewrite() during Query#createWeight" (DoubleValuesSource.java:97-99); this query has the searcher there and does not.
  • The getQuery javadoc limits no source; the other Queries taking a caller's source rewrite it (FunctionMatchQuery, FunctionScoreQuery, CoveringQuery, RescoreTopNQuery).
  • Contested: the Multi* wrappers document that rewrite is never called, and build this same query. Adjudication: a defect, medium confidence.
The full argument341 words

This is a conflict between two written contracts: core's rule for Queries and the facet module's documented no-rewrite design for its Multi* wrappers, which the wrapped getQuery overload shares at run time. The adjudication rated it a defect (medium confidence), the devil's-advocate verifier called intent "uncertain", and a cross-model fact check of this doc rated the case for "intended" strong, though not decisive.

  • Contract. DoubleValuesSource.java:97-99 (above); the client protocol at :34-36 and LongValuesSource.java:30-32. ValueSourceQuery is an ordinary Query that receives the IndexSearcher in createWeight. The Query sentence is on DoubleValuesSource#rewrite only; the LongRange half rests on the LongValuesSource class protocol and the parallel design (the sandbox CoveringQuery rewrites its LongValuesSource in createWeight, CoveringQuery.java:172).
  • The getQuery javadoc (DoubleRange.java:323-334, LongRange.java:310-321): "Create a Query that matches documents in this range ... If the value source is static, eg an indexed numeric field, it may be faster to use PointRangeQuery". No restriction on the source; non-static sources are implied.
  • History. The getValues(context, null) line is from LUCENE-7610 (master 713b65d1dcc, branch_6x a238610bab1, both committed 2017-01-07; first release 6.4.0, from the branch_6x commit), before any rewrite step existed. LUCENE-7736 (#8787; 03afeb7766a on branch_7x, 2a4dd499bbd on master, committed 2017-11-15) added rewrite() and the rule, added the call to FunctionMatchQuery and FunctionScoreQuery, and in the facet module changed only a test source (rewrite() { return this; }). In its review Adrien Grand wrote "I think you forgot to update FunctionMatchQuery to make it rewrite the values source?", and Alan Woodward answered "I did, thanks for picking it up"; CoveringQuery was fixed in a follow-up commit (3f0b240853d). The facet range queries are not mentioned.
  • Other Queries. Every other class in Lucene that extends Query and takes a caller-supplied DoubleValuesSource or LongValuesSource rewrites it: FunctionMatchQuery.java:88, FunctionScoreQuery.java:121 and CoveringQuery.java:172 in createWeight, RescoreTopNQuery.java:68-69 in Query#rewrite. The only other one that skips the step, spatial-extras' private PointVectorStrategy.DistanceRangeQuery, only ever gets its own DistanceValueSource, whose rewrite returns this (PointVectorStrategy.java:197-198, :277; DistanceValueSource.java:96-99).
The devil's-advocate check322 words

The strongest case for "intended" (built by the devil's-advocate verifier and strengthened by the cross-model fact check): the facet module documents a no-rewrite design. MultiDoubleValuesSource.java:41-47 says "DoubleValuesSource#rewrite(IndexSearcher) will also never be called. The user should be aware of this if using a DoubleValuesSource that relies on rewriting or scores. The faceting use-cases don't call rewrite or provide scores, which is why this simplification was made." (MultiLongValuesSource.java:43-49 likewise.) In PR #543 (LUCENE-10245) Alan Woodward wrote "I don't think we need needsScores() or rewrite() for facets", and Greg Miller kept only isCacheable "for the query implementations in LongRange and DoubleRange", so that discussion had the range queries in view. The LUCENE-10245 CHANGES entry says "DoubleRange and LongRange also support creating queries from these multi-valued sources" (lucene/CHANGES.txt:4438-4442). The direct overload builds a query equal to the documented fromSingleValued path's, so the two cannot be told apart at run time, and the code fix changes behaviour on that documented route too. The range counters do not rewrite either, the upstream test picks either overload at random (TestRangeFacetCounts.java:1703-1709), and a caller-side rewrite works.

Why the adjudication and the devil's-advocate verifier still read the direct overloads as a defect: the caveat sits on the fromSingleValued wrappers, not on getQuery(Query, DoubleValuesSource), whose javadoc states no restriction; the PR #543 exchange decided which methods the new Multi* classes have (they have no rewrite()), not that this Query may skip DoubleValuesSource#rewrite; the counters have no searcher, while this Query does, and core's rule is written for Queries; and in LUCENE-7736 Alan Woodward wrote "the only callers of rewrite are internal lucene classes" (while discussing equals() across readers), which suggests users were not expected to pre-rewrite. Verdict: a defect (adjudication, medium confidence), with intent contested: the devil's-advocate verifier called the case "not decisive", and the cross-model fact check rated it strong and would file this as a neutral design or documentation question.

Suggested fix

Recommended Rewrite once in createWeight, as FunctionMatchQuery does

If the maintainers want a behaviour change: rewrite the source once at the start of ValueSourceQuery.createWeight, use the result for getValues and isCacheable in both classes, as FunctionMatchQuery does, and update the Multi*ValuesSource note. The repro and :lucene:facet:check pass. Land it with LFV-770's fix. Otherwise, document that the source must be pre-rewritten.

     public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost)
         throws IOException {
+      final DoubleValuesSource rewrittenSource = valueSource.rewrite(searcher);
       ...
-          final DoubleValues values = valueSource.getValues(context, null);
+          final DoubleValues values = rewrittenSource.getValues(context, null);
       ...
         public boolean isCacheable(LeafReaderContext ctx) {
-          return valueSource.isCacheable(ctx);
+          return rewrittenSource.isCacheable(ctx);
         }
All options and trade-offs2 options · 635 words

One option was built and run in a scratch worktree of 645d4fe14a20; the other is not built. Which one fits depends on which contract the maintainers want the direct overloads to follow (see Why it is a bug); a maintainer could reasonably choose either, and the code fix is not the only reasonable answer.

Rewrite once in createWeight, as FunctionMatchQuery does (validated by the checks) (recommended)

The finder's code fix plus a javadoc update (4 files, +19/-10). Its two-line code comment was corrected on 2026-10-30: it now credits the Query sentence to DoubleValuesSource#rewrite only, and in LongRange cites the LongValuesSource class protocol. In DoubleRange.ValueSourceQuery.createWeight (and the same in LongRange with LongValuesSource), rewrite first and use the rewritten source for getValues and isCacheable. In MultiDoubleValuesSource / MultiLongValuesSource, "will also never be called" becomes "will also never be called when counting facets (the range queries from DoubleRange#getQuery do rewrite an unwrapped single-valued source)".

     public Weight createWeight(IndexSearcher searcher, ScoreMode scoreMode, float boost)
         throws IOException {
+      final DoubleValuesSource rewrittenSource = valueSource.rewrite(searcher);
       ...
-          final DoubleValues values = valueSource.getValues(context, null);
+          final DoubleValues values = rewrittenSource.getValues(context, null);
       ...
         public boolean isCacheable(LeafReaderContext ctx) {
-          return valueSource.isCacheable(ctx);
+          return rewrittenSource.isCacheable(ctx);
         }

Trade-offs

  • Must land with LFV-770. toSortableLongDoubleValuesSource().rewrite() returns the plain cast adapter (DoubleValuesSource.java:193-195), which today never matters here. Probe: one document with value 2.5, LongRange over the sortable longs of 2.0 and 3.0, source fromDoubleField("v").toSortableLongDoubleValuesSource(): count 1 on main, 0 with this fix alone, 1 with LFV-770's one-line fix added (return inner.rewrite(searcher).toSortableLongDoubleValuesSource();).
  • getQuery(Query, Multi*ValuesSource) unwraps fromSingleValued into the same query, so the fix also changes behaviour on that documented route: it starts rewriting there, against the Multi* note as written today; hence the javadoc update (or keep the two paths apart).
  • One rewrite per createWeight, the same cost FunctionMatchQuery pays. A source the caller already rewrote is rewritten again: for fromQuery's rewritten form that returns the same object (WeightDoubleValuesSource.rewrite returns this), a legacy WrappedDoubleValuesSource makes a new wrapper (ValueSource.java:244-246), and core says implementations "should assume that this will only be called once" (DoubleValuesSource.java:94), so other pre-rewritten sources are not covered; FunctionMatchQuery behaves the same. No benchmark was run.
  • Touches the isCacheable line that LFV-320 (the fast-match query's cacheability is ignored) also changes; coordinate the two.
  • Score-dependent sources stay unsupported: the fixed query still calls getValues(context, null).

What the checks validated

  • Scratch worktree of 645d4fe14a20, gradle, JDK 25: stock + test fails with assertions on and off; + fix: the test passes with assertions on and off.
  • + fix: :lucene:facet:test --tests 'org.apache.lucene.facet.range.*' passes, 37 tests (TestRangeFacetCounts 22, TestDynamicRangeUtil 14, the repro 1); + fix and the test: :lucene:facet:check BUILD SUCCESSFUL (license, format, ecj lint and forbidden-API checks; :lucene:facet:test 307 tests, 7 skipped); :lucene:facet:renderJavadoc passes with no warnings.
  • Earlier, code part shadowed onto the main jars: the finder's 3 tests and the independent reproduction's 7 pass; upstream TestRangeFacetCounts 22 of 22.
  • With LFV-770's fix added (shadowed): the coupling probe and the minimal test pass.
  • After the comment fix (2026-10-30), the new diff in a fresh scratch worktree: :lucene:facet:check passes again (307 tests, 7 skipped; format, ecj and forbidden-API checks) and the repro passes with assertions off; diff stats unchanged.
  • Not run: the full Lucene suite, nightly tests, the sandbox module, benchmarks.

Document that the source must be pre-rewritten

Add to both getQuery(Query, DoubleValuesSource / LongValuesSource) javadocs that a source which needs rewriting must be passed as source.rewrite(searcher).

Trade-offs

No behaviour change, it matches the Multi* note as written, and it has no dependency on LFV-770; but these stay the only Queries that take a caller-supplied values source and skip core's rule, the query is tied to one searcher for searcher-bound sources, and users must know about the silent QueryValueSource case themselves.

Downstream note

Downstream relevance is limited to products that build these range queries over score or index-statistic sources: they get a clear per-query exception, or, with a legacy QueryValueSource view and a custom Similarity, a wrong filter result over their own index.

How it was found

An independent reviewer from another model family (OpenAI GPT-5.6), asked what the hunt unit's facet test harnesses could not see, named this defect: the range query never rewrites its source (DoubleRange.java:190 and the LongRange twin), with a one-document input that should count 1. The finder confirmed it by execution and found the silent QueryValueSource variant.

The method in full172 words

Method: a read-only cross-model critique, confirmed by execution (non-formal), a hunt unit (facet residual algorithms). The unit's bounded-exhaustive harnesses checked range drill-downs against counting, but only through the Multi* overloads with from*Field sources. Asked to find a concrete defect that those harnesses could not see, a reviewer from another model family (OpenAI GPT-5.6, reading only, running nothing) reported one finding at high confidence: the DoubleRange drill-down query never rewrites a rewrite-dependent DoubleValuesSource (DoubleRange.java:156-163, :190), with a one-document input (DoubleValuesSource.fromQuery(new MatchAllDocsQuery()), range [0, 2]), expected count 1 and a derived actual of "This DoubleValuesSource must be rewritten", plus the LongRange twin (LongRange.java:143-150, :177). The harness gap was its explanation of why the harnesses missed it. The finder read ValueSourceQuery, confirmed the defect with a public-API JUnit on the stock main jars (2026-10-27), and added the silent QueryValueSource variant through ValueSource#asDoubleValuesSource() and QueryValueSource. The independent reproduction added IndexReaderFunctions and the fast-match case; this doc's test is a one-method form of the finder's first test.

Prior reports, history and checks

Prior reports

The recorded searches (2026-10-27) found no prior report: over 200 queries over GitHub issues, PRs and commits, downstream projects, four mailing lists, the JIRA archive and the web, plus scans of all 408 open PRs and the full history. Related: #8787 (LUCENE-7736) added the rule; #543 (LUCENE-10245) the Multi* no-rewrite note.

Searches3 searches

Related, not reports of this bug: #8787 / LUCENE-7736 (added rewrite() and the Query rule; facets not mentioned; FunctionMatchQuery caught in review, CoveringQuery in a follow-up commit); #9046 / LUCENE-7998 (added DoubleValuesSource.fromQuery); #543 / LUCENE-10245 (the Multi* no-rewrite design); #8661 / LUCENE-7610 (introduced the direct overloads); #13375 / #13383 and open PR #16847 (the same classes, unrelated changes).

Coverage gaps: StackOverflow pages could not be fetched (their titles are generic facet questions); private security@ reports are not visible.

What was searched, when, and what turned up
WhereWhenHowFound
GitHub (apache/lucene issues, PRs, commits; lucene-solr, Solr, Elasticsearch, OpenSearch)2026-10-27scripted prior-report search 1 (77 queries incl. lists, JIRA and downstream; 3,403 distinct hits, 0 high, 27 medium, all triaged), 62 own GitHub searches, the finder's 51 queries and 13 direct searchesno report; #8787, #9046, #543, #8661, #13375, #13383 are related
Mailing lists and JIRA archive2026-10-27scripted prior-report search 2 (64 queries over dev@, java-user@, issues@, commits@ and JIRA; 0 high, 12 medium)no report
Open and past PRs, commits, history2026-10-27changed files of all 408 open PRs and up to 265 facet and values-source PRs; commits by path on main, branch_10x, branch_10_5, branch_9x; the full git history; CHANGES.txt; 9 web queriesonly #16847 touches the files (mechanical change); no commit ever added a values-source rewrite to ValueSourceQuery

History

Introduced in 7.2.0 by LUCENE-7736 (03afeb7766a, 2017-11-15), which added rewrite() and the rule for Queries but did not update these queries. 6.5.0 to 7.1.0 behave consistently; every executed release from 7.2.0 to 10.6.0, and main, fails.

Versions tested5 rows

LUCENE-7736 "Expose some IndexReader stats via DoubleValuesSources" (Alan Woodward, reviewed by Adrien Grand; JIRA patch, no GitHub PR; issue #8787): 03afeb7766a on branch_7x (first release 7.2.0) and 2a4dd499bbd on master (first release 8.0.0), committed 2017-11-15. DoubleValuesSource.fromQuery came the next day with LUCENE-7998 (c6171019d5f), also in 7.2.0. The getValues(context, null) line is from LUCENE-7610 (master 713b65d1dcc, the line's origin in git blame, first in 7.0.0; branch_6x backport a238610bab1, first in 6.4.0; both committed 2017-01-07). Bisection is at release granularity plus the commit diff; no build at the commit's parent. Releases between the executed ones were scanned statically (no release ever rewrites the source in these classes); 8.8.2-8.11.3 are inferred. All runs used JDK 25, not each release's own JDK. On 7.2.0-7.7.1 and 8.0.0 a QueryValueSource view rewritten earlier by another consumer can hide the silent variant (fixed by LUCENE-8726); the throwing triggers are not affected.

Versions and branches executed, with results
Version or commitVerdictResult
main 645d4fe14a20 affectedthe minimal test fails through gradle, assertions on and off
10.6.0 (release jars) affectedthe minimal test fails, assertions on and off
7.2.0, 7.2.1, 7.7.3, 8.0.0, 8.11.4, 9.0.0, 9.12.3, 10.0.0, 10.5.2, main a9779ef3c193 affected5-check probe: all 5 triggers fail, both controls pass (assertions on and off on 7.2.0, 10.5.2 and main)
6.5.0, 6.6.6, 7.0.0, 7.1.0 before the rewrite APIthe facet query and FunctionMatchQuery agree; neither sees the searcher, and no rewrite API exists
with the fix (main 645d4fe14a20) not affectedthe minimal test passes; the facet.range tests (37) and :lucene:facet:check (307 tests) pass

Independent checks

The independent reproduction (real), the adjudication (confirmed, medium) and the root-cause and intended-lens cross-model reviews (real, reading only) agree; the devil's advocate called intent "uncertain"; a cross-model fact check of this doc rated the case for "intended" strong, not decisive; the refute-lens review gave a procedural "not real".

Checks and verdicts469 words

The reviewers from another model family (OpenAI GPT-5.6) could not execute code; the executed evidence comes from the other checks, each of which re-ran a repro.

Each independent check and its verdict
CheckVerdictWhat it found
lead critique (cross-model, reading only) one finding, high confidenceNamed the defect (DoubleRange.java:156-163, :190, the LongRange twin) with a one-document fromQuery input and a derived "must be rewritten"; it ran nothing.
finder (a hunt unit) candidateJUnit with 3 tests fails 3 of 3 on main -ea and -da, 10.5.2 and 9.12.3; passes with the fix; 22 of 22 TestRangeFacetCounts pass with the fix.
cross-model review, intended lens real, medium (reading only)Intended argument "moderate": the facet note covers the Multi* wrappers, not the direct overloads; a separate run re-ran the JUnit (3 of 3 fail).
cross-model review, refute lens not real, low, proceduralCould not run code; agreed on the root cause and that the inputs are in contract; a separate run re-ran the JUnit (3 of 3 fail).
Independent reproducer realOwn JUnit (7 tests, 2 controls): 5 of 7 fail on main -ea/-da, 10.5.2 and 9.12.3; added IndexReaderFunctions.maxDoc(); 7 of 7 pass with the fix.
Cross-model review, root-cause lens real, medium (reading only)Root cause agreed; separate runs: 3 of 3 fail on main -ea/-da, 3 of 3 pass with the fix.
Devil's advocate uncertain, mediumBuilt the Multi* design case and judged it not decisive; design probe: the two overloads build equal queries, the counters also throw, the workaround agrees everywhere.
adjudication confirmed, mediumRe-ran the independent reproduction's test (seed 5EED1A2B, 5 of 7 fail, -ea/-da), the finder's (3 of 3) and the independent reproduction's with the fix (7 of 7 pass); class crash, low priority.
Follow-up, audit, repro minimization keepHistory over 17 releases and main, prior-report search, security screen; re-runs with new seeds; the one-method test verified on 645d4fe14a20.
fact check of this doc (2026-10-30) real as described; 4 wording errors, fixedRe-ran the gradle command (assertions on and off), the standalone runner on main and 10.6.0, fix A with :lucene:facet:check, and its own probe confirming every executed number; the errors (commit/release pairing, unchanged-files claim, "every other Query consumer", how it was found) are fixed.
cross-model fact check of this doc (2026-10-30) behaviour real; intended case strong (reading only)2 errors, 6 overclaims and 4 missing caveats, all addressed; it judged the bug noise under the hunt's no-noise rule and suggests a neutral design or documentation question, which is how this version frames it.
diagram check (2026-10-30) passRe-ran the diagram probe and the minimal test; every on-screen value, line and quote matches the code at 645d4fe14a20.

Diagram file

The diagram as a still image, generated with AI and checked against the code. It is under 10 MB, the limit for a GitHub attachment.

Sources and provenance

Finding record, Lucene and upstream sources, repro files and diagram notes1,642 words

Finding record

LFV-776, snapshot of 2026-11-06

Registry title
DoubleRange#getQuery(Query, DoubleValuesSource) and LongRange#getQuery(Query, LongValuesSource) never call rewrite(searcher) on the values source, so a source that needs rewriting (DoubleValuesSource.fromQuery, IndexReaderFunctions) throws 'must be rewritten', and a legacy QueryValueSource view silently ignores the searcher's Similarity
Area
facet / range drill-down and filter queries (DoubleRange/LongRange.ValueSourceQuery)
Priority
Low, rank 624: The experimental DoubleRange/LongRange#getQuery fails loudly only for values sources that need rewriting (not the documented field or expression sources), and passing source.rewrite(searcher) works around it.
Impact class
crash
Impact note
Applications that filter or drill down with the experimental DoubleRange#getQuery(Query, DoubleValuesSource) or LongRange#getQuery(Query, LongValuesSource) over a values source that needs rewriting (DoubleValuesSource.fromQuery or its toLongValuesSource() view, an IndexReaderFunctions statistic, or a searcher-dependent legacy ValueSource exposed with asDoubleValuesSource()) and do not call rewrite(searcher) themselves get a deterministic UnsupportedOperationException ('This DoubleValuesSource must be rewritten' or 'IndexReaderFunction must be rewritten before use') from every search with the query, with default index and searcher settings and at any index size; in the narrower case of a legacy QueryValueSource view on a searcher with a non-default Similarity the range is checked against default-Similarity scores and silently matches the wrong documents (0 instead of 3 in the repro); field and expression-of-fields sources, the documented use, are unaffected, passing source.rewrite(searcher) works around it, and every release from 7.2.0 to 10.6.0 has it (10.6.0 executed on the release jars).
Summary
You build a range filter with DoubleRange#getQuery(null, DoubleValuesSource.fromQuery(new TermQuery(...))) (or LongRange#getQuery with the source's toLongValuesSource() view) and run it on an IndexSearcher, and expect it to match the documents whose query score falls in the range, as FunctionMatchQuery does with the same source, because core says Queries that use a DoubleValuesSource call rewrite() in createWeight. Instead the search throws UnsupportedOperationException 'This DoubleValuesSource must be rewritten': the range query hands the source to getValues without ever rewriting it against the searcher. With a legacy QueryValueSource view on a searcher that uses ClassicSimilarity the same omission is silent: the source scores with a fresh default-Similarity searcher, and the range [top score, top score] matches 0 documents where FunctionMatchQuery matches 3.
Introduced
03afeb7766a39996de3c85e8a6ab24d2a352dd1c on branch_7x (first released in 7.2.0); master equivalent 2a4dd499bbd2f5ed1f481e17eeaee4f32b927185 (first released in 8.0.0). LUCENE-7736 'Expose some IndexReader stats via DoubleValuesSources' (apache/lucene#8787; JIRA patch, no GitHub PR; author romseygeek, reviewed by jpountz) added DoubleValuesSource/LongValuesSource#rewrite(IndexSearcher) with the rule that Queries call rewrite() in createWeight, updated FunctionMatchQuery and FunctionScoreQuery, and left the facet DoubleRange/LongRange ValueSourceQuery unchanged [read; bisected at release granularity by execution, no build at the commit's parent]. The no-rewrite line valueSource.getValues(context, null) comes from LUCENE-7610 (713b65d1dcc on master, a238610bab1 on branch_6x, first in 6.4.0), written before any rewrite step existed; DoubleValuesSource.fromQuery, the main throwing trigger, came the next day with LUCENE-7998 (c6171019d5f on branch_7x, 423677f20e6 on master), in the same 7.2.0 release. (2017-11-15 (LUCENE-7736 committed on branch_7x and master; author date 2017-03-28); LUCENE-7998 committed 2017-11-16; the getValues line from LUCENE-7610, committed 2017-01-07)
First affected version
7.2.0 [exec, -ea and -da]: the 5-check probe fails 5 of 5 with its controls passing, and the no-rewrite probe gives [] where FunctionMatchQuery gives [0, 1, 4]; 7.1.0 is the last unaffected release (no rewrite API: the 5-check probe does not compile, and the no-rewrite probe passes on 6.5.0, 6.6.6, 7.0.0 and 7.1.0 [exec], where the facet query and FunctionMatchQuery both ignore the searcher); the direct overloads exist since 6.4.0.
Affected versions
every release from 7.2.0 to 10.5.2 (71 releases: 7.2.0-7.7.3, 8.0.0-8.11.4, 9.0.0-9.12.3, 10.0.0-10.5.2), 10.6.0 [exec: the doc's repro on the release jars, -ea and -da] and main a9779ef3c193 (unreleased 11.0); branch_10_6 and branch_10x [read]; upstream main f7bcc0abd816 (2026-10-27) has the same code; never fixed upstream. [exec] the follow-up's 5-check probe fails on 7.2.0, 7.2.1, 7.7.3, 8.0.0, 8.11.4, 9.0.0, 9.12.3, 10.0.0, 10.5.2 and main (-ea and -da on 7.2.0, 10.5.2 and main), the finder's JUnit 3 of 3 and the independent reproduction's JUnit 5 of 7 on 9.12.3, 10.5.2 and main; [read] a static scan of every other 6.x-10.x release tag in the history clone (no release ever calls valueSource.rewrite in these classes); 8.8.2-8.11.3 are not tagged in the clone and are inferred from 8.8.1 (scan) and 8.11.4 (exec). On 7.2.0-7.7.1 and 8.0.0 (before LUCENE-8726) a QueryValueSource view already rewritten by another consumer carries the searcher into the facet query and masks the silent variant; fresh instances show it there too, and the throwing triggers are not affected.
Status
documented (brief, checks and a per-finding doc exist)
Verification
Confirmed by AI-run checks: an independent repro, a cross-model review that could only read the code (its low-confidence “not real” is procedural), an intended-behaviour check, a prior-report search and a skeptical audit; not yet triaged by a person.
Found by
non-formal: code reading
Upstream and prior reports
none found (2026-10-27, the prior-report record, read-only): scripted prior-report search 1 (77 planned queries: 32 GitHub issue/PR, 5 commit, 4 downstream-repository, 24 mailing-list, 6 JIRA; 3403 distinct hits, 0 high, 27 medium, all triaged), run 2 (64 queries over dev@, java-user@, issues@, commits@ and JIRA; 0 high, 12 medium), 62 own GitHub searches over apache/lucene, apache/lucene-solr and the Elasticsearch, OpenSearch and Solr repositories (25 non-empty, all triaged), the changed files of all 408 open PRs (only #16847 touches DoubleRange.java/LongRange.java, mechanically: maxDoc to context.maxLeafDoc()), the files of up to 265 facet and values-source PRs, commits by path on main, branch_10x, branch_10_5 and branch_9x, the full local history (no commit ever added valueSource.rewrite to ValueSourceQuery), 9 web queries and main CHANGES.txt; the finder's earlier run (51 scripted prior-report search queries, 13 gh queries) found nothing either. Design context, not reports: #543 (LUCENE-10245, merged: the facet Multi*ValuesSource wrappers need no rewrite(), now the documented fromSingleValued caveat, a path this row excludes), #8787 (LUCENE-7736: added rewrite; FunctionMatchQuery was fixed during review and CoveringQuery in follow-up commit 3f0b240, facets not mentioned), #9046 (LUCENE-7998: fromQuery and the Query rewrite javadoc), #8661 (LUCENE-7610: the direct overloads); #13375/#13383 touch the same classes for an unrelated FieldExistsQuery shortcut. Gap: StackOverflow pages could not be fetched; their titles are generic facet questions.
Lucene commit tested
main 645d4fe14a20 (commit of 2026-10-28): the repro test through gradle in a scratch worktree, assertions on and off, stock and with the fix, plus the facet.range tests and :lucene:facet:check with the fix; the 10.6.0 release jars through the standalone runner, assertions on and off; a coupling probe stock, with the fix, and with the fix plus LFV-770's fix. After the 2026-10-30 checks the fix's code comment was corrected and the new diff re-run: :lucene:facet:check and the repro with assertions off pass. Upstream main 716d6f216f52 (2026-10-29) and branch_11x have the code-path files unchanged. The hunt, the verifiers, the adjudication and the follow-up used main a9779ef3c193, where every file on the bug's code path is identical; RescoreTopNQuery.java, cited only as an example of a rewriting Query, changed in #16926 but not at the cited lines.

Lucene and upstream sources

Finding record
  • lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java:156-164 (at a9779ef3c193) (ValueSourceQuery.rewrite rewrites only fastMatchQuery), :166-213 (createWeight; valueSource.getValues(context, null) at :190, valueSource.isCacheable(ctx) at :209-210), :323-336 (getQuery(Query, DoubleValuesSource) javadoc and overload: no restriction on the source; 'If the value source is static ... PointRangeQuery'), :339-358 (getQuery(Query, MultiDoubleValuesSource) unwraps fromSingleValued into the same ValueSourceQuery)
  • lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java:143-150 (at a9779ef3c193) (rewrite: fast match only), :153-200 (createWeight; getValues at :177, isCacheable at :196-197), :322 (getQuery(Query, LongValuesSource)), :338 (getQuery(Query, MultiLongValuesSource)); DoubleRange.java and LongRange.java are byte-identical at a Lucene main checkout ea57360912ed
  • lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java:33-35 (at a9779ef3c193) (client protocol: rewrite against the top-level searcher, then getValues), :97-99 ('Queries that use DoubleValuesSource objects should call rewrite() during Query#createWeight'), :768 (QueryDoubleValuesSource.getValues throws 'This DoubleValuesSource must be rewritten'); LongValuesSource.java:30-32 (client protocol)
  • lucene/queries/src/java/org/apache/lucene/queries/function/FunctionMatchQuery.java:88 (at a9779ef3c193) (DoubleValuesSource vs = source.rewrite(searcher) in createWeight: the same pattern done right)
  • lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java:184-205 (at a9779ef3c193) (asDoubleValuesSource() -> WrappedDoubleValuesSource(this, null), newContext(searcher)) and valuesource/QueryValueSource.java:114-125 (null context searcher -> new IndexSearcher over the top-level reader, default Similarity)
  • lucene/facet/src/java/org/apache/lucene/facet/MultiDoubleValuesSource.java:41-47 (at a9779ef3c193) and MultiLongValuesSource.java:43-49 (documented: rewrite 'will also never be called' for fromSingleValued wrappers; 'The faceting use-cases don't call rewrite or provide scores')
  • lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java:243 (at a9779ef3c193) (documented use: range.getQuery over an expression-of-fields source, which needs no rewrite); DoubleRange.java:42 and LongRange.java:41 (@lucene.experimental)
The finding in 30 seconds
  • a Lucene main checkout (645d4fe14a20) lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java:156-164, :166-213 (:190, :209-210), :323-337
  • a Lucene main checkout (645d4fe14a20) lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java:143-150, :153-200 (:177, :196-197), :322
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java:34-36, :97-99, :741, :768, :777-779
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/search/LongValuesSource.java:30-32, :64-70
  • a Lucene main checkout (645d4fe14a20) lucene/queries/src/java/org/apache/lucene/queries/function/FunctionMatchQuery.java:88; FunctionScoreQuery.java:121; ValueSource.java:184-186, :199-200, :244-246; valuesource/QueryValueSource.java:114-125; IndexReaderFunctions.java:99, :320
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/search/RescoreTopNQuery.java:68-69; lucene/sandbox/src/java/org/apache/lucene/sandbox/search/CoveringQuery.java:172
  • a Lucene main checkout (645d4fe14a20) lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java:197-198, :244-293 (:277); DistanceValueSource.java:96-99
  • a Lucene main checkout (645d4fe14a20) grep: the classes under lucene/*/src/java that extend Query and hold a DoubleValuesSource or LongValuesSource (RescoreTopNQuery, DoubleRange, LongRange, FunctionMatchQuery, FunctionScoreQuery, CoveringQuery, PointVectorStrategy; SerializedDVStrategy holds a ShapeValuesSource)
Who is affected
  • a Lucene main checkout (645d4fe14a20) lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java:42; lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java:41 (@lucene.experimental)
  • a Lucene main checkout (645d4fe14a20) lucene/demo/src/java/org/apache/lucene/demo/facet/DistanceFacetsExample.java:125-139, :239-243
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java:92, :97-99, :350-354, :777-779; lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java:183-185, :190
Why it looks unintended
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java:34-36, :97-99
  • a Lucene main checkout (645d4fe14a20) lucene/facet/src/java/org/apache/lucene/facet/range/DoubleRange.java:323-334; lucene/facet/src/java/org/apache/lucene/facet/range/LongRange.java:310-321
  • a Lucene main checkout (645d4fe14a20) lucene/facet/src/java/org/apache/lucene/facet/MultiDoubleValuesSource.java:41-47; a Lucene main checkout (645d4fe14a20) lucene/facet/src/java/org/apache/lucene/facet/MultiLongValuesSource.java:43-49
  • a Lucene main checkout (645d4fe14a20) lucene/facet/src/test/org/apache/lucene/facet/range/TestRangeFacetCounts.java:1703-1709
  • a Lucene main checkout (645d4fe14a20) lucene/CHANGES.txt:4438-4442 (LUCENE-10245 entry)
  • a Lucene main checkout (645d4fe14a20) lucene/queries/src/java/org/apache/lucene/queries/function/FunctionMatchQuery.java:88; FunctionScoreQuery.java:121; lucene/sandbox/src/java/org/apache/lucene/sandbox/search/CoveringQuery.java:172; lucene/core/src/java/org/apache/lucene/search/RescoreTopNQuery.java:68-69; lucene/spatial-extras/src/java/org/apache/lucene/spatial/vector/PointVectorStrategy.java:197-198, :277; DistanceValueSource.java:96-99
Suggested fix
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java:193-195, :848-850
  • a Lucene main checkout (645d4fe14a20) lucene/core/src/java/org/apache/lucene/search/DoubleValuesSource.java:94; lucene/queries/src/java/org/apache/lucene/queries/function/ValueSource.java:244-246

Other statements trace to the hunt's working files (models, logs, reviews and briefs), which are not published.

Repro files

About the diagrams

Made in parallel with this doc from the diagram's executed public-API probe on main 645d4fe14a20 (the one-document example, the LongRange view, IndexReaderFunctions.maxDoc(), the QueryValueSource view on a ClassicSimilarity searcher and a field-source control; stock and with the fix, assertions on and off); every code row is checked against the source at that commit.

Built 2026-11-06 from the hunt's record of this finding and its doc file. Lucene main under test in this hunt: ea57360912ed. Every listed finding was re-run on Lucene main e1faeba08bde on 2026-11-02.