This is an automated email from the ASF dual-hosted git repository.

houston pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git


The following commit(s) were added to refs/heads/main by this push:
     new 3c7b671595a SOLR-18019: Optimize existence queries for non-docValued 
PointFields (#3930)
3c7b671595a is described below

commit 3c7b671595a7700807d1ecfd31a5718022eee99c
Author: Houston Putman <[email protected]>
AuthorDate: Tue Dec 9 11:57:49 2025 -0800

    SOLR-18019: Optimize existence queries for non-docValued PointFields (#3930)
---
 .../SOLR-18019-optimize-existence-queries.yml      | 10 +++
 .../org/apache/solr/schema/DoublePointField.java   | 13 ++++
 .../org/apache/solr/schema/FloatPointField.java    | 13 ++++
 .../apache/solr/search/TestSolrQueryParser.java    | 75 +++++++++++++++-------
 4 files changed, 88 insertions(+), 23 deletions(-)

diff --git a/changelog/unreleased/SOLR-18019-optimize-existence-queries.yml 
b/changelog/unreleased/SOLR-18019-optimize-existence-queries.yml
new file mode 100644
index 00000000000..03bd0bdfdf9
--- /dev/null
+++ b/changelog/unreleased/SOLR-18019-optimize-existence-queries.yml
@@ -0,0 +1,10 @@
+# See https://github.com/apache/solr/blob/main/dev-docs/changelog.adoc
+title: Optimize existence queries for non-docValued FloatPointField and 
DoublePointField
+type: other # added, changed, fixed, deprecated, removed, dependency_update, 
security, other
+authors:
+  - name: Houston Putman
+    nick: HoustonPutman
+    url: https://home.apache.org/phonebook.html?uid=houston
+links:
+  - name: SOLR-18019
+    url: https://issues.apache.org/jira/browse/SOLR-18019
diff --git a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java 
b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
index 430d3ff968d..70cc45bfd5a 100644
--- a/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/DoublePointField.java
@@ -178,4 +178,17 @@ public class DoublePointField extends PointField 
implements DoubleValueFieldType
   protected StoredField getStoredField(SchemaField sf, Object value) {
     return new StoredField(sf.getName(), (Double) this.toNativeType(value));
   }
+
+  /**
+   * Override the default existence behavior, so that the non-docValued/norms 
implementation matches
+   * NaN values for double and float fields. The [* TO *] query for those 
fields does not match
+   * 'NaN' values, so they must be matched separately.
+   *
+   * <p>For doubles and floats the query behavior is equivalent to 
field:[-Infinity TO NaN] since
+   * NaN has a value greater than +Infinity
+   */
+  @Override
+  public Query getSpecializedExistenceQuery(QParser parser, SchemaField field) 
{
+    return DoublePoint.newRangeQuery(field.getName(), 
Double.NEGATIVE_INFINITY, Double.NaN);
+  }
 }
diff --git a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java 
b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
index e1b7786f8ac..4ed65fe45c3 100644
--- a/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
+++ b/solr/core/src/java/org/apache/solr/schema/FloatPointField.java
@@ -178,4 +178,17 @@ public class FloatPointField extends PointField implements 
FloatValueFieldType {
   protected StoredField getStoredField(SchemaField sf, Object value) {
     return new StoredField(sf.getName(), (Float) this.toNativeType(value));
   }
+
+  /**
+   * Override the default existence behavior, so that the non-docValued/norms 
implementation matches
+   * NaN values for double and float fields. The [* TO *] query for those 
fields does not match
+   * 'NaN' values, so they must be matched separately.
+   *
+   * <p>For doubles and floats the query behavior is equivalent to 
field:[-Infinity TO NaN] since
+   * NaN has a value greater than +Infinity
+   */
+  @Override
+  public Query getSpecializedExistenceQuery(QParser parser, SchemaField field) 
{
+    return FloatPoint.newRangeQuery(field.getName(), Float.NEGATIVE_INFINITY, 
Float.NaN);
+  }
 }
diff --git a/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java 
b/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
index e8a8b3562f9..6dcf66563e7 100644
--- a/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
+++ b/solr/core/src/test/org/apache/solr/search/TestSolrQueryParser.java
@@ -32,6 +32,8 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Random;
+import org.apache.lucene.document.DoublePoint;
+import org.apache.lucene.document.FloatPoint;
 import org.apache.lucene.search.BooleanClause;
 import org.apache.lucene.search.BooleanQuery;
 import org.apache.lucene.search.BoostQuery;
@@ -40,6 +42,7 @@ import org.apache.lucene.search.FieldExistsQuery;
 import org.apache.lucene.search.IndexOrDocValuesQuery;
 import org.apache.lucene.search.IndexSearcher;
 import org.apache.lucene.search.PointInSetQuery;
+import org.apache.lucene.search.PointRangeQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.search.TermInSetQuery;
 import org.apache.lucene.search.TermQuery;
@@ -1834,29 +1837,55 @@ public class TestSolrQueryParser extends SolrTestCaseJ4 
{
                 createdQuery instanceof FieldExistsQuery);
           } else if (schemaField.getType().getNumberType() == NumberType.DOUBLE
               || schemaField.getType().getNumberType() == NumberType.FLOAT) {
-            assertTrue(
-                "PointField with NaN values must include \"exists or NaN\" if 
the field doesn't have norms or docValues: \""
-                    + query
-                    + "\".",
-                createdQuery instanceof ConstantScoreQuery);
-            assertTrue(
-                "PointField with NaN values must include \"exists or NaN\" if 
the field doesn't have norms or docValues: \""
-                    + query
-                    + "\".",
-                ((ConstantScoreQuery) createdQuery).getQuery() instanceof 
BooleanQuery);
-            assertEquals(
-                "PointField with NaN values must include \"exists or NaN\" if 
the field doesn't have norms or docValues: \""
-                    + query
-                    + "\". This boolean query must be an OR.",
-                1,
-                ((BooleanQuery) ((ConstantScoreQuery) createdQuery).getQuery())
-                    .getMinimumNumberShouldMatch());
-            assertEquals(
-                "PointField with NaN values must include \"exists or NaN\" if 
the field doesn't have norms or docValues: \""
-                    + query
-                    + "\". This boolean query must have 2 clauses.",
-                2,
-                ((BooleanQuery) ((ConstantScoreQuery) 
createdQuery).getQuery()).clauses().size());
+            if (schemaField.getType().isPointField()) {
+              assertTrue(
+                  "PointField with NaN values must do a range query with an 
upper bound of NaN (Sorted higher than +Infinity) if the field doesn't have 
norms or docValues: \""
+                      + query
+                      + "\".",
+                  createdQuery instanceof PointRangeQuery);
+              if (schemaField.getType().getNumberType() == NumberType.DOUBLE) {
+                assertEquals(
+                    "PointField with NaN values must do a range query with an 
upper bound of NaN (Sorted higher than +Infinity) if the field doesn't have 
norms or docValues: \""
+                        + query
+                        + "\".",
+                    Double.NaN,
+                    DoublePoint.decodeDimension(
+                        ((PointRangeQuery) createdQuery).getUpperPoint(), 0),
+                    0);
+              } else {
+                assertEquals(
+                    "PointField with NaN values must do a range query with an 
upper bound of NaN (Sorted higher than +Infinity) if the field doesn't have 
norms or docValues: \""
+                        + query
+                        + "\".",
+                    Float.NaN,
+                    FloatPoint.decodeDimension(((PointRangeQuery) 
createdQuery).getUpperPoint(), 0),
+                    0);
+              }
+            } else {
+              assertTrue(
+                  "PointField with NaN values must include \"exists or NaN\" 
if the field doesn't have norms or docValues: \""
+                      + query
+                      + "\".",
+                  createdQuery instanceof ConstantScoreQuery);
+              assertTrue(
+                  "NumericField with NaN values must include \"exists or NaN\" 
if the field doesn't have norms or docValues: \""
+                      + query
+                      + "\".",
+                  ((ConstantScoreQuery) createdQuery).getQuery() instanceof 
BooleanQuery);
+              assertEquals(
+                  "NumericField with NaN values must include \"exists or NaN\" 
if the field doesn't have norms or docValues: \""
+                      + query
+                      + "\". This boolean query must be an OR.",
+                  1,
+                  ((BooleanQuery) ((ConstantScoreQuery) 
createdQuery).getQuery())
+                      .getMinimumNumberShouldMatch());
+              assertEquals(
+                  "NumericField with NaN values must include \"exists or NaN\" 
if the field doesn't have norms or docValues: \""
+                      + query
+                      + "\". This boolean query must have 2 clauses.",
+                  2,
+                  ((BooleanQuery) ((ConstantScoreQuery) 
createdQuery).getQuery()).clauses().size());
+            }
           } else {
             assertFalse(
                 "Field doesn't have docValues, so existence query \""

Reply via email to