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.