From e3432d90c2fb49b3c18d1fd13dee07b8a38f5dbb Mon Sep 17 00:00:00 2001 From: Andrey Bozhko Date: Tue, 29 Apr 2025 09:43:06 -0500 Subject: [PATCH 01/10] changes --- solr/CHANGES.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index 5501eb95017..b80350bcdb5 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -17,6 +17,8 @@ New Features * SOLR-14414: Introduce a new, experimental Admin UI that can be used aside the current Admin UI. The new module is loaded together with the existing Admin UI and available under the URL path /solr/compose. (Christos Malliaridis) +* SOLR-15030: Add score() function that returns the score of the current hit. The function can be used in pseudo-fields and post-filters. (Andrey Bozhko) + Improvements --------------------- From c070dd1c3d618566a22f732ab87b98459e6ccc4a Mon Sep 17 00:00:00 2001 From: Andrey Bozhko Date: Tue, 29 Apr 2025 09:43:53 -0500 Subject: [PATCH 02/10] add score() function --- .../transform/ValueSourceAugmenter.java | 28 +- .../solr/search/FunctionRangeQuery.java | 31 +++ .../apache/solr/search/ValueSourceParser.java | 9 + .../solr/search/function/ScoreFunction.java | 72 +++++ .../apache/solr/search/QueryEqualityTest.java | 4 + .../search/function/ScoreFunctionTest.java | 256 ++++++++++++++++++ .../query-guide/pages/function-queries.adoc | 11 + 7 files changed, 410 insertions(+), 1 deletion(-) create mode 100644 solr/core/src/java/org/apache/solr/search/function/ScoreFunction.java create mode 100644 solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java diff --git a/solr/core/src/java/org/apache/solr/response/transform/ValueSourceAugmenter.java b/solr/core/src/java/org/apache/solr/response/transform/ValueSourceAugmenter.java index 2b5af93e56b..cd476a4d909 100644 --- a/solr/core/src/java/org/apache/solr/response/transform/ValueSourceAugmenter.java +++ b/solr/core/src/java/org/apache/solr/response/transform/ValueSourceAugmenter.java @@ -23,6 +23,7 @@ import org.apache.lucene.index.ReaderUtil; import org.apache.lucene.queries.function.FunctionValues; import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.search.Scorable; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrException; import org.apache.solr.response.ResultContext; @@ -79,8 +80,13 @@ public void transform(SolrDocument doc, int docid, DocIterationInfo docInfo) { // TODO: calculate this stuff just once across diff functions int idx = ReaderUtil.subIndex(docid, readerContexts); LeafReaderContext rcontext = readerContexts.get(idx); - FunctionValues values = valueSource.getValues(fcontext, rcontext); int localId = docid - rcontext.docBase; + + if (context.wantsScores()) { + fcontext.put("scorer", new ScoreAndDoc(localId, docInfo.score())); + } + + FunctionValues values = valueSource.getValues(fcontext, rcontext); setValue(doc, values.objectVal(localId)); } catch (IOException e) { throw new SolrException( @@ -101,4 +107,24 @@ protected void setValue(SolrDocument doc, Object val) { doc.setField(name, val); } } + + private static class ScoreAndDoc extends Scorable { + private final int docId; + private final float score; + + ScoreAndDoc(int docId, float score) { + this.docId = docId; + this.score = score; + } + + @Override + public int docID() { + return docId; + } + + @Override + public float score() { + return score; + } + } } diff --git a/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java b/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java index db71c7d6589..f10aae39071 100644 --- a/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java +++ b/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java @@ -25,6 +25,7 @@ import org.apache.lucene.queries.function.ValueSourceScorer; import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.QueryVisitor; +import org.apache.lucene.search.Scorable; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.Weight; import org.apache.solr.common.SolrException; @@ -68,6 +69,14 @@ class FunctionRangeCollector extends DelegatingCollector { public FunctionRangeCollector(Map fcontext, Weight weight) { this.fcontext = fcontext; this.weight = weight; + // It would make sense to put this.scorer into the context, + // so that the context can be consumed in the call to + // doSetNextReader. But this.scorer is null, and will only be + // set at a later point by DelegatingCollector#setScorer + // (which happens *after* the call to doSetNextReader). + // So instead of using this.scorer directly, we wrap this collector + // as scorable - to work around the late initialization of this.scorer. + this.fcontext.put("scorer", new DelegatingCollectorWrapper(this)); } @Override @@ -113,4 +122,26 @@ public boolean equals(Object obj) { public int hashCode() { return 31 * classHash() + rangeFilt.hashCode(); } + + private static class DelegatingCollectorWrapper extends Scorable { + private final DelegatingCollector collector; + + DelegatingCollectorWrapper(DelegatingCollector collector) { + this.collector = collector; + } + + @Override + public float score() throws IOException { + assert collector.scorer != null + : "scorer must first be set via DelegatingCollector#setScorer"; + return collector.scorer.score(); + } + + @Override + public int docID() { + assert collector.scorer != null + : "scorer must first be set via DelegatingCollector#setScorer"; + return collector.scorer.docID(); + } + } } 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 2cd071a8938..28d11cd0d6c 100644 --- a/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java +++ b/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java @@ -106,6 +106,7 @@ import org.apache.solr.search.function.EqualFunction; import org.apache.solr.search.function.OrdFieldSource; import org.apache.solr.search.function.ReverseOrdFieldSource; +import org.apache.solr.search.function.ScoreFunction; import org.apache.solr.search.function.SolrComparisonBoolFunction; import org.apache.solr.search.function.distance.GeoDistValueSourceParser; import org.apache.solr.search.function.distance.GeohashFunction; @@ -330,6 +331,14 @@ public ValueSource parse(FunctionQParser fp) throws SyntaxError { return new CollapseScoreFunction(); } }); + addParser( + "score", + new ValueSourceParser() { + @Override + public ValueSource parse(FunctionQParser fp) { + return new ScoreFunction(); + } + }); addParser( "sum", new ValueSourceParser() { diff --git a/solr/core/src/java/org/apache/solr/search/function/ScoreFunction.java b/solr/core/src/java/org/apache/solr/search/function/ScoreFunction.java new file mode 100644 index 00000000000..12bf904ecda --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/function/ScoreFunction.java @@ -0,0 +1,72 @@ +/* + * 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.solr.search.function; + +import java.io.IOException; +import java.util.Map; +import org.apache.lucene.index.LeafReaderContext; +import org.apache.lucene.queries.function.FunctionValues; +import org.apache.lucene.queries.function.ValueSource; +import org.apache.lucene.queries.function.docvalues.FloatDocValues; +import org.apache.lucene.search.Scorable; +import org.apache.solr.common.SolrException; + +/** Returns the score of the current hit. */ +public final class ScoreFunction extends ValueSource { + + @Override + public FunctionValues getValues(Map context, LeafReaderContext readerContext) + throws IOException { + Scorable scorer = (Scorable) context.get("scorer"); + if (scorer == null) { + throw new SolrException( + SolrException.ErrorCode.BAD_REQUEST, + "score() function cannot access the document scores"); + } + + return new FloatDocValues(this) { + @Override + public float floatVal(int doc) throws IOException { + assert scorer.docID() == doc + : "Expected scorer to be positioned on doc: " + doc + ", but was: " + scorer.docID(); + return scorer.score(); + } + + @Override + public boolean exists(int doc) { + assert scorer.docID() == doc + : "Expected scorer to be positioned on doc: " + doc + ", but was: " + scorer.docID(); + return true; + } + }; + } + + @Override + public boolean equals(Object o) { + return o instanceof ScoreFunction; + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + + @Override + public String description() { + return "score()"; + } +} diff --git a/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java b/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java index 8df761740ae..6ac644bce16 100644 --- a/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java +++ b/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java @@ -858,6 +858,10 @@ public void testFuncCscore() throws Exception { assertFuncEquals("cscore()", "cscore( )"); } + public void testFuncScore() throws Exception { + assertFuncEquals("score()", "score( )"); + } + public void testFuncTop() throws Exception { assertFuncEquals("top(sum(3,foo_i))"); } diff --git a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java new file mode 100644 index 00000000000..97775bf83f8 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java @@ -0,0 +1,256 @@ +/* + * 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.solr.search.function; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apache.solr.SolrTestCaseJ4; +import org.apache.solr.common.SolrException; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ScoreFunctionTest extends SolrTestCaseJ4 { + + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig-functionquery.xml", "schema11.xml"); + + addDocsInRandomOrder(); + } + + private static void addDocsInRandomOrder() { + // add documents to the collection, but randomize + // the number of segments, and which specific docs + // are added to each segment - this is especially + // useful for testing score function in pseudo fields + List updates = + Arrays.asList( + commit(), + commit(), + commit(), + adoc("id", "1", "text", "foo"), + adoc("id", "2", "text", "bar"), + adoc("id", "3", "text", "qux"), + adoc("id", "4", "text", "asd"), + adoc("id", "101", "text", "random text"), + adoc("id", "102", "text", "random text"), + adoc("id", "103", "text", "random text"), + adoc("id", "104", "text", "random text"), + adoc("id", "105", "text", "random text"), + adoc("id", "106", "text", "random text"), + adoc("id", "107", "text", "random text"), + adoc("id", "108", "text", "random text"), + adoc("id", "109", "text", "random text")); + + Collections.shuffle(updates, random()); + + for (var update : updates) { + assertU(update); + } + + assertU(commit()); + } + + @Test + public void testScoreFunction_boostQuery() throws Exception { + assertJQ( + req("q", "foo^=1 bar^=2 qux^=3 asd^=4", "df", "text", "fl", "id,score"), + "/response/numFound==4", + "/response/docs/[0]/id=='4'", + "/response/docs/[0]/score==4.0", + "/response/docs/[1]/id=='3'", + "/response/docs/[1]/score==3.0", + "/response/docs/[2]/id=='2'", + "/response/docs/[2]/score==2.0", + "/response/docs/[3]/id=='1'", + "/response/docs/[3]/score==1.0"); + + // boost function that relies on score() + assertJQ( + req( + "q", + "{!boost b=if(lte(score(),2),2.5,1)}foo^=1 bar^=2 qux^=3 asd^=4", + "df", + "text", + "fl", + "id,score"), + "/response/numFound==4", + "/response/docs/[0]/id=='2'", + "/response/docs/[0]/score==5.0", + "/response/docs/[1]/id=='4'", + "/response/docs/[1]/score==4.0", + "/response/docs/[2]/id=='3'", + "/response/docs/[2]/score==3.0", + "/response/docs/[3]/id=='1'", + "/response/docs/[3]/score==2.5"); + } + + @Test + public void testScoreFunction_postFilter() throws Exception { + // frange query as postfilter + assertJQ( + req( + "q", + "foo^=1 bar^=2 qux^=3 asd^=4", + "df", + "text", + "fl", + "id,score", + "fq", + "{!frange l=2 u=3 cache=false}score()"), + "/response/numFound==2", + "/response/docs/[0]/id=='3'", + "/response/docs/[0]/score==3.0", + "/response/docs/[1]/id=='2'", + "/response/docs/[1]/score==2.0"); + + // doesn't work if not a postfilter + assertThrows( + SolrException.class, + () -> + assertJQ( + req( + "q", + "foo^=1 bar^=2 qux^=3 asd^=4", + "df", + "text", + "fl", + "id,score", + "fq", + "{!frange l=2 u=3}score()"))); + } + + @Test + public void testScoreFunction_pseudoField() throws Exception { + assertJQ( + req( + "q", "foo^=1 bar^=2 qux^=3", + "df", "text", + "fl", "id,score,custom:add(1,score(),score())"), + "/response/numFound==3", + "/response/docs/[0]/id=='3'", + "/response/docs/[0]/score==3.0", + "/response/docs/[0]/custom==7.0", + "/response/docs/[1]/id=='2'", + "/response/docs/[1]/score==2.0", + "/response/docs/[1]/custom==5.0", + "/response/docs/[2]/id=='1'", + "/response/docs/[2]/score==1.0", + "/response/docs/[2]/custom==3.0"); + + // error if scores not enabled + assertThrows( + SolrException.class, + () -> + assertJQ( + req( + "q", "foo^=1 bar^=2 qux^=3", + "df", "text", + "fl", "id,custom:add(1,score(),score())"))); + } + + @Test + public void testScoreFunction_nested() throws Exception { + assertJQ( + req( + "json", + """ + { + "query": { + "boost": { + "b": "if(lte(score(),6),1,0.1)", + "query": { + "boost": { + "b": "if(gte(score(),3),2,1)", + "query": "foo^=1 bar^=2 qux^=3 asd^=4" + } + } + } + } + }""", + "df", "text", + "fl", "id,score"), + "/response/numFound==4", + "/response/docs/[0]/id=='3'", + "/response/docs/[0]/score==6.0", + "/response/docs/[1]/id=='2'", + "/response/docs/[1]/score==2.0", + "/response/docs/[2]/id=='1'", + "/response/docs/[2]/score==1.0", + "/response/docs/[3]/id=='4'", + "/response/docs/[3]/score==0.8"); + + assertJQ( + req( + "json", + """ + { + "query": { + "boost": { + "b": "if(gte(score(),10),0.1,1)", + "query": { + "boost": { + "b": "if(eq(query($q1),score()),100,1)", + "query": "foo^=1 bar^=2 qux^=3 asd^=4" + } + } + } + }, + "queries": { + "q1": { + "boost": { + "b": "if(eq(score(),3),1,2)", + "query": "qux^=3 asd^=4" + } + } + } + }""", + "df", "text", + "fl", "id,score"), + "/response/numFound==4", + "/response/docs/[0]/id=='3'", + "/response/docs/[0]/score==30.0", + "/response/docs/[1]/id=='4'", + "/response/docs/[1]/score==4.0", + "/response/docs/[2]/id=='2'", + "/response/docs/[2]/score==2.0", + "/response/docs/[3]/id=='1'", + "/response/docs/[3]/score==1.0"); + } + + @Test + public void testScoreFunction_combined() throws Exception { + assertJQ( + req( + "q", "{!boost b=if(gte(score(),3),2,1) v=$qq}", + "qq", "foo^=1 bar^=2 qux^=3 asd^=4", + "fq", "{!frange cache=false u=7}score()", + "df", "text", + "fl", "id,score,score_plus_one:add(1,score())"), + "/response/numFound==3", + "/response/docs/[0]/id=='3'", + "/response/docs/[0]/score==6.0", + "/response/docs/[0]/score_plus_one==7.0", + "/response/docs/[1]/id=='2'", + "/response/docs/[1]/score==2.0", + "/response/docs/[1]/score_plus_one==3.0", + "/response/docs/[2]/id=='1'", + "/response/docs/[2]/score==1.0", + "/response/docs/[2]/score_plus_one==2.0"); + } +} diff --git a/solr/solr-ref-guide/modules/query-guide/pages/function-queries.adoc b/solr/solr-ref-guide/modules/query-guide/pages/function-queries.adoc index 7c6f1a9d0ea..92bfb3184f3 100644 --- a/solr/solr-ref-guide/modules/query-guide/pages/function-queries.adoc +++ b/solr/solr-ref-guide/modules/query-guide/pages/function-queries.adoc @@ -447,6 +447,17 @@ In these cases, an appropriate `map()` function could be used as a workaround to * `scale(x, minTarget, maxTarget)` * `scale(x,1,2)`: scales the values of x such that all values will be between 1 and 2 inclusive. +=== score Function +Returns the score of the current document. + +The function can only be used in a few specific ways - see examples below for what is currently supported. + +*Syntax Examples* + +* `fl=*,score,score_plus_one:add(1,score())`: a xref:common-query-parameters.adoc#functions-with-fl[pseudo-field] that computes the field value based on the document score, +* `fq={!frange cache=false l=4}score()`: a xref:common-query-parameters.adoc#cache-local-parameter[post filter] that filters the hits based on their score, +* `q={!boost b=if(gte(score(),10),2,1)}solrrocks`: a boost query that determines the boost factor from the document score produced by the underlying query. + === sqedist Function The Square Euclidean distance calculates the 2-norm (Euclidean distance) but does not take the square root, thus saving a fairly expensive operation. It is often the case that applications that care about Euclidean distance do not need the actual distance, but instead can use the square of the distance. From 4e8f9f2aeb12cfc061552a73a046f7287302cf04 Mon Sep 17 00:00:00 2001 From: Andrey Bozhko Date: Fri, 2 May 2025 16:46:19 -0500 Subject: [PATCH 03/10] address comments --- .../src/java/org/apache/solr/search/FunctionRangeQuery.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java b/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java index f10aae39071..efe76dc521c 100644 --- a/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java +++ b/solr/core/src/java/org/apache/solr/search/FunctionRangeQuery.java @@ -76,7 +76,7 @@ public FunctionRangeCollector(Map fcontext, Weight weight) { // (which happens *after* the call to doSetNextReader). // So instead of using this.scorer directly, we wrap this collector // as scorable - to work around the late initialization of this.scorer. - this.fcontext.put("scorer", new DelegatingCollectorWrapper(this)); + this.fcontext.put("scorer", new ScorableView(this)); } @Override @@ -123,10 +123,10 @@ public int hashCode() { return 31 * classHash() + rangeFilt.hashCode(); } - private static class DelegatingCollectorWrapper extends Scorable { + private static class ScorableView extends Scorable { private final DelegatingCollector collector; - DelegatingCollectorWrapper(DelegatingCollector collector) { + ScorableView(DelegatingCollector collector) { this.collector = collector; } From 190d8ea871134b22c3be71e2511f812ff6f3d117 Mon Sep 17 00:00:00 2001 From: Andrey Bozhko Date: Fri, 2 May 2025 16:46:09 -0500 Subject: [PATCH 04/10] make sure distrib queries work as well --- .../apache/solr/search/SolrReturnFields.java | 10 +- .../function/ScoreFunctionDistribTest.java | 188 ++++++++++++++++++ 2 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java diff --git a/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java b/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java index 36b0a7b885b..fd7ac683761 100644 --- a/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java +++ b/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java @@ -61,7 +61,7 @@ public class SolrReturnFields extends ReturnFields { // Field names that are OK to include in the response. // This will include pseudo fields, lucene fields, and matching globs - private Set okFieldNames = new HashSet<>(); + private final Set okFieldNames = new HashSet<>(); // The list of explicitly requested fields // Order is important for CSVResponseWriter @@ -547,6 +547,12 @@ private void addField( String disp = (key == null) ? field : key; augmenters.addTransformer(new MatchScoreAugmenter(disp)); scoreDependentFields.put(disp, disp.equals(MATCH_SCORE) ? "" : MATCH_SCORE); + } else if (key != null && isPseudoField) { + // SOLR-15030: a pseudo-field based on the function query may need scores, + // so we consider all pseudo-fields as potentially requiring scores. + // At the same time, we don't set _wantScore = true because the field + // list must explicitly include 'score' field to enable scores + scoreDependentFields.put(key, ""); } } @@ -608,7 +614,7 @@ public boolean wantsScore() { @Override public Map getScoreDependentReturnFields() { - return scoreDependentFields; + return _wantsScore ? scoreDependentFields : Map.of(); } @Override diff --git a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java new file mode 100644 index 00000000000..acb67ceacf2 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java @@ -0,0 +1,188 @@ +package org.apache.solr.search.function; + +import java.util.List; +import java.util.concurrent.TimeUnit; +import org.apache.solr.client.solrj.SolrClient; +import org.apache.solr.client.solrj.request.CollectionAdminRequest; +import org.apache.solr.client.solrj.request.UpdateRequest; +import org.apache.solr.cloud.SolrCloudTestCase; +import org.apache.solr.common.SolrException; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ScoreFunctionDistribTest extends SolrCloudTestCase { + + private static final String COLLECTION = "coll1"; + + private static SolrClient client; + + @BeforeClass + public static void configureSolr() throws Exception { + configureCluster(2).addConfig("config1", configset("cloud-minimal")).configure(); + + client = cluster.getSolrClient(); + + prepareCollection(); + } + + private static void prepareCollection() throws Exception { + var createResponse = + CollectionAdminRequest.createCollection(COLLECTION, "config1", 2, 1).process(client); + assertEquals(0, createResponse.getStatus()); + + cluster.waitForActiveCollection(COLLECTION, 10, TimeUnit.SECONDS); + + var updateResponse = + new UpdateRequest() + .add( + List.of( + sdoc("id", "1", "text_s", "foo"), + sdoc("id", "2", "text_s", "bar"), + sdoc("id", "3", "text_s", "qux"), + sdoc("id", "4", "text_s", "asd"), + sdoc("id", "101", "text_s", "random text"), + sdoc("id", "102", "text_s", "random text"), + sdoc("id", "103", "text_s", "random text"), + sdoc("id", "104", "text_s", "random text"), + sdoc("id", "105", "text_s", "random text"), + sdoc("id", "106", "text_s", "random text"), + sdoc("id", "107", "text_s", "random text"), + sdoc("id", "108", "text_s", "random text"), + sdoc("id", "109", "text_s", "random text"))) + .commit(client, COLLECTION); + assertEquals(0, updateResponse.getStatus()); + } + + @Test + public void testScoreFunction_boostQuery() throws Exception { + var resp = + client.query( + COLLECTION, + params( + "q", + "{!boost b=if(lte(score(),2),2.5,1)}foo^=1 bar^=2 qux^=3 asd^=4", + "df", + "text_s", + "fl", + "id,score")); + + assertJSONEquals( + """ + { + "numFound": 4, + "start": 0, + "maxScore": 5.0, + "numFoundExact": true, + "docs": [ + { + "id": "2", + "score": 5.0 + }, + { + "id": "4", + "score": 4.0 + }, + { + "id": "3", + "score": 3.0 + }, + { + "id": "1", + "score": 2.5 + } + ] + }""", + resp.getResults().jsonStr()); + } + + @Test + public void testScoreFunction_postFilter() throws Exception { + var resp = + client.query( + COLLECTION, + params( + "q", + "foo^=1 bar^=2 qux^=3 asd^=4", + "df", + "text_s", + "fl", + "id,score", + "fq", + "{!frange l=2 u=3 cache=false}score()")); + + assertJSONEquals( + """ + { + "numFound": 2, + "start": 0, + "maxScore": 3.0, + "numFoundExact": true, + "docs": [ + { + "id": "3", + "score": 3.0 + }, + { + "id": "2", + "score": 2.0 + } + ] + }""", + resp.getResults().jsonStr()); + } + + @Test + public void testScoreFunction_pseudoField() throws Exception { + var resp = + client.query( + COLLECTION, + params( + "q", + "foo^=1 bar^=2 qux^=3", + "df", + "text_s", + "fl", + "id,score,custom:add(1,score(),score())")); + + assertJSONEquals( + """ + { + "numFound": 3, + "start": 0, + "maxScore": 3.0, + "numFoundExact": true, + "docs": [ + { + "id": "3", + "score": 3.0, + "custom": 7.0 + }, + { + "id": "2", + "score": 2.0, + "custom": 5.0 + }, + { + "id": "1", + "score": 1.0, + "custom": 3.0 + } + ] + }""", + resp.getResults().jsonStr()); + + // error if scores not enabled + assertThrows( + SolrException.class, + () -> + client.query( + COLLECTION, + params( + "q", + "foo^=1 bar^=2 qux^=3", + "df", + "text_s", + "fl", + "id,custom:add(1,score(),score())"))); + } +} From 1377ffb76574830b955ed0f42c5f53b3fbea35ef Mon Sep 17 00:00:00 2001 From: David Smiley Date: Tue, 6 May 2025 13:15:59 -0400 Subject: [PATCH 05/10] sort doesn't work --- .../solr/search/function/ScoreFunctionTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java index 97775bf83f8..77b88f9a090 100644 --- a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java +++ b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java @@ -21,6 +21,7 @@ import java.util.List; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; +import org.apache.solr.common.SolrException.ErrorCode; import org.junit.BeforeClass; import org.junit.Test; @@ -253,4 +254,17 @@ public void testScoreFunction_combined() throws Exception { "/response/docs/[2]/score==1.0", "/response/docs/[2]/score_plus_one==2.0"); } + + // @Ignore // TODO would like this to work someday + @Test + public void testSortFunction_sort() throws Exception { + // TODO incorporate a docValues value, and some assertions on the order + assertQEx( + "unsupported", + "score() function cannot access the document scores", + req( + "q", "*:*", + "sort", "div(1,score()) desc"), + ErrorCode.BAD_REQUEST); + } } From 11f80131e32006f6eba5aa30dd1153878383daa3 Mon Sep 17 00:00:00 2001 From: Andrey Bozhko Date: Mon, 7 Jul 2025 22:52:35 -0500 Subject: [PATCH 06/10] support score instead of score() --- .../apache/solr/search/FunctionQParser.java | 3 ++ .../apache/solr/search/ValueSourceParser.java | 9 ------ .../solr/search/function/ScoreFunction.java | 7 ++-- .../function/ScoreFunctionDistribTest.java | 8 ++--- .../search/function/ScoreFunctionTest.java | 32 +++++++++---------- .../query-guide/pages/function-queries.adoc | 6 ++-- 6 files changed, 30 insertions(+), 35 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/search/FunctionQParser.java b/solr/core/src/java/org/apache/solr/search/FunctionQParser.java index 97b1fb35d22..8481ecc6c29 100644 --- a/solr/core/src/java/org/apache/solr/search/FunctionQParser.java +++ b/solr/core/src/java/org/apache/solr/search/FunctionQParser.java @@ -35,6 +35,7 @@ import org.apache.solr.schema.SchemaField; import org.apache.solr.search.facet.AggValueSource; import org.apache.solr.search.function.FieldNameValueSource; +import org.apache.solr.search.function.ScoreFunction; public class FunctionQParser extends QParser { @@ -485,6 +486,8 @@ protected ValueSource parseValueSource(int flags) throws SyntaxError { valueSource = ValueSourceParser.BoolConstValueSource.TRUE; } else if ("false".equals(id)) { valueSource = ValueSourceParser.BoolConstValueSource.FALSE; + } else if ("score".equals(id)) { + valueSource = ScoreFunction.INSTANCE; } else { if ((flags & FLAG_USE_FIELDNAME_SOURCE) != 0) { // Don't try to create a ValueSource for the field, just use a placeholder. 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 28d11cd0d6c..2cd071a8938 100644 --- a/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java +++ b/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java @@ -106,7 +106,6 @@ import org.apache.solr.search.function.EqualFunction; import org.apache.solr.search.function.OrdFieldSource; import org.apache.solr.search.function.ReverseOrdFieldSource; -import org.apache.solr.search.function.ScoreFunction; import org.apache.solr.search.function.SolrComparisonBoolFunction; import org.apache.solr.search.function.distance.GeoDistValueSourceParser; import org.apache.solr.search.function.distance.GeohashFunction; @@ -331,14 +330,6 @@ public ValueSource parse(FunctionQParser fp) throws SyntaxError { return new CollapseScoreFunction(); } }); - addParser( - "score", - new ValueSourceParser() { - @Override - public ValueSource parse(FunctionQParser fp) { - return new ScoreFunction(); - } - }); addParser( "sum", new ValueSourceParser() { diff --git a/solr/core/src/java/org/apache/solr/search/function/ScoreFunction.java b/solr/core/src/java/org/apache/solr/search/function/ScoreFunction.java index 12bf904ecda..64e57321f2a 100644 --- a/solr/core/src/java/org/apache/solr/search/function/ScoreFunction.java +++ b/solr/core/src/java/org/apache/solr/search/function/ScoreFunction.java @@ -28,14 +28,15 @@ /** Returns the score of the current hit. */ public final class ScoreFunction extends ValueSource { + public static final ScoreFunction INSTANCE = new ScoreFunction(); + @Override public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException { Scorable scorer = (Scorable) context.get("scorer"); if (scorer == null) { throw new SolrException( - SolrException.ErrorCode.BAD_REQUEST, - "score() function cannot access the document scores"); + SolrException.ErrorCode.BAD_REQUEST, "score function cannot access the document scores"); } return new FloatDocValues(this) { @@ -67,6 +68,6 @@ public int hashCode() { @Override public String description() { - return "score()"; + return "score"; } } diff --git a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java index acb67ceacf2..6ab07e01ab4 100644 --- a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java +++ b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java @@ -60,7 +60,7 @@ public void testScoreFunction_boostQuery() throws Exception { COLLECTION, params( "q", - "{!boost b=if(lte(score(),2),2.5,1)}foo^=1 bar^=2 qux^=3 asd^=4", + "{!boost b=if(lte(score,2),2.5,1)}foo^=1 bar^=2 qux^=3 asd^=4", "df", "text_s", "fl", @@ -108,7 +108,7 @@ public void testScoreFunction_postFilter() throws Exception { "fl", "id,score", "fq", - "{!frange l=2 u=3 cache=false}score()")); + "{!frange l=2 u=3 cache=false}score")); assertJSONEquals( """ @@ -142,7 +142,7 @@ public void testScoreFunction_pseudoField() throws Exception { "df", "text_s", "fl", - "id,score,custom:add(1,score(),score())")); + "id,score,custom:add(1,score,score)")); assertJSONEquals( """ @@ -183,6 +183,6 @@ public void testScoreFunction_pseudoField() throws Exception { "df", "text_s", "fl", - "id,custom:add(1,score(),score())"))); + "id,custom:add(1,score,score)"))); } } diff --git a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java index 77b88f9a090..4dc503fd352 100644 --- a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java +++ b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java @@ -81,11 +81,11 @@ public void testScoreFunction_boostQuery() throws Exception { "/response/docs/[3]/id=='1'", "/response/docs/[3]/score==1.0"); - // boost function that relies on score() + // boost function that relies on score assertJQ( req( "q", - "{!boost b=if(lte(score(),2),2.5,1)}foo^=1 bar^=2 qux^=3 asd^=4", + "{!boost b=if(lte(score,2),2.5,1)}foo^=1 bar^=2 qux^=3 asd^=4", "df", "text", "fl", @@ -113,7 +113,7 @@ public void testScoreFunction_postFilter() throws Exception { "fl", "id,score", "fq", - "{!frange l=2 u=3 cache=false}score()"), + "{!frange l=2 u=3 cache=false}score"), "/response/numFound==2", "/response/docs/[0]/id=='3'", "/response/docs/[0]/score==3.0", @@ -133,7 +133,7 @@ public void testScoreFunction_postFilter() throws Exception { "fl", "id,score", "fq", - "{!frange l=2 u=3}score()"))); + "{!frange l=2 u=3}score"))); } @Test @@ -142,7 +142,7 @@ public void testScoreFunction_pseudoField() throws Exception { req( "q", "foo^=1 bar^=2 qux^=3", "df", "text", - "fl", "id,score,custom:add(1,score(),score())"), + "fl", "id,score,custom:add(1,score,score)"), "/response/numFound==3", "/response/docs/[0]/id=='3'", "/response/docs/[0]/score==3.0", @@ -162,7 +162,7 @@ public void testScoreFunction_pseudoField() throws Exception { req( "q", "foo^=1 bar^=2 qux^=3", "df", "text", - "fl", "id,custom:add(1,score(),score())"))); + "fl", "id,custom:add(1,score,score)"))); } @Test @@ -174,10 +174,10 @@ public void testScoreFunction_nested() throws Exception { { "query": { "boost": { - "b": "if(lte(score(),6),1,0.1)", + "b": "if(lte(score,6),1,0.1)", "query": { "boost": { - "b": "if(gte(score(),3),2,1)", + "b": "if(gte(score,3),2,1)", "query": "foo^=1 bar^=2 qux^=3 asd^=4" } } @@ -203,10 +203,10 @@ public void testScoreFunction_nested() throws Exception { { "query": { "boost": { - "b": "if(gte(score(),10),0.1,1)", + "b": "if(gte(score,10),0.1,1)", "query": { "boost": { - "b": "if(eq(query($q1),score()),100,1)", + "b": "if(eq(query($q1),score),100,1)", "query": "foo^=1 bar^=2 qux^=3 asd^=4" } } @@ -215,7 +215,7 @@ public void testScoreFunction_nested() throws Exception { "queries": { "q1": { "boost": { - "b": "if(eq(score(),3),1,2)", + "b": "if(eq(score,3),1,2)", "query": "qux^=3 asd^=4" } } @@ -238,11 +238,11 @@ public void testScoreFunction_nested() throws Exception { public void testScoreFunction_combined() throws Exception { assertJQ( req( - "q", "{!boost b=if(gte(score(),3),2,1) v=$qq}", + "q", "{!boost b=if(gte(score,3),2,1) v=$qq}", "qq", "foo^=1 bar^=2 qux^=3 asd^=4", - "fq", "{!frange cache=false u=7}score()", + "fq", "{!frange cache=false u=7}score", "df", "text", - "fl", "id,score,score_plus_one:add(1,score())"), + "fl", "id,score,score_plus_one:add(1,score)"), "/response/numFound==3", "/response/docs/[0]/id=='3'", "/response/docs/[0]/score==6.0", @@ -261,10 +261,10 @@ public void testSortFunction_sort() throws Exception { // TODO incorporate a docValues value, and some assertions on the order assertQEx( "unsupported", - "score() function cannot access the document scores", + "score function cannot access the document scores", req( "q", "*:*", - "sort", "div(1,score()) desc"), + "sort", "div(1,score) desc"), ErrorCode.BAD_REQUEST); } } diff --git a/solr/solr-ref-guide/modules/query-guide/pages/function-queries.adoc b/solr/solr-ref-guide/modules/query-guide/pages/function-queries.adoc index 92bfb3184f3..b28ef48066f 100644 --- a/solr/solr-ref-guide/modules/query-guide/pages/function-queries.adoc +++ b/solr/solr-ref-guide/modules/query-guide/pages/function-queries.adoc @@ -454,9 +454,9 @@ The function can only be used in a few specific ways - see examples below for wh *Syntax Examples* -* `fl=*,score,score_plus_one:add(1,score())`: a xref:common-query-parameters.adoc#functions-with-fl[pseudo-field] that computes the field value based on the document score, -* `fq={!frange cache=false l=4}score()`: a xref:common-query-parameters.adoc#cache-local-parameter[post filter] that filters the hits based on their score, -* `q={!boost b=if(gte(score(),10),2,1)}solrrocks`: a boost query that determines the boost factor from the document score produced by the underlying query. +* `fl=*,score,score_plus_one:add(1,score)`: a xref:common-query-parameters.adoc#functions-with-fl[pseudo-field] that computes the field value based on the document score, +* `fq={!frange cache=false l=4}score`: a xref:common-query-parameters.adoc#cache-local-parameter[post filter] that filters the hits based on their score, +* `q={!boost b=if(gte(score,10),2,1)}solrrocks`: a boost query that determines the boost factor from the document score produced by the underlying query. === sqedist Function The Square Euclidean distance calculates the 2-norm (Euclidean distance) but does not take the square root, thus saving a fairly expensive operation. From d99c97d85a02bb29bf990dae72c079aaabe6af5e Mon Sep 17 00:00:00 2001 From: Andrey Bozhko Date: Mon, 7 Jul 2025 22:57:00 -0500 Subject: [PATCH 07/10] remove static SolrClient field --- .../function/ScoreFunctionDistribTest.java | 90 ++++++++++--------- 1 file changed, 47 insertions(+), 43 deletions(-) diff --git a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java index 6ab07e01ab4..f2bd8bb20e1 100644 --- a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java +++ b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.concurrent.TimeUnit; -import org.apache.solr.client.solrj.SolrClient; import org.apache.solr.client.solrj.request.CollectionAdminRequest; import org.apache.solr.client.solrj.request.UpdateRequest; import org.apache.solr.cloud.SolrCloudTestCase; @@ -14,18 +13,15 @@ public class ScoreFunctionDistribTest extends SolrCloudTestCase { private static final String COLLECTION = "coll1"; - private static SolrClient client; - @BeforeClass public static void configureSolr() throws Exception { configureCluster(2).addConfig("config1", configset("cloud-minimal")).configure(); - client = cluster.getSolrClient(); - prepareCollection(); } private static void prepareCollection() throws Exception { + var client = cluster.getSolrClient(); var createResponse = CollectionAdminRequest.createCollection(COLLECTION, "config1", 2, 1).process(client); assertEquals(0, createResponse.getStatus()); @@ -56,15 +52,17 @@ private static void prepareCollection() throws Exception { @Test public void testScoreFunction_boostQuery() throws Exception { var resp = - client.query( - COLLECTION, - params( - "q", - "{!boost b=if(lte(score,2),2.5,1)}foo^=1 bar^=2 qux^=3 asd^=4", - "df", - "text_s", - "fl", - "id,score")); + cluster + .getSolrClient() + .query( + COLLECTION, + params( + "q", + "{!boost b=if(lte(score,2),2.5,1)}foo^=1 bar^=2 qux^=3 asd^=4", + "df", + "text_s", + "fl", + "id,score")); assertJSONEquals( """ @@ -98,17 +96,19 @@ public void testScoreFunction_boostQuery() throws Exception { @Test public void testScoreFunction_postFilter() throws Exception { var resp = - client.query( - COLLECTION, - params( - "q", - "foo^=1 bar^=2 qux^=3 asd^=4", - "df", - "text_s", - "fl", - "id,score", - "fq", - "{!frange l=2 u=3 cache=false}score")); + cluster + .getSolrClient() + .query( + COLLECTION, + params( + "q", + "foo^=1 bar^=2 qux^=3 asd^=4", + "df", + "text_s", + "fl", + "id,score", + "fq", + "{!frange l=2 u=3 cache=false}score")); assertJSONEquals( """ @@ -134,15 +134,17 @@ public void testScoreFunction_postFilter() throws Exception { @Test public void testScoreFunction_pseudoField() throws Exception { var resp = - client.query( - COLLECTION, - params( - "q", - "foo^=1 bar^=2 qux^=3", - "df", - "text_s", - "fl", - "id,score,custom:add(1,score,score)")); + cluster + .getSolrClient() + .query( + COLLECTION, + params( + "q", + "foo^=1 bar^=2 qux^=3", + "df", + "text_s", + "fl", + "id,score,custom:add(1,score,score)")); assertJSONEquals( """ @@ -175,14 +177,16 @@ public void testScoreFunction_pseudoField() throws Exception { assertThrows( SolrException.class, () -> - client.query( - COLLECTION, - params( - "q", - "foo^=1 bar^=2 qux^=3", - "df", - "text_s", - "fl", - "id,custom:add(1,score,score)"))); + cluster + .getSolrClient() + .query( + COLLECTION, + params( + "q", + "foo^=1 bar^=2 qux^=3", + "df", + "text_s", + "fl", + "id,custom:add(1,score,score)"))); } } From db435dcddecdcedbfe3772e520d31425642cbd59 Mon Sep 17 00:00:00 2001 From: Andrey Bozhko Date: Mon, 7 Jul 2025 23:44:27 -0500 Subject: [PATCH 08/10] add test --- .../search/function/ScoreFunctionTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java index 4dc503fd352..2cc4c447b3f 100644 --- a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java +++ b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java @@ -16,12 +16,18 @@ */ package org.apache.solr.search.function; +import static org.hamcrest.Matchers.isA; + import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.solr.SolrTestCaseJ4; import org.apache.solr.common.SolrException; import org.apache.solr.common.SolrException.ErrorCode; +import org.apache.solr.response.transform.DocTransformers; +import org.apache.solr.response.transform.RenameFieldTransformer; +import org.apache.solr.response.transform.ScoreAugmenter; +import org.apache.solr.response.transform.ValueSourceAugmenter; import org.junit.BeforeClass; import org.junit.Test; @@ -255,6 +261,35 @@ public void testScoreFunction_combined() throws Exception { "/response/docs/[2]/score_plus_one==2.0"); } + @Test + public void testScoreFunction_renameFieldAndValueSource() throws Exception { + // The pseudo-field definition 'renamed_score:score' can be + // parsed as either the rename field or the value source. + // Solr attempts to parse rename fields first, + // so 'renamed_score' is treated as a rename field. + var request = + req( + "q", "asd^=4", + "df", "text", + "fl", "id,score,renamed_score:score,score_plus_one:add(1,score)"); + + try (request) { + var response = h.queryAndResponse("/select", request); + var transformer = (DocTransformers) response.getReturnFields().getTransformer(); + + assertEquals(4, transformer.size()); + + // score + assertThat(transformer.getTransformer(0), isA(ScoreAugmenter.class)); + // renamed_score + assertThat(transformer.getTransformer(1), isA(ScoreAugmenter.class)); + // score_plus_one + assertThat(transformer.getTransformer(2), isA(ValueSourceAugmenter.class)); + // renamed_score + assertThat(transformer.getTransformer(3), isA(RenameFieldTransformer.class)); + } + } + // @Ignore // TODO would like this to work someday @Test public void testSortFunction_sort() throws Exception { From d85b077f1cd1fedc8658351c4ff3e125c100a3d5 Mon Sep 17 00:00:00 2001 From: Andrey Bozhko Date: Tue, 8 Jul 2025 21:54:14 -0500 Subject: [PATCH 09/10] update test --- .../core/src/test/org/apache/solr/search/QueryEqualityTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java b/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java index 6ac644bce16..6f6bd95fcbc 100644 --- a/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java +++ b/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java @@ -859,7 +859,7 @@ public void testFuncCscore() throws Exception { } public void testFuncScore() throws Exception { - assertFuncEquals("score()", "score( )"); + assertFuncEquals("score", "score "); } public void testFuncTop() throws Exception { From f461da7c495c14188654f00080e6f0ef5142305d Mon Sep 17 00:00:00 2001 From: Andrey Bozhko Date: Tue, 8 Jul 2025 23:44:21 -0500 Subject: [PATCH 10/10] pre-fetching in ValueSourceAugmenter breaks tests --- .../apache/solr/response/transform/ValueSourceAugmenter.java | 3 +-- .../apache/solr/search/function/ScoreFunctionDistribTest.java | 2 ++ .../org/apache/solr/search/function/ScoreFunctionTest.java | 2 ++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/solr/core/src/java/org/apache/solr/response/transform/ValueSourceAugmenter.java b/solr/core/src/java/org/apache/solr/response/transform/ValueSourceAugmenter.java index beb1054add4..81db54c14d9 100644 --- a/solr/core/src/java/org/apache/solr/response/transform/ValueSourceAugmenter.java +++ b/solr/core/src/java/org/apache/solr/response/transform/ValueSourceAugmenter.java @@ -125,9 +125,8 @@ public void transform(SolrDocument doc, int docid, DocIterationInfo docIteration if (context.wantsScores()) { fcontext.put("scorer", new ScoreAndDoc(localId, docIterationInfo.score())); } - - FunctionValues values = valueSource.getValues(fcontext, rcontext); + FunctionValues values = valueSource.getValues(fcontext, rcontext); setValue(doc, values.objectVal(localId)); } catch (IOException e) { throw new SolrException( diff --git a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java index f2bd8bb20e1..5be71e8b3ba 100644 --- a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java +++ b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionDistribTest.java @@ -143,6 +143,8 @@ public void testScoreFunction_pseudoField() throws Exception { "foo^=1 bar^=2 qux^=3", "df", "text_s", + "preFetchDocs", + "0", // TODO FIXME handle pre-fetching introduced in SOLR-17775 "fl", "id,score,custom:add(1,score,score)")); diff --git a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java index 2cc4c447b3f..9691b321fdf 100644 --- a/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java +++ b/solr/core/src/test/org/apache/solr/search/function/ScoreFunctionTest.java @@ -148,6 +148,7 @@ public void testScoreFunction_pseudoField() throws Exception { req( "q", "foo^=1 bar^=2 qux^=3", "df", "text", + "preFetchDocs", "0", // TODO FIXME handle pre-fetching introduced in SOLR-17775 "fl", "id,score,custom:add(1,score,score)"), "/response/numFound==3", "/response/docs/[0]/id=='3'", @@ -248,6 +249,7 @@ public void testScoreFunction_combined() throws Exception { "qq", "foo^=1 bar^=2 qux^=3 asd^=4", "fq", "{!frange cache=false u=7}score", "df", "text", + "preFetchDocs", "0", // TODO FIXME handle pre-fetching introduced in SOLR-17775 "fl", "id,score,score_plus_one:add(1,score)"), "/response/numFound==3", "/response/docs/[0]/id=='3'",