SOLR-17296: Remove (broken) singlePass attempt when reRankScale + debug is used, and fix underlying NPE.
(cherry picked from commit 6af52f33ac78155523ec6fc610c563127b81c139)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 00a2322..cb3e966 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -16,6 +16,8 @@
* SOLR-17275: SolrJ ZkClientClusterStateProvider, revert SOLR-17153 for perf regression when aliases are used. (Aparna Suresh)
+* SOLR-17296: Remove (broken) singlePass attempt when reRankScale + debug is used, and fix underlying NPE. (hossman)
+
Dependency Upgrades
---------------------
(No changes)
diff --git a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
index 4f17cb7..bb364f8 100644
--- a/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
+++ b/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
@@ -89,7 +89,6 @@
import org.apache.solr.search.QueryResult;
import org.apache.solr.search.QueryUtils;
import org.apache.solr.search.RankQuery;
-import org.apache.solr.search.ReRankQParserPlugin;
import org.apache.solr.search.ReturnFields;
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.search.SolrReturnFields;
@@ -779,7 +778,6 @@
boolean distribSinglePass = rb.req.getParams().getBool(ShardParams.DISTRIB_SINGLE_PASS, false);
if (distribSinglePass
- || singlePassExplain(rb.req.getParams())
|| (fields != null
&& fields.wantsField(keyFieldName)
&& fields.getRequestedFieldNames() != null
@@ -861,36 +859,6 @@
rb.addRequest(this, sreq);
}
- private boolean singlePassExplain(SolrParams params) {
-
- /*
- * Currently there is only one explain that requires a single pass
- * and that is the reRank when scaling is used.
- */
-
- String rankQuery = params.get(CommonParams.RQ);
- if (rankQuery != null) {
- if (rankQuery.contains(ReRankQParserPlugin.RERANK_MAIN_SCALE)
- || rankQuery.contains(ReRankQParserPlugin.RERANK_SCALE)) {
- boolean debugQuery = params.getBool(CommonParams.DEBUG_QUERY, false);
- if (debugQuery) {
- return true;
- } else {
- String[] debugParams = params.getParams(CommonParams.DEBUG);
- if (debugParams != null) {
- for (String debugParam : debugParams) {
- if (debugParam.equals("true")) {
- return true;
- }
- }
- }
- }
- }
- }
-
- return false;
- }
-
protected boolean addFL(StringBuilder fl, String field, boolean additionalAdded) {
if (additionalAdded) fl.append(",");
fl.append(field);
diff --git a/solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java
index e9597c3..5bf5ce4 100644
--- a/solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java
+++ b/solr/core/src/java/org/apache/solr/search/ReRankQParserPlugin.java
@@ -25,7 +25,9 @@
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.StrUtils;
+import org.apache.solr.handler.component.ResponseBuilder;
import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.request.SolrRequestInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -63,6 +65,40 @@
private static class ReRankQParser extends QParser {
+ private boolean isExplainResults() {
+ final SolrRequestInfo ri = SolrRequestInfo.getRequestInfo();
+ if (null != ri) {
+ final ResponseBuilder rb = ri.getResponseBuilder();
+ if (null != rb) {
+ return rb.isDebugResults();
+ }
+ }
+
+ // HACK: The code below should not be used. It is preserved for backcompat
+ // on the slim remote chance that someone is using ReRankQParserPlugin
+ // w/o using SearchHandler+ResponseBuilder
+ //
+ // (It's also wrong, and doesn't account for thigns like debug=true
+ // or debug=all ... but as stated: it's for esoteric backcompat purposes
+ // only, so we're not going to change it and start returning "true"
+ // if existing code doesn't expect it
+
+ boolean debugQuery = params.getBool(CommonParams.DEBUG_QUERY, false);
+
+ if (!debugQuery) {
+ String[] debugParams = params.getParams(CommonParams.DEBUG);
+ if (debugParams != null) {
+ for (String debugParam : debugParams) {
+ if ("true".equals(debugParam)) {
+ debugQuery = true;
+ break;
+ }
+ }
+ }
+ }
+ return debugQuery;
+ }
+
public ReRankQParser(
String query, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
super(query, localParams, params, req);
@@ -88,19 +124,8 @@
String mainScale = localParams.get(RERANK_MAIN_SCALE);
String reRankScale = localParams.get(RERANK_SCALE);
- boolean debugQuery = params.getBool(CommonParams.DEBUG_QUERY, false);
- if (!debugQuery) {
- String[] debugParams = params.getParams(CommonParams.DEBUG);
- if (debugParams != null) {
- for (String debugParam : debugParams) {
- if ("true".equals(debugParam)) {
- debugQuery = true;
- break;
- }
- }
- }
- }
+ final boolean explainResults = isExplainResults();
double reRankScaleWeight = reRankWeight;
@@ -111,7 +136,7 @@
reRankScaleWeight,
reRankOperator,
new ReRankQueryRescorer(reRankQuery, 1, ReRankOperator.REPLACE),
- debugQuery);
+ explainResults);
if (reRankScaler.scaleScores()) {
// Scaler applies the weighting instead of the rescorer
@@ -119,7 +144,7 @@
}
return new ReRankQuery(
- reRankQuery, reRankDocs, reRankWeight, reRankOperator, reRankScaler, debugQuery);
+ reRankQuery, reRankDocs, reRankWeight, reRankOperator, reRankScaler, explainResults);
}
}
@@ -165,7 +190,7 @@
private static final class ReRankQuery extends AbstractReRankQuery {
private final Query reRankQuery;
private final double reRankWeight;
- private final boolean debugQuery;
+ private final boolean explainResults;
@Override
public int hashCode() {
@@ -198,7 +223,7 @@
double reRankWeight,
ReRankOperator reRankOperator,
ReRankScaler reRankScaler,
- boolean debugQuery) {
+ boolean explainResults) {
super(
defaultQuery,
reRankDocs,
@@ -207,7 +232,7 @@
reRankOperator);
this.reRankQuery = reRankQuery;
this.reRankWeight = reRankWeight;
- this.debugQuery = debugQuery;
+ this.explainResults = explainResults;
}
@Override
@@ -246,13 +271,13 @@
@Override
protected Query rewrite(Query rewrittenMainQuery) throws IOException {
return new ReRankQuery(
- reRankQuery, reRankDocs, reRankWeight, reRankOperator, reRankScaler, debugQuery)
+ reRankQuery, reRankDocs, reRankWeight, reRankOperator, reRankScaler, explainResults)
.wrap(rewrittenMainQuery);
}
@Override
public boolean getCache() {
- if (reRankScaler.scaleScores() && debugQuery) {
+ if (reRankScaler.scaleScores() && explainResults) {
// Caching breaks explain when reRankScaling is used.
return false;
} else {
diff --git a/solr/core/src/java/org/apache/solr/search/ReRankScaler.java b/solr/core/src/java/org/apache/solr/search/ReRankScaler.java
index 686faa4..8410467 100644
--- a/solr/core/src/java/org/apache/solr/search/ReRankScaler.java
+++ b/solr/core/src/java/org/apache/solr/search/ReRankScaler.java
@@ -32,7 +32,7 @@
protected int mainQueryMax = -1;
protected int reRankQueryMin = -1;
protected int reRankQueryMax = -1;
- protected boolean debugQuery;
+ protected boolean explainResults;
protected ReRankOperator reRankOperator;
protected ReRankScalerExplain reRankScalerExplain;
private QueryRescorer replaceRescorer;
@@ -45,11 +45,11 @@
double reRankScaleWeight,
ReRankOperator reRankOperator,
QueryRescorer replaceRescorer,
- boolean debugQuery)
+ boolean explainResults)
throws SyntaxError {
this.reRankScaleWeight = reRankScaleWeight;
- this.debugQuery = debugQuery;
+ this.explainResults = explainResults;
this.reRankScalerExplain = new ReRankScalerExplain(mainScale, reRankScale);
this.replaceRescorer = replaceRescorer;
if (reRankOperator != ReRankOperator.ADD
@@ -171,12 +171,12 @@
scaledOriginalScoreMap = originalScoreMap;
}
- this.reRankSet = debugQuery ? new HashSet<>() : null;
+ this.reRankSet = explainResults ? new HashSet<>() : null;
for (int i = 0; i < howMany; i++) {
ScoreDoc rescoredDoc = rescoredDocs[i];
int doc = rescoredDoc.doc;
- if (debugQuery) {
+ if (explainResults) {
reRankSet.add(doc);
}
float score = rescoredDoc.score;
@@ -345,7 +345,14 @@
int doc, Explanation mainQueryExplain, Explanation reRankQueryExplain) {
float reRankScore = reRankQueryExplain.getDetails()[1].getValue().floatValue();
float mainScore = mainQueryExplain.getValue().floatValue();
- if (reRankSet.contains(doc)) {
+ if (null == reRankSet) {
+ // we don't have the data needed to accurately report scaling,
+ // probably due to distributed request
+ return Explanation.match(
+ reRankScore,
+ "ReRank Scaling effects unkown, consider using distrib.singlePass=true (see https://issues.apache.org/jira/browse/SOLR-17299)",
+ reRankQueryExplain);
+ } else if (reRankSet.contains(doc)) {
if (scaleMainScores() && scaleReRankScores()) {
if (reRankScore > 0) {
MinMaxExplain mainScaleExplain = reRankScalerExplain.getMainScaleExplain();
diff --git a/solr/core/src/test/org/apache/solr/search/DistributedReRankExplainTest.java b/solr/core/src/test/org/apache/solr/search/DistributedReRankExplainTest.java
index defe6fe..63343d3 100644
--- a/solr/core/src/test/org/apache/solr/search/DistributedReRankExplainTest.java
+++ b/solr/core/src/test/org/apache/solr/search/DistributedReRankExplainTest.java
@@ -17,9 +17,11 @@
package org.apache.solr.search;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.not;
+
import java.lang.invoke.MethodHandles;
import java.util.Map;
-import java.util.Random;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.request.CollectionAdminRequest;
@@ -29,7 +31,8 @@
import org.apache.solr.cloud.SolrCloudTestCase;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.CommonParams;
-import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.params.ShardParams;
+import org.apache.solr.common.params.SolrParams;
import org.junit.BeforeClass;
import org.junit.Test;
import org.slf4j.Logger;
@@ -61,10 +64,7 @@
.process(cluster.getSolrClient());
cluster.waitForActiveCollection(collection, 2, 2);
- }
- @Test
- public void testReRankExplain() throws Exception {
CloudSolrClient client = cluster.getSolrClient();
UpdateRequest updateRequest = new UpdateRequest();
for (int i = 0; i < 100; i++) {
@@ -75,35 +75,90 @@
}
updateRequest.process(client, COLLECTIONORALIAS);
client.commit(COLLECTIONORALIAS);
+ }
- String[] debugParams = {CommonParams.DEBUG, CommonParams.DEBUG_QUERY};
- Random random = random();
- ModifiableSolrParams solrParams = new ModifiableSolrParams();
- String reRank = "{!rerank reRankDocs=10 reRankMainScale=0-10 reRankQuery='test_s:hello'}";
- solrParams
- .add("q", "test_s:hello")
- .add(debugParams[random.nextInt(2)], "true")
- .add(CommonParams.RQ, reRank);
- QueryRequest queryRequest = new QueryRequest(solrParams);
- QueryResponse queryResponse = queryRequest.process(client, COLLECTIONORALIAS);
- Map<String, Object> debug = queryResponse.getDebugMap();
- assertNotNull(debug);
- String explain = debug.get("explain").toString();
- assertTrue(
- explain.contains("5.0101576 = combined scaled first and unscaled second pass score "));
+ @Test
+ public void testDebugTrue() throws Exception {
+ doTestReRankExplain(params(CommonParams.DEBUG_QUERY, "true"));
+ doTestReRankExplain(params(CommonParams.DEBUG, "true"));
+ }
- solrParams = new ModifiableSolrParams();
- reRank = "{!rerank reRankDocs=10 reRankScale=0-10 reRankQuery='test_s:hello'}";
- solrParams
- .add("q", "test_s:hello")
- .add(debugParams[random.nextInt(2)], "true")
- .add(CommonParams.RQ, reRank);
- queryRequest = new QueryRequest(solrParams);
- queryResponse = queryRequest.process(client, COLLECTIONORALIAS);
- debug = queryResponse.getDebugMap();
- assertNotNull(debug);
- explain = debug.get("explain").toString();
- assertTrue(
- explain.contains("10.005078 = combined unscaled first and scaled second pass score "));
+ @Test
+ public void testDebugAll() throws Exception {
+ doTestReRankExplain(params(CommonParams.DEBUG, "all"));
+ }
+
+ @Test
+ public void testDebugResults() throws Exception {
+ doTestReRankExplain(params(CommonParams.DEBUG, CommonParams.RESULTS));
+ }
+
+ private void doTestReRankExplain(final SolrParams debugParams) throws Exception {
+ final String reRankMainScale =
+ "{!rerank reRankDocs=10 reRankMainScale=0-10 reRankQuery='test_s:hello'}";
+ final String reRankScale =
+ "{!rerank reRankDocs=10 reRankScale=0-10 reRankQuery='test_s:hello'}";
+
+ { // multi-pass reRankMainScale
+ final QueryResponse queryResponse =
+ doQueryAndCommonChecks(
+ SolrParams.wrapDefaults(params(CommonParams.RQ, reRankMainScale), debugParams));
+ final Map<String, Object> debug = queryResponse.getDebugMap();
+ assertNotNull(debug);
+ final String explain = debug.get("explain").toString();
+ assertThat(explain, containsString("ReRank Scaling effects unkown"));
+ }
+
+ { // single-pass reRankMainScale
+ final QueryResponse queryResponse =
+ doQueryAndCommonChecks(
+ SolrParams.wrapDefaults(
+ params(CommonParams.RQ, reRankMainScale, ShardParams.DISTRIB_SINGLE_PASS, "true"),
+ debugParams));
+ final Map<String, Object> debug = queryResponse.getDebugMap();
+ assertNotNull(debug);
+ final String explain = debug.get("explain").toString();
+ assertThat(
+ explain,
+ containsString("5.0101576 = combined scaled first and unscaled second pass score "));
+ assertThat(explain, not(containsString("ReRank Scaling effects unkown")));
+ }
+
+ { // multi-pass reRankMainScale
+ final QueryResponse queryResponse =
+ doQueryAndCommonChecks(
+ SolrParams.wrapDefaults(params(CommonParams.RQ, reRankScale), debugParams));
+ final Map<String, Object> debug = queryResponse.getDebugMap();
+ assertNotNull(debug);
+ final String explain = debug.get("explain").toString();
+ assertThat(explain, containsString("ReRank Scaling effects unkown"));
+ }
+
+ { // single-pass reRankMainScale
+ final QueryResponse queryResponse =
+ doQueryAndCommonChecks(
+ SolrParams.wrapDefaults(
+ params(CommonParams.RQ, reRankScale, ShardParams.DISTRIB_SINGLE_PASS, "true"),
+ debugParams));
+ final Map<String, Object> debug = queryResponse.getDebugMap();
+ assertNotNull(debug);
+ final String explain = debug.get("explain").toString();
+ assertThat(
+ explain,
+ containsString("10.005078 = combined unscaled first and scaled second pass score "));
+ assertThat(explain, not(containsString("ReRank Scaling effects unkown")));
+ }
+ }
+
+ private QueryResponse doQueryAndCommonChecks(final SolrParams params) throws Exception {
+ final CloudSolrClient client = cluster.getSolrClient();
+ final QueryRequest queryRequest =
+ new QueryRequest(
+ SolrParams.wrapDefaults(
+ params, params(CommonParams.Q, "test_s:hello", "fl", "id,test_s,score")));
+
+ final QueryResponse queryResponse = queryRequest.process(client, COLLECTIONORALIAS);
+ assertNotNull(queryResponse.getResults().get(0).getFieldValue("test_s"));
+ return queryResponse;
}
}