You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ho...@apache.org on 2026/01/14 22:13:34 UTC
(solr) 01/02: flesh out some nocommits
This is an automated email from the ASF dual-hosted git repository.
hossman pushed a commit to branch jira/SOLR-17975
in repository https://gitbox.apache.org/repos/asf/solr.git
commit a51f2a3d3e8f88dec04390d9ff24f70bfe72ba9b
Author: Chris Hostetter <ho...@apache.org>
AuthorDate: Wed Jan 14 11:10:40 2026 -0700
flesh out some nocommits
---
.../solr/schema/LateInteractionVectorField.java | 74 +++++++++++++++-------
.../org/apache/solr/search/ValueSourceParser.java | 5 +-
.../schema/TestLateInteractionVectorFieldInit.java | 4 +-
.../solr/search/TestLateInteractionVectors.java | 38 +++++++----
4 files changed, 82 insertions(+), 39 deletions(-)
diff --git a/solr/core/src/java/org/apache/solr/schema/LateInteractionVectorField.java b/solr/core/src/java/org/apache/solr/schema/LateInteractionVectorField.java
index 10ea9f1939a..d0aa0ee98e3 100644
--- a/solr/core/src/java/org/apache/solr/schema/LateInteractionVectorField.java
+++ b/solr/core/src/java/org/apache/solr/schema/LateInteractionVectorField.java
@@ -32,6 +32,7 @@ import org.apache.lucene.index.VectorSimilarityFunction;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.search.DoubleValuesSource;
import org.apache.lucene.search.LateInteractionFloatValuesSource;
+import org.apache.lucene.search.LateInteractionFloatValuesSource.ScoreFunction;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SortField;
import org.apache.lucene.util.BytesRef;
@@ -52,6 +53,8 @@ public class LateInteractionVectorField extends FieldType {
public static final String SIMILARITY_FUNCTION = "similarityFunction";
public static final VectorSimilarityFunction DEFAULT_SIMILARITY =
VectorSimilarityFunction.EUCLIDEAN;
+ public static final String SCORE_FUNCTION = "scoreFunction";
+ public static final ScoreFunction DEFAULT_SCORE_FUNCTION = ScoreFunction.SUM_MAX_SIM;
private static final int MUST_BE_TRUE = DOC_VALUES;
private static final int MUST_BE_FALSE = MULTIVALUED | TOKENIZED | INDEXED | UNINVERTIBLE;
@@ -63,10 +66,7 @@ public class LateInteractionVectorField extends FieldType {
private int dimension;
private VectorSimilarityFunction similarityFunction;
-
- // nocommit: pre-emptively add ScoreFunction opt?
- // nocommit: if we don't add it now, write a test to fail if/when new options added to
- // ScoreFunction enum
+ private ScoreFunction scoreFunction;
public LateInteractionVectorField() {
super();
@@ -74,31 +74,32 @@ public class LateInteractionVectorField extends FieldType {
@Override
public void init(IndexSchema schema, Map<String, String> args) {
+
this.dimension =
- ofNullable(args.get(VECTOR_DIMENSION))
+ ofNullable(args.remove(VECTOR_DIMENSION))
.map(Integer::parseInt)
.orElseThrow(
() ->
new SolrException(
SolrException.ErrorCode.SERVER_ERROR,
VECTOR_DIMENSION + " is a mandatory parameter"));
- args.remove(VECTOR_DIMENSION);
- try {
- this.similarityFunction =
- ofNullable(args.get(SIMILARITY_FUNCTION))
- .map(value -> VectorSimilarityFunction.valueOf(value.toUpperCase(Locale.ROOT)))
- .orElse(DEFAULT_SIMILARITY);
- } catch (IllegalArgumentException e) {
- throw new SolrException(
- SolrException.ErrorCode.SERVER_ERROR,
- SIMILARITY_FUNCTION + " not recognized: " + args.get(SIMILARITY_FUNCTION));
- }
- args.remove(SIMILARITY_FUNCTION);
+ this.similarityFunction =
+ optionalEnumArg(
+ SIMILARITY_FUNCTION,
+ args.remove(SIMILARITY_FUNCTION),
+ VectorSimilarityFunction.class,
+ DEFAULT_SIMILARITY);
+ this.scoreFunction =
+ optionalEnumArg(
+ SCORE_FUNCTION,
+ args.remove(SCORE_FUNCTION),
+ ScoreFunction.class,
+ DEFAULT_SCORE_FUNCTION);
// By the time this method is called, FieldType.setArgs has already set "typical" defaults,
// and parsed the users explicit options.
- // We need to override those defaults, and error if the user asked for nonesense
+ // We need to override those defaults, and error if the user asked for nonsense
this.properties |= MUST_BE_TRUE;
this.properties &= ~MUST_BE_FALSE;
@@ -122,11 +123,17 @@ public class LateInteractionVectorField extends FieldType {
return similarityFunction;
}
+ public ScoreFunction getScoreFunction() {
+ return scoreFunction;
+ }
+
public DoubleValuesSource getMultiVecSimilarityValueSource(
final SchemaField f, final String vecStr) throws SyntaxError {
- // nocommit: use ScoreFunction here if we add it
return new LateInteractionFloatValuesSource(
- f.getName(), stringToMultiFloatVector(dimension, vecStr), getSimilarityFunction());
+ f.getName(),
+ stringToMultiFloatVector(dimension, vecStr),
+ getSimilarityFunction(),
+ getScoreFunction());
}
@Override
@@ -290,7 +297,7 @@ public class LateInteractionVectorField extends FieldType {
public void write(TextResponseWriter writer, String name, IndexableField f) throws IOException {
writer.writeStr(name, toExternal(f), false);
}
-
+
@Override
public Object toObject(SchemaField sf, BytesRef term) {
return multiFloatVectorToString(LateInteractionField.decode(term));
@@ -309,7 +316,8 @@ public class LateInteractionVectorField extends FieldType {
public ValueSource getValueSource(SchemaField field, QParser parser) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
- getClass().getSimpleName() + " not supported for function queries, use lateVector() function.");
+ getClass().getSimpleName()
+ + " not supported for function queries, use lateVector() function.");
}
/** Not supported */
@@ -317,7 +325,8 @@ public class LateInteractionVectorField extends FieldType {
public Query getFieldQuery(QParser parser, SchemaField field, String externalVal) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
- getClass().getSimpleName() + " not supported for field queries, use lateVector() function.");
+ getClass().getSimpleName()
+ + " not supported for field queries, use lateVector() function.");
}
/** Not Supported */
@@ -349,4 +358,23 @@ public class LateInteractionVectorField extends FieldType {
SolrException.ErrorCode.BAD_REQUEST,
getClass().getSimpleName() + " not supported for sorting.");
}
+
+ /**
+ * @param key Config option name, used in exception messages
+ * @param value Value specified in configuration, may be <code>null</code>
+ * @param clazz Enum class specifying the return type
+ * @param defaultValue default to use if value is <code>null</code>
+ */
+ private static final <E extends Enum<E>> E optionalEnumArg(
+ final String key, final String value, final Class<E> clazz, final E defaultValue)
+ throws SolrException {
+ try {
+ return ofNullable(value)
+ .map(v -> Enum.valueOf(clazz, v.toUpperCase(Locale.ROOT)))
+ .orElse(defaultValue);
+ } catch (IllegalArgumentException e) {
+ throw new SolrException(
+ SolrException.ErrorCode.SERVER_ERROR, key + " not recognized: " + value);
+ }
+ }
}
diff --git a/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java b/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java
index fa03a6b2931..bbd9387e0b0 100644
--- a/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java
+++ b/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java
@@ -1377,10 +1377,9 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
"Invalid number of arguments. Please provide both a field name, and a (String) multi-vector.");
}
final SchemaField sf = fp.getReq().getSchema().getField(fieldName);
- if (sf.getType() instanceof LateInteractionVectorField) {
+ if (sf.getType() instanceof LateInteractionVectorField lif) {
return ValueSource.fromDoubleValuesSource(
- ((LateInteractionVectorField) sf.getType())
- .getMultiVecSimilarityValueSource(sf, vecStr));
+ lif.getMultiVecSimilarityValueSource(sf, vecStr));
}
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
diff --git a/solr/core/src/test/org/apache/solr/schema/TestLateInteractionVectorFieldInit.java b/solr/core/src/test/org/apache/solr/schema/TestLateInteractionVectorFieldInit.java
index cda86969dc0..4f154ef5b79 100644
--- a/solr/core/src/test/org/apache/solr/schema/TestLateInteractionVectorFieldInit.java
+++ b/solr/core/src/test/org/apache/solr/schema/TestLateInteractionVectorFieldInit.java
@@ -48,7 +48,9 @@ public class TestLateInteractionVectorFieldInit extends AbstractBadConfigTestBas
assertConfigs(
"solrconfig-basic.xml", "bad-schema-late-vec-field-indexed.xml", "indexed: bad_field");
assertConfigs(
- "solrconfig-basic.xml", "bad-schema-late-vec-field-multivalued.xml", "multiValued: bad_field");
+ "solrconfig-basic.xml",
+ "bad-schema-late-vec-field-multivalued.xml",
+ "multiValued: bad_field");
}
public void test_SchemaFields() throws Exception {
diff --git a/solr/core/src/test/org/apache/solr/search/TestLateInteractionVectors.java b/solr/core/src/test/org/apache/solr/search/TestLateInteractionVectors.java
index e9a2dc4a958..5d1e3b7c308 100644
--- a/solr/core/src/test/org/apache/solr/search/TestLateInteractionVectors.java
+++ b/solr/core/src/test/org/apache/solr/search/TestLateInteractionVectors.java
@@ -22,16 +22,16 @@ import static org.apache.solr.schema.LateInteractionVectorField.stringToMultiFlo
import static org.hamcrest.Matchers.startsWith;
import java.util.Arrays;
+import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import org.apache.lucene.document.LateInteractionField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.VectorSimilarityFunction;
+import org.apache.lucene.search.LateInteractionFloatValuesSource.ScoreFunction;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.request.SolrQueryRequest;
-import org.apache.solr.schema.FieldType;
-import org.apache.solr.schema.LateInteractionVectorField;
import org.apache.solr.schema.SchemaField;
import org.junit.After;
import org.junit.Before;
@@ -50,6 +50,18 @@ public class TestLateInteractionVectors extends SolrTestCaseJ4 {
deleteCore();
}
+ public void testFutureProofAgainstNewScoreFunctions() throws Exception {
+ // if this assert fails, it means there are new value(s) in the ScoreFunction enum,
+ // and we need to add fieldType declarations using those new ScoreFunctions to our test
+ // configs, and confirm the correct score function is used in various tests
+ //
+ // then remove this test method
+ assertEquals(
+ "The ScoreFunction enum in Lucene now has more then value, test needs updated",
+ EnumSet.of(ScoreFunction.SUM_MAX_SIM),
+ EnumSet.allOf(ScoreFunction.class));
+ }
+
public void testStringEncodingAndDecoding() throws Exception {
final int DIMENSIONS = 4;
@@ -143,10 +155,12 @@ public class TestLateInteractionVectors extends SolrTestCaseJ4 {
/** Low level test of createFields */
public void createFields() throws Exception {
- final Map<String,float[][]> data = Map.of("[[1,2,3,4]]",
- new float[][] { { 1F, 2F, 3F, 4F }},
- "[[1,2,3,4],[5,6,7,8]]",
- new float[][] { { 1F, 2F, 3F, 4F }, { 5F, 6F, 7F, 8F }});
+ final Map<String, float[][]> data =
+ Map.of(
+ "[[1,2,3,4]]",
+ new float[][] {{1F, 2F, 3F, 4F}},
+ "[[1,2,3,4],[5,6,7,8]]",
+ new float[][] {{1F, 2F, 3F, 4F}, {5F, 6F, 7F, 8F}});
try (SolrQueryRequest r = req()) {
// defaults with stored + doc values
@@ -155,7 +169,7 @@ public class TestLateInteractionVectors extends SolrTestCaseJ4 {
final float[][] expected = data.get(input);
final List<IndexableField> actual = f.getType().createFields(f, input);
assertEquals(2, actual.size());
-
+
if (actual.get(0) instanceof LateInteractionField lif) {
assertEquals(expected, lif.getValue());
} else {
@@ -167,14 +181,14 @@ public class TestLateInteractionVectors extends SolrTestCaseJ4 {
fail("second Field isn't stored: " + actual.get(1).getClass());
}
}
-
+
// stored=false, only doc values
for (String input : data.keySet()) {
final SchemaField f = r.getSchema().getField("lv_4_nostored");
final float[][] expected = data.get(input);
final List<IndexableField> actual = f.getType().createFields(f, input);
assertEquals(1, actual.size());
-
+
if (actual.get(0) instanceof LateInteractionField lif) {
assertEquals(expected, lif.getValue());
} else {
@@ -183,7 +197,7 @@ public class TestLateInteractionVectors extends SolrTestCaseJ4 {
}
}
}
-
+
public void testSimpleIndexAndRetrieval() throws Exception {
// for simplicity, use a single doc, with identical values in several fields
@@ -244,8 +258,8 @@ public class TestLateInteractionVectors extends SolrTestCaseJ4 {
"//str[@name='lv_4_cosine'][.='" + d4s + "']",
// dv only non-stored fields
- "//str[@name='lv_3_nostored'][.='"+d3s+"']",
- "//str[@name='lv_4_nostored'][.='"+d4s+"']",
+ "//str[@name='lv_3_nostored'][.='" + d3s + "']",
+ "//str[@name='lv_4_nostored'][.='" + d4s + "']",
// function computations
"//float[@name='euclid_3_def'][.=" + euclid3 + "]",