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

mkhludnev 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 b475e58ccf9 SOLR-18440: fix (#4880) tests for trie fields (#4920)
b475e58ccf9 is described below

commit b475e58ccf969775d3bd43efc65b724ef33f36b9
Author: Mikhail Khludnev <[email protected]>
AuthorDate: Sat Sep 19 09:50:15 2026 +0300

    SOLR-18440: fix (#4880) tests for trie fields (#4920)
---
 .../solr/search/join/ScoreJoinQParserPlugin.java   | 47 +++++++++++++---------
 .../test-files/solr/collection1/conf/schema12.xml  |  6 +++
 .../solr/search/join/TestScoreJoinQPScore.java     | 24 +++++++++++
 .../query-guide/pages/join-query-parser.adoc       |  2 +
 4 files changed, 61 insertions(+), 18 deletions(-)

diff --git 
a/solr/core/src/java/org/apache/solr/search/join/ScoreJoinQParserPlugin.java 
b/solr/core/src/java/org/apache/solr/search/join/ScoreJoinQParserPlugin.java
index d97b7681cd9..29b4a44f432 100644
--- a/solr/core/src/java/org/apache/solr/search/join/ScoreJoinQParserPlugin.java
+++ b/solr/core/src/java/org/apache/solr/search/join/ScoreJoinQParserPlugin.java
@@ -260,7 +260,8 @@ public class ScoreJoinQParserPlugin extends QParserPlugin {
    * Creates a join query, delegating to {@link 
JoinUtil#createJoinQuery(String, boolean, String,
    * Class, Query, IndexSearcher, ScoreMode)} for a numeric {@code 
fromField}/Point {@code toField}
    * pair, or to {@link JoinUtil#createJoinQuery(String, boolean, String, 
Query, IndexSearcher,
-   * ScoreMode)} otherwise.
+   * ScoreMode)} otherwise. A legacy (non-Point) numeric {@code fromField} 
paired with a non-Point
+   * {@code toField} keeps using the term-based join, as it did before numeric 
joins were supported.
    *
    * @param fromField "foreign key" field name; any field type with a numeric 
{@link NumberType}
    *     (not necessarily a Point field type) qualifies, as long as {@code 
docValues="true"} is set.
@@ -287,18 +288,34 @@ public class ScoreJoinQParserPlugin extends QParserPlugin 
{
     final NumberType fromNumberType =
         fromSchemaField == null ? null : 
fromSchemaField.getType().getNumberType();
     if (fromNumberType != null) {
-      if (!fromSchemaField.hasDocValues()) {
-        throw new SolrException(
-            SolrException.ErrorCode.BAD_REQUEST,
-            "Numeric join 'from' field '"
-                + fromField
-                + "' must have docValues enabled; it doesn't need to be 
indexed.");
-      }
       final SchemaField toSchemaField = toSchema.getFieldOrNull(toField);
-      final boolean toIsPoint = toSchemaField != null && 
toSchemaField.getType().isPointField();
       final NumberType toNumberType =
           toSchemaField == null ? null : 
toSchemaField.getType().getNumberType();
-      if (!toIsPoint || fromNumberType != toNumberType) {
+      final boolean toIsMatchingPoint =
+          toSchemaField != null
+              && toSchemaField.getType().isPointField()
+              && fromNumberType == toNumberType;
+      if (toIsMatchingPoint) {
+        if (!fromSchemaField.hasDocValues()) {
+          throw new SolrException(
+              SolrException.ErrorCode.BAD_REQUEST,
+              "Numeric join 'from' field '"
+                  + fromField
+                  + "' must have docValues enabled; it doesn't need to be 
indexed.");
+        }
+        return JoinUtil.createJoinQuery(
+            fromField,
+            fromSchemaField.multiValued(),
+            toField,
+            numericClass(fromNumberType),
+            fromQuery,
+            fromSearcher,
+            scoreMode);
+      }
+      // A Point 'from' field can only be joined by point values: it has 
neither indexed terms nor
+      // the sorted/binary docValues the term-based join below needs, so fail 
with a clear message
+      // rather than letting Lucene stumble over the encoding.
+      if (fromSchemaField.getType().isPointField()) {
         throw new SolrException(
             SolrException.ErrorCode.BAD_REQUEST,
             "Numeric join 'from' field '"
@@ -311,14 +328,8 @@ public class ScoreJoinQParserPlugin extends QParserPlugin {
                 + (toSchemaField == null ? "undefined" : 
toSchemaField.getType().getTypeName())
                 + ".");
       }
-      return JoinUtil.createJoinQuery(
-          fromField,
-          fromSchemaField.multiValued(),
-          toField,
-          numericClass(fromNumberType),
-          fromQuery,
-          fromSearcher,
-          scoreMode);
+      // Legacy (non-Point) numeric 'from' field with a non-Point 'to' field: 
keep joining on terms,
+      // as it was done before numeric joins were introduced.
     }
     return JoinUtil.createJoinQuery(fromField, true, toField, fromQuery, 
fromSearcher, scoreMode);
   }
diff --git a/solr/core/src/test-files/solr/collection1/conf/schema12.xml 
b/solr/core/src/test-files/solr/collection1/conf/schema12.xml
index fb73c98e39e..b45642027e7 100644
--- a/solr/core/src/test-files/solr/collection1/conf/schema12.xml
+++ b/solr/core/src/test-files/solr/collection1/conf/schema12.xml
@@ -46,6 +46,11 @@
   <!-- explicit (non-templated) legacy Trie numeric field type with docValues, 
used to test that
        non-Point numeric fields can serve as the "from" side of a numeric 
score join -->
   <fieldType name="trieint_dv" class="solr.TrieIntField" docValues="true" 
precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
+
+  <!-- explicit (non-templated) legacy Trie numeric field type without 
docValues; multi-valued
+       instances of it are uninverted into SortedSetDocValues, which is what 
the term-based join
+       has always used for non-Point numerics -->
+  <fieldType name="trieint_nodv" class="solr.TrieIntField" docValues="false" 
precisionStep="0" omitNorms="true" positionIncrementGap="0"/>
   
   <!-- Point Fields -->
   <fieldType name="pint" class="solr.IntPointField" docValues="true"/>
@@ -767,6 +772,7 @@
   <dynamicField name="*_iis" type="pint" indexed="false" stored="false" 
useDocValuesAsStored="true"/>
   <dynamicField name="*_ff" type="pfloat" indexed="false" stored="false" 
useDocValuesAsStored="false"/>
   <dynamicField name="*_trie_i" type="trieint_dv" indexed="false" 
stored="false" useDocValuesAsStored="true"/>
+  <dynamicField name="*_trie_is" type="trieint_nodv" indexed="true" 
stored="true" multiValued="true"/>
 
   <!-- testing fields with & without norms
        TODO: Remove numeric norms for SOLR-14199 -->
diff --git 
a/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPScore.java 
b/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPScore.java
index 3b312ab8105..61e0a15263f 100644
--- a/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPScore.java
+++ b/solr/core/src/test/org/apache/solr/search/join/TestScoreJoinQPScore.java
@@ -302,6 +302,30 @@ public class TestScoreJoinQPScore extends SolrTestCaseJ4 {
         SolrException.ErrorCode.BAD_REQUEST);
   }
 
+  public void testLegacyNumericToLegacyNumericJoin() throws Exception {
+    clearIndex();
+
+    // Both sides are legacy (non-Point) Trie int fields without docValues. 
Such a pair can't use
+    // the point-based numeric join, but it has always worked through the 
term-based join, which
+    // reads the uninverted SortedSetDocValues of the "from" field, so it must 
keep working.
+    assertU(add(doc("name", "name1", idField, "1", "cat_trie_is", "100", 
"cat_trie_is", "300")));
+    assertU(add(doc("name", "name2", idField, "4", "cat_trie_is", "200")));
+
+    assertU(add(doc("price_s", "10.0", idField, "2", "prodRef_trie_is", 
"100")));
+    assertU(add(doc("price_s", "20.0", idField, "3", "prodRef_trie_is", 
"300")));
+    assertU(add(doc("price_s", "10.0", idField, "5", "prodRef_trie_is", 
"200")));
+
+    assertU(commit());
+
+    assertJQ(
+        req("q", "{!join from=cat_trie_is to=prodRef_trie_is 
score=None}name:name1", "fl", "id"),
+        
"/response=={'numFound':2,'start':0,'numFoundExact':true,'docs':[{'id':'2'},{'id':'3'}]}");
+
+    assertJQ(
+        req("q", "{!join from=cat_trie_is to=prodRef_trie_is 
score=None}name:name2", "fl", "id"),
+        
"/response=={'numFound':1,'start':0,'numFoundExact':true,'docs':[{'id':'5'}]}");
+  }
+
   public void testDeleteByScoreJoinQuery() throws Exception {
     indexDataForScoring();
     String joinQuery = "{!join from=" + toField + " to=" + idField + " 
score=Max}title:random";
diff --git 
a/solr/solr-ref-guide/modules/query-guide/pages/join-query-parser.adoc 
b/solr/solr-ref-guide/modules/query-guide/pages/join-query-parser.adoc
index bf1cacd8324..e62ec8b8614 100644
--- a/solr/solr-ref-guide/modules/query-guide/pages/join-query-parser.adoc
+++ b/solr/solr-ref-guide/modules/query-guide/pages/join-query-parser.adoc
@@ -127,6 +127,8 @@ This method must be used if score information is required, 
and should also be co
 The `dvWithScore` method supports numeric fields on the "from" side that have 
`docValues="true"` (single- or multi-valued), regardless of whether the field 
type is a `Point` field (`IntPointField`, `LongPointField`, `FloatPointField`, 
`DoublePointField`, `DatePointField`) or a legacy `Trie` numeric field, as long 
as the "to" side field uses the matching numeric `Point` field type.
 The "from" field only needs `docValues="true"`; it does not need to be 
`indexed="true"`.
 The "to" field, however, must be indexed as a `Point` field, since it is 
queried by point value.
+A `Point` "from" field paired with anything else raises an error.
+When neither side is a `Point` field, the join falls back to matching on 
indexed terms, exactly as it did in earlier versions.
 ====
 
 `topLevelDV`::: Can only be used when `to` and `from` fields have docValues 
data, and does not currently support numeric fields.

Reply via email to