You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2026/09/21 18:36:07 UTC
(lucene) branch branch_10x updated: Fix range query for NumericDocValues for float and double values (#16629)
This is an automated email from the ASF dual-hosted git repository.
HoustonPutman pushed a commit to branch branch_10x
in repository https://gitbox.apache.org/repos/asf/lucene.git
The following commit(s) were added to refs/heads/branch_10x by this push:
new a7be0c1580b Fix range query for NumericDocValues for float and double values (#16629)
a7be0c1580b is described below
commit a7be0c1580baca26d1c8169f427f73a499c11fb2
Author: Houston Putman <ho...@apache.org>
AuthorDate: Mon Sep 21 11:21:17 2026 -0700
Fix range query for NumericDocValues for float and double values (#16629)
This reverts commit da18ffc5890f72df279373d40ce5c4192734cd11.
---
lucene/CHANGES.txt | 6 +
.../document/SortedNumericDocValuesRangeQuery.java | 23 +-
.../apache/lucene/search/NumericFieldStats.java | 119 --------
.../apache/lucene/search/TestDocValuesQueries.java | 87 ++++--
.../lucene/search/TestNumericFieldStats.java | 332 ---------------------
.../sandbox/document/TestHalfFloatPoint.java | 24 --
6 files changed, 79 insertions(+), 512 deletions(-)
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 448cd168d45..c4254c777f7 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -278,6 +278,12 @@ API Changes
Lucene 10.4 behavior and is deprecated, and [Byte|Float]VectorSimilarityQuery.Adaptive exposes the 10.5 behavior.
(Kaival Parikh, Chris Hostetter, Michael Sokolov)
+* GITHUB#16573: Remove the NumericFieldStats API (added in 10.5.0 via GITHUB#15740, GITHUB#15817) and
+ revert SortedNumericDocValuesRangeQuery to no longer use it. The statistics it derived from points
+ and doc values could be incorrect if points and doc values are encoded differently, for example when
+ a float/double field is indexed with points and NumericDocValues, causing range queries to return
+ wrong results. (Chris Hostetter, Houston Putman)
+
Bug Fixes
---------------------
diff --git a/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
index 729aa07b6da..7a740296ee0 100644
--- a/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/document/SortedNumericDocValuesRangeQuery.java
@@ -34,8 +34,6 @@ import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.NumericDocValuesRangeQuery;
-import org.apache.lucene.search.NumericFieldStats;
-import org.apache.lucene.search.NumericFieldStats.Stats;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryVisitor;
import org.apache.lucene.search.ScoreMode;
@@ -96,17 +94,16 @@ final class SortedNumericDocValuesRangeQuery extends NumericDocValuesRangeQuery
if (lowerValue > upperValue) {
return MatchNoDocsQuery.INSTANCE;
}
- final Stats stats = NumericFieldStats.getStats(indexSearcher.getIndexReader(), field);
- if (stats != null) {
- if (lowerValue > stats.max() || upperValue < stats.min()) {
- return MatchNoDocsQuery.INSTANCE;
- }
- if (lowerValue <= stats.min() && upperValue >= stats.max()) {
- // Every value in the field falls within [lowerValue, upperValue], so this matches exactly
- // the documents that have a value for the field.
- if (stats.docCount() == indexSearcher.getIndexReader().maxDoc()) {
- return MatchAllDocsQuery.INSTANCE;
- }
+ long globalMin = DocValuesSkipper.globalMinValue(indexSearcher.getIndexReader(), field);
+ long globalMax = DocValuesSkipper.globalMaxValue(indexSearcher.getIndexReader(), field);
+ if (lowerValue > globalMax || upperValue < globalMin) {
+ return MatchNoDocsQuery.INSTANCE;
+ }
+ if (lowerValue <= globalMin && upperValue >= globalMax) {
+ if (DocValuesSkipper.globalDocCount(indexSearcher.getIndexReader(), field)
+ == indexSearcher.getIndexReader().maxDoc()) {
+ return MatchAllDocsQuery.INSTANCE;
+ } else {
return new FieldExistsQuery(field);
}
}
diff --git a/lucene/core/src/java/org/apache/lucene/search/NumericFieldStats.java b/lucene/core/src/java/org/apache/lucene/search/NumericFieldStats.java
deleted file mode 100644
index 1a529947b3f..00000000000
--- a/lucene/core/src/java/org/apache/lucene/search/NumericFieldStats.java
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.search;
-
-import java.io.IOException;
-import org.apache.lucene.index.DocValuesSkipper;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.LeafReader;
-import org.apache.lucene.index.LeafReaderContext;
-import org.apache.lucene.index.PointValues;
-
-/**
- * Utility class for retrieving global numeric field statistics from index metadata structures,
- * without accessing individual documents. It probes {@link PointValues} first and falls back to
- * {@link DocValuesSkipper}. Returns {@code null} when neither structure is available for the field.
- *
- * @lucene.experimental
- */
-public final class NumericFieldStats {
-
- private NumericFieldStats() {}
-
- /**
- * Holds the global statistics for a numeric field.
- *
- * @param min the global minimum value
- * @param max the global maximum value
- * @param docCount the total number of documents containing the field
- */
- public record Stats(long min, long max, int docCount) {}
-
- /**
- * Returns the global statistics for the given numeric field across all segments. Probes {@link
- * PointValues} first; if unavailable, falls back to {@link DocValuesSkipper}.
- *
- * @param reader the {@link IndexReader} to query
- * @param field the name of the numeric field
- * @return a {@link Stats} containing the global min, max, and doc count, or {@code null} if
- * neither {@link PointValues} nor {@link DocValuesSkipper} are available for the field
- * @throws IOException if an I/O error occurs
- */
- public static Stats getStats(IndexReader reader, String field) throws IOException {
- final Stats result = getStatsFromPoints(reader, field);
- if (result != null) {
- return result;
- }
- return getStatsFromSkipper(reader, field);
- }
-
- private static Stats getStatsFromPoints(IndexReader reader, String field) throws IOException {
- final byte[] minPacked = PointValues.getMinPackedValue(reader, field);
- final byte[] maxPacked = PointValues.getMaxPackedValue(reader, field);
- if (minPacked == null
- || maxPacked == null
- || minPacked.length > Long.BYTES
- || maxPacked.length > Long.BYTES) {
- return null;
- }
- final int docCount = PointValues.getDocCount(reader, field);
- return new Stats(decodeLong(minPacked), decodeLong(maxPacked), docCount);
- }
-
- private static Stats getStatsFromSkipper(IndexReader reader, String field) throws IOException {
- Long min = null;
- Long max = null;
- int docCount = 0;
- for (LeafReaderContext ctx : reader.leaves()) {
- final LeafReader leafReader = ctx.reader();
- if (leafReader.getFieldInfos().fieldInfo(field) == null) {
- continue;
- }
- final DocValuesSkipper skipper = leafReader.getDocValuesSkipper(field);
- if (skipper == null) {
- return null;
- }
- if (min == null && max == null) {
- min = skipper.minValue();
- max = skipper.maxValue();
- } else {
- min = Math.min(min, skipper.minValue());
- max = Math.max(max, skipper.maxValue());
- }
- docCount += skipper.docCount();
- }
- if (min == null || max == null) {
- return null;
- }
- return new Stats(min, max, docCount);
- }
-
- /**
- * Decodes a packed {@code byte[]} point value into a {@code long}. Handles any packed value
- * length from 1 to 8 bytes, covering {@code HalfFloatPoint} (2 bytes), {@code IntField} (4
- * bytes), {@code LongField} (8 bytes), and any other width that uses the standard sortable
- * encoding (big-endian with sign bit flipped).
- */
- private static long decodeLong(byte[] packed) {
- assert packed.length >= 1 && packed.length <= Long.BYTES;
- long result = (byte) (packed[0] ^ 0x80);
- for (int i = 1; i < packed.length; i++) {
- result = (result << 8) | (packed[i] & 0xFF);
- }
- return result;
- }
-}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestDocValuesQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestDocValuesQueries.java
index 4f6719d3a6c..42b0b4d449e 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestDocValuesQueries.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestDocValuesQueries.java
@@ -27,14 +27,14 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
-import java.util.Random;
import java.util.Set;
import java.util.function.IntFunction;
import org.apache.lucene.codecs.Codec;
import org.apache.lucene.codecs.lucene90.Lucene90DocValuesFormat;
import org.apache.lucene.document.Document;
+import org.apache.lucene.document.DoubleDocValuesField;
+import org.apache.lucene.document.DoublePoint;
import org.apache.lucene.document.Field;
-import org.apache.lucene.document.LongField;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.document.NumericDocValuesField;
import org.apache.lucene.document.SortedDocValuesField;
@@ -915,7 +915,7 @@ public class TestDocValuesQueries extends LuceneTestCase {
}
}
- public void testSortedNumericDocValuesRangeQueryRewrites(Random random) throws Exception {
+ public void testSortedNumericDocValuesRangeQueryRewrites() throws Exception {
try (Directory dir = newDirectory();
RandomIndexWriter iw = new RandomIndexWriter(random(), dir)) {
for (int i = 0; i < 100; i++) {
@@ -986,28 +986,67 @@ public class TestDocValuesQueries extends LuceneTestCase {
}
}
- public void testRewriteWorksWithPointsButNoSkipIndex() throws IOException {
- try (Directory dir = newDirectory()) {
- try (RandomIndexWriter iw = new RandomIndexWriter(random(), dir)) {
- for (int i = 0; i < 100; i++) {
- final Document doc = new Document();
- doc.add(new LongField("field", 100 + i, Field.Store.NO));
- iw.addDocument(doc);
- }
- iw.commit();
- try (IndexReader reader = iw.getReader()) {
- final IndexSearcher searcher = new IndexSearcher(reader);
- // Query range [0, 50] is entirely below field range [100, 199]
- Query query = SortedNumericDocValuesField.newSlowRangeQuery("field", 0, 50);
- Query rewritten = searcher.rewrite(query);
- assertThat(rewritten, instanceOf(MatchNoDocsQuery.class));
-
- // Query range [0, 250] covers entire field range [100, 199]
- // and all docs have a value
- query = SortedNumericDocValuesField.newSlowRangeQuery("field", 0, 250);
- rewritten = searcher.rewrite(query);
- assertThat(rewritten, instanceOf(MatchAllDocsQuery.class));
+ @Test
+ public void testDocValueQueryWhenFieldNameIsAlsoUsedForPoints() throws IOException {
+ // GITHUB#16573: a field name used for both DoublePoint and (sorted-)numeric doc values must not
+ // let a point-stats-based range rewrite drop matching docs. Each exact-value query must find
+ // its
+ // one document, whether the doc-values were written as raw double bits (as Solr does) or via
+ // DoubleDocValuesField (as Lucene does), and whether or not the query is wrapped in an
+ // IndexOrDocValuesQuery.
+ final String solrField = "solr_p_d_dv";
+ final String luceneField = "lucene_p_d_dv";
+ final List<Double> values =
+ List.of(
+ Double.MIN_VALUE,
+ Double.MAX_VALUE,
+ Double.NEGATIVE_INFINITY,
+ Double.POSITIVE_INFINITY,
+ -1D * Double.MIN_VALUE,
+ -1D * Double.MAX_VALUE);
+ try (Directory dir = newDirectory();
+ RandomIndexWriter iw = new RandomIndexWriter(random(), dir)) {
+ for (double val : values) {
+ Document doc = new Document();
+ doc.add(new DoublePoint(solrField, val));
+ doc.add(new NumericDocValuesField(solrField, Double.doubleToLongBits(val)));
+ iw.addDocument(doc);
+
+ doc = new Document();
+ doc.add(new DoublePoint(luceneField, val));
+ doc.add(new DoubleDocValuesField(luceneField, val));
+ iw.addDocument(doc);
+ }
+ iw.commit();
+ iw.forceMerge(1);
+ try (IndexReader reader = iw.getReader()) {
+ assertEquals(1, reader.leaves().size());
+ final IndexSearcher searcher = newSearcher(reader, false);
+ final List<String> errors = new ArrayList<>();
+ for (double val : values) {
+ final long solrBits = Double.doubleToLongBits(val);
+ final long luceneBits = Double.doubleToRawLongBits(val);
+ for (Query q :
+ List.of(
+ SortedNumericDocValuesField.newSlowRangeQuery(
+ luceneField, luceneBits, luceneBits),
+ SortedNumericDocValuesField.newSlowRangeQuery(solrField, solrBits, solrBits),
+ // wrapping in IndexOrDocValuesQuery must not change the result
+ new IndexOrDocValuesQuery(
+ DoublePoint.newRangeQuery(luceneField, val, val),
+ SortedNumericDocValuesField.newSlowRangeQuery(
+ luceneField, luceneBits, luceneBits)),
+ new IndexOrDocValuesQuery(
+ DoublePoint.newRangeQuery(solrField, val, val),
+ SortedNumericDocValuesField.newSlowRangeQuery(
+ solrField, solrBits, solrBits)))) {
+ TotalHits h = searcher.search(q, 99999).totalHits;
+ if (1 != h.value()) {
+ errors.add(q + " => " + h);
+ }
+ }
}
+ assertEquals(List.of(), errors);
}
}
}
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestNumericFieldStats.java b/lucene/core/src/test/org/apache/lucene/search/TestNumericFieldStats.java
deleted file mode 100644
index babcb39b38a..00000000000
--- a/lucene/core/src/test/org/apache/lucene/search/TestNumericFieldStats.java
+++ /dev/null
@@ -1,332 +0,0 @@
-/*
- * 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.search;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import org.apache.lucene.document.Document;
-import org.apache.lucene.document.Field;
-import org.apache.lucene.document.FieldType;
-import org.apache.lucene.document.InetAddressPoint;
-import org.apache.lucene.document.IntField;
-import org.apache.lucene.document.LongField;
-import org.apache.lucene.document.SortedNumericDocValuesField;
-import org.apache.lucene.index.DirectoryReader;
-import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.index.IndexWriter;
-import org.apache.lucene.index.IndexWriterConfig;
-import org.apache.lucene.index.NoMergePolicy;
-import org.apache.lucene.search.NumericFieldStats.Stats;
-import org.apache.lucene.store.Directory;
-import org.apache.lucene.tests.util.LuceneTestCase;
-import org.apache.lucene.util.BytesRef;
-
-public class TestNumericFieldStats extends LuceneTestCase {
-
- private static Field newCustomWidthPoint(String name, int numBytes, long value) {
- FieldType type = new FieldType();
- type.setDimensions(1, numBytes);
- type.freeze();
- byte[] packed = new byte[numBytes];
- for (int i = numBytes - 1; i >= 0; i--) {
- packed[i] = (byte) (value & 0xFF);
- value >>= 8;
- }
- packed[0] ^= (byte) 0x80;
- return new Field(name, new BytesRef(packed), type);
- }
-
- private static long minValueForWidth(int numBytes) {
- return -(1L << (numBytes * 8 - 1));
- }
-
- private static long maxValueForWidth(int numBytes) {
- return (1L << (numBytes * 8 - 1)) - 1;
- }
-
- public void testGetStatsWithAllByteWidths() throws IOException {
- for (int numBytes = 1; numBytes <= Long.BYTES; numBytes++) {
- long min = minValueForWidth(numBytes);
- long max = maxValueForWidth(numBytes);
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- for (long value : new long[] {min, 0, max}) {
- final Document doc = new Document();
- doc.add(newCustomWidthPoint("field", numBytes, value));
- w.addDocument(doc);
- }
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull("numBytes=" + numBytes, stats);
- assertEquals("numBytes=" + numBytes, min, stats.min());
- assertEquals("numBytes=" + numBytes, max, stats.max());
- assertEquals("numBytes=" + numBytes, 3, stats.docCount());
- }
- }
- }
- }
-
- public void testGetStatsWithLongField() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- for (long value : new long[] {10L, 20L, 30L}) {
- final Document doc = new Document();
- doc.add(new LongField("field", value, Field.Store.NO));
- w.addDocument(doc);
- }
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull(stats);
- assertEquals(10L, stats.min());
- assertEquals(30L, stats.max());
- }
- }
- }
-
- public void testGetStatsWithIntField() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- for (int value : new int[] {-5, 0, 42}) {
- final Document doc = new Document();
- doc.add(new IntField("field", value, Field.Store.NO));
- w.addDocument(doc);
- }
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull(stats);
- assertEquals(-5L, stats.min());
- assertEquals(42L, stats.max());
- }
- }
- }
-
- public void testGetStatsFromDocValuesSkipper() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- for (long value : new long[] {100L, 200L, 300L}) {
- final Document doc = new Document();
- doc.add(SortedNumericDocValuesField.indexedField("field", value));
- w.addDocument(doc);
- }
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull(stats);
- assertEquals(100L, stats.min());
- assertEquals(300L, stats.max());
- }
- }
- }
-
- public void testGetStatsEmptyIndex() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- assertNull(NumericFieldStats.getStats(reader, "field"));
- }
- }
- }
-
- public void testGetStatsNonexistentField() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- final Document doc = new Document();
- doc.add(new LongField("other_field", 42L, Field.Store.NO));
- w.addDocument(doc);
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- assertNull(NumericFieldStats.getStats(reader, "field"));
- }
- }
- }
-
- public void testGetStatsDocValuesWithoutSkipIndex() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- for (long value : new long[] {10L, 20L, 30L}) {
- final Document doc = new Document();
- doc.add(new SortedNumericDocValuesField("field", value));
- w.addDocument(doc);
- }
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNull(stats);
- }
- }
- }
-
- public void testDocCountWithLongField() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- for (long value : new long[] {10L, 20L, 30L}) {
- final Document doc = new Document();
- doc.add(new LongField("field", value, Field.Store.NO));
- w.addDocument(doc);
- }
- w.addDocument(new Document());
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- assertEquals(3, NumericFieldStats.getStats(reader, "field").docCount());
- }
- }
- }
-
- public void testDocCountFromDocValuesSkipper() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- for (long value : new long[] {10L, 20L, 30L}) {
- final Document doc = new Document();
- doc.add(SortedNumericDocValuesField.indexedField("field", value));
- w.addDocument(doc);
- }
- w.addDocument(new Document());
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- assertEquals(3, NumericFieldStats.getStats(reader, "field").docCount());
- }
- }
- }
-
- public void testGetStatsWithExtremeLongValues() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- final Document doc1 = new Document();
- doc1.add(new LongField("field", Long.MIN_VALUE, Field.Store.NO));
- w.addDocument(doc1);
- final Document doc2 = new Document();
- doc2.add(new LongField("field", Long.MAX_VALUE, Field.Store.NO));
- w.addDocument(doc2);
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull(stats);
- assertEquals(Long.MIN_VALUE, stats.min());
- assertEquals(Long.MAX_VALUE, stats.max());
- }
- }
- }
-
- public void testGetStatsWithExtremeIntValues() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- final Document doc1 = new Document();
- doc1.add(new IntField("field", Integer.MIN_VALUE, Field.Store.NO));
- w.addDocument(doc1);
- final Document doc2 = new Document();
- doc2.add(new IntField("field", Integer.MAX_VALUE, Field.Store.NO));
- w.addDocument(doc2);
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull(stats);
- assertEquals(Integer.MIN_VALUE, stats.min());
- assertEquals(Integer.MAX_VALUE, stats.max());
- }
- }
- }
-
- public void testGetStatsMultiValuedField() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- final Document doc1 = new Document();
- doc1.add(SortedNumericDocValuesField.indexedField("field", 5L));
- doc1.add(SortedNumericDocValuesField.indexedField("field", 50L));
- w.addDocument(doc1);
- final Document doc2 = new Document();
- doc2.add(SortedNumericDocValuesField.indexedField("field", 25L));
- w.addDocument(doc2);
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull(stats);
- assertEquals(5L, stats.min());
- assertEquals(50L, stats.max());
- }
- }
- }
-
- public void testGetStatsMultipleSegments() throws IOException {
- try (Directory dir = newDirectory()) {
- IndexWriterConfig config = newIndexWriterConfig();
- config.setMergePolicy(NoMergePolicy.INSTANCE);
- try (IndexWriter w = new IndexWriter(dir, config)) {
- final Document doc1 = new Document();
- doc1.add(new LongField("field", 100L, Field.Store.NO));
- w.addDocument(doc1);
- w.commit();
-
- final Document doc2 = new Document();
- doc2.add(new LongField("field", 50L, Field.Store.NO));
- w.addDocument(doc2);
- w.commit();
-
- final Document doc3 = new Document();
- doc3.add(new LongField("field", 200L, Field.Store.NO));
- w.addDocument(doc3);
- w.commit();
-
- try (IndexReader reader = DirectoryReader.open(w)) {
- assertTrue(reader.leaves().size() > 1);
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull(stats);
- assertEquals(50L, stats.min());
- assertEquals(200L, stats.max());
- assertEquals(3, stats.docCount());
- }
- }
- }
- }
-
- public void testGetStatsWithSegmentsWithAndWithoutField() throws IOException {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- final Document doc = new Document();
- doc.add(new LongField("field", 100L, Field.Store.NO));
- w.addDocument(doc);
- w.commit();
-
- w.addDocument(new Document());
- w.commit();
-
- try (IndexReader reader = DirectoryReader.open(w)) {
- final Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull(stats);
- assertEquals(100L, stats.min());
- assertEquals(100L, stats.max());
- assertEquals(1, stats.docCount());
- }
- }
- }
-
- public void testGetStatsReturnsNullForWidePointValues() throws Exception {
- try (Directory dir = newDirectory();
- IndexWriter w = new IndexWriter(dir, newIndexWriterConfig())) {
- final Document doc = new Document();
- doc.add(new InetAddressPoint("field", InetAddress.getByName("192.168.0.1")));
- w.addDocument(doc);
- w.commit();
- try (IndexReader reader = DirectoryReader.open(w)) {
- assertNull(NumericFieldStats.getStats(reader, "field"));
- }
- }
- }
-}
diff --git a/lucene/sandbox/src/test/org/apache/lucene/sandbox/document/TestHalfFloatPoint.java b/lucene/sandbox/src/test/org/apache/lucene/sandbox/document/TestHalfFloatPoint.java
index 8b4013c5a31..4182af1692f 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/sandbox/document/TestHalfFloatPoint.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/sandbox/document/TestHalfFloatPoint.java
@@ -20,7 +20,6 @@ import java.util.Arrays;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.IndexSearcher;
-import org.apache.lucene.search.NumericFieldStats;
import org.apache.lucene.store.Directory;
import org.apache.lucene.tests.index.RandomIndexWriter;
import org.apache.lucene.tests.util.LuceneTestCase;
@@ -246,29 +245,6 @@ public class TestHalfFloatPoint extends LuceneTestCase {
assertEquals(Float.floatToIntBits(0f), Float.floatToIntBits(HalfFloatPoint.nextUp(-0f)));
}
- public void testNumericFieldStats() throws Exception {
- Directory dir = newDirectory();
- RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
- Document doc1 = new Document();
- doc1.add(new HalfFloatPoint("field", -2f));
- writer.addDocument(doc1);
- Document doc2 = new Document();
- doc2.add(new HalfFloatPoint("field", 1.25f));
- writer.addDocument(doc2);
- Document doc3 = new Document();
- doc3.add(new HalfFloatPoint("field", 100f));
- writer.addDocument(doc3);
- IndexReader reader = writer.getReader();
- NumericFieldStats.Stats stats = NumericFieldStats.getStats(reader, "field");
- assertNotNull(stats);
- assertTrue(stats.min() < 0);
- assertTrue(stats.max() > 0);
- assertEquals(3, stats.docCount());
- reader.close();
- writer.close();
- dir.close();
- }
-
public void testNextDown() {
assertEquals(Float.NaN, HalfFloatPoint.nextDown(Float.NaN), 0f);
assertEquals(Float.NEGATIVE_INFINITY, HalfFloatPoint.nextDown(Float.NEGATIVE_INFINITY), 0f);