From b05a8f3939e5caa617f16236cf94ee07e32a3f42 Mon Sep 17 00:00:00 2001 From: Christine Poerschke Date: Wed, 22 Dec 2021 19:05:07 +0000 Subject: [PATCH 1/4] SOLR-15869: expand LTRRescorer.rescore test coverage --- .../solr/ltr/TestLTRReRankingPipeline.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java b/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java index e8a69422ae0..8309ab16744 100644 --- a/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java +++ b/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java @@ -192,14 +192,16 @@ public void testDifferentTopN() throws IOException { // test rerank with different topN cuts + // cap firstPassTopDocs length at topN for (int topN = 1; topN <= 5; topN++) { - log.info("rerank {} documents ", topN); + log.info("rerank {} documents, return {} documents", topN, topN); hits = searcher.search(bqBuilder.build(), 10); final ScoreDoc[] slice = new ScoreDoc[topN]; System.arraycopy(hits.scoreDocs, 0, slice, 0, topN); hits = new TopDocs(hits.totalHits, slice); hits = rescorer.rescore(searcher, hits, topN); + assertEquals(topN, hits.scoreDocs.length); for (int i = topN - 1, j = 0; i >= 0; i--, j++) { if (log.isInfoEnabled()) { log.info("doc {} in pos {}", searcher.doc(hits.scoreDocs[j].doc) @@ -211,6 +213,25 @@ public void testDifferentTopN() throws IOException { } } + + // use full firstPassTopDocs (possibly higher than topN) + for (int topN = 1; topN <= 5; topN++) { + hits = searcher.search(bqBuilder.build(), 10); + log.info("rerank {} documents, return {} documents", hits.scoreDocs.length, topN); + + TopDocs rescoredHits = rescorer.rescore(searcher, hits, topN); + assertEquals(topN, rescoredHits.scoreDocs.length); + for (int i = hits.scoreDocs.length-1, j = 0; i >= 0 && j < topN; i--, j++) { + if (log.isInfoEnabled()) { + log.info("doc {} in pos {}", searcher.doc(rescoredHits.scoreDocs[j].doc) + .get("id"), j); + } + assertEquals(i, + Integer.parseInt(searcher.doc(rescoredHits.scoreDocs[j].doc).get("id"))); + assertEquals((i + 1) * features.size()*featureWeight, rescoredHits.scoreDocs[j].score, 0.00001); + + } + } } } From 1372751261957d97fadb966f0f8ac426b1da4021 Mon Sep 17 00:00:00 2001 From: Christine Poerschke Date: Thu, 23 Dec 2021 12:16:34 +0000 Subject: [PATCH 2/4] TestLTRReRankingPipeline: one local variable rename for clarity --- .../org/apache/solr/ltr/TestLTRReRankingPipeline.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java b/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java index 8309ab16744..5f34fe84e2b 100644 --- a/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java +++ b/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java @@ -216,12 +216,12 @@ public void testDifferentTopN() throws IOException { // use full firstPassTopDocs (possibly higher than topN) for (int topN = 1; topN <= 5; topN++) { - hits = searcher.search(bqBuilder.build(), 10); - log.info("rerank {} documents, return {} documents", hits.scoreDocs.length, topN); + final TopDocs allHits = searcher.search(bqBuilder.build(), 10); + log.info("rerank {} documents, return {} documents", allHits.scoreDocs.length, topN); - TopDocs rescoredHits = rescorer.rescore(searcher, hits, topN); + TopDocs rescoredHits = rescorer.rescore(searcher, allHits, topN); assertEquals(topN, rescoredHits.scoreDocs.length); - for (int i = hits.scoreDocs.length-1, j = 0; i >= 0 && j < topN; i--, j++) { + for (int i = allHits.scoreDocs.length-1, j = 0; i >= 0 && j < topN; i--, j++) { if (log.isInfoEnabled()) { log.info("doc {} in pos {}", searcher.doc(rescoredHits.scoreDocs[j].doc) .get("id"), j); From 5f9078bca9cbfd189179eb33557f05bbf97651fc Mon Sep 17 00:00:00 2001 From: Christine Poerschke Date: Thu, 23 Dec 2021 09:59:45 +0000 Subject: [PATCH 3/4] SOLR-15NNN: factor out a ReRankRescorer class --- .../java/org/apache/solr/ltr/LTRRescorer.java | 21 ++++++- .../interleaving/LTRInterleavingRescorer.java | 17 ++++++ .../solr/ltr/TestLTRReRankingPipeline.java | 5 +- .../apache/solr/search/ReRankCollector.java | 8 ++- .../apache/solr/search/ReRankRescorer.java | 55 +++++++++++++++++++ 5 files changed, 100 insertions(+), 6 deletions(-) create mode 100644 solr/core/src/java/org/apache/solr/search/ReRankRescorer.java diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRRescorer.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRRescorer.java index 4909a6f8a96..5e4054722b8 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRRescorer.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRRescorer.java @@ -25,13 +25,13 @@ import org.apache.lucene.index.ReaderUtil; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; -import org.apache.lucene.search.Rescorer; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.ScoreMode; import org.apache.lucene.search.TopDocs; import org.apache.lucene.search.TotalHits; import org.apache.lucene.search.Weight; import org.apache.solr.ltr.interleaving.OriginalRankingLTRScoringQuery; +import org.apache.solr.search.ReRankRescorer; import org.apache.solr.search.SolrIndexSearcher; @@ -41,7 +41,7 @@ * new score to each document. The top documents will be resorted based on the * new score. * */ -public class LTRRescorer extends Rescorer { +public class LTRRescorer extends ReRankRescorer { final private LTRScoringQuery scoringQuery; @@ -109,6 +109,19 @@ protected static void heapify(ScoreDoc[] hits, int size) { } } + /** + * rescores all the documents: + * + * @param searcher + * current IndexSearcher + * @param firstPassTopDocs + * documents to rerank; + */ + @Override + public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs) throws IOException { + return rescore(searcher, firstPassTopDocs, firstPassTopDocs.scoreDocs.length); + } + /** * rescores the documents: * @@ -118,7 +131,11 @@ protected static void heapify(ScoreDoc[] hits, int size) { * documents to rerank; * @param topN * documents to return; + + * @deprecated Use {@link #rescore(IndexSearcher, TopDocs)} instead. + * From Solr 9.1.0 onwards this method will be removed. */ + @Deprecated @Override public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs, int topN) throws IOException { diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/interleaving/LTRInterleavingRescorer.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/interleaving/LTRInterleavingRescorer.java index 799f4d9e36a..6f8b62d3bbe 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/interleaving/LTRInterleavingRescorer.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/interleaving/LTRInterleavingRescorer.java @@ -53,6 +53,19 @@ public LTRInterleavingRescorer( Interleaving interleavingAlgorithm, LTRInterleav } } + /** + * rescores all the documents: + * + * @param searcher + * current IndexSearcher + * @param firstPassTopDocs + * documents to rerank; + */ + @Override + public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs) throws IOException { + return rescore(searcher, firstPassTopDocs, firstPassTopDocs.scoreDocs.length); + } + /** * rescores the documents: * @@ -62,7 +75,11 @@ public LTRInterleavingRescorer( Interleaving interleavingAlgorithm, LTRInterleav * documents to rerank; * @param topN * documents to return; + + * @deprecated Use {@link #rescore(IndexSearcher, TopDocs)} instead. + * From Solr 9.1.0 onwards this method will be removed. */ + @Deprecated @Override public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs, int topN) throws IOException { diff --git a/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java b/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java index 5f34fe84e2b..b27a236cde1 100644 --- a/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java +++ b/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java @@ -132,7 +132,8 @@ public void testRescorer() throws Exception { LTRScoringQuery ltrScoringQuery = new LTRScoringQuery(ltrScoringModel); ltrScoringQuery.setRequest(solrQueryRequest); final LTRRescorer rescorer = new LTRRescorer(ltrScoringQuery); - hits = rescorer.rescore(searcher, hits, 2); + assertEquals(2, hits.scoreDocs.length); + hits = rescorer.rescore(searcher, hits); // rerank using the field finalScore assertEquals("1", searcher.doc(hits.scoreDocs[0].doc).get("id")); @@ -200,7 +201,7 @@ public void testDifferentTopN() throws IOException { final ScoreDoc[] slice = new ScoreDoc[topN]; System.arraycopy(hits.scoreDocs, 0, slice, 0, topN); hits = new TopDocs(hits.totalHits, slice); - hits = rescorer.rescore(searcher, hits, topN); + hits = rescorer.rescore(searcher, hits); assertEquals(topN, hits.scoreDocs.length); for (int i = topN - 1, j = 0; i >= 0; i--, j++) { if (log.isInfoEnabled()) { diff --git a/solr/core/src/java/org/apache/solr/search/ReRankCollector.java b/solr/core/src/java/org/apache/solr/search/ReRankCollector.java index 11108e74e50..42f05917da1 100644 --- a/solr/core/src/java/org/apache/solr/search/ReRankCollector.java +++ b/solr/core/src/java/org/apache/solr/search/ReRankCollector.java @@ -112,8 +112,12 @@ public TopDocs topDocs(int start, int howMany) { mainDocs.scoreDocs = reRankScoreDocs; - TopDocs rescoredDocs = reRankQueryRescorer - .rescore(searcher, mainDocs, mainDocs.scoreDocs.length); + final TopDocs rescoredDocs; + if (reRankQueryRescorer instanceof ReRankRescorer) { + rescoredDocs = ((ReRankRescorer) reRankQueryRescorer).rescore(searcher, mainDocs); + } else { + rescoredDocs = reRankQueryRescorer.rescore(searcher, mainDocs, mainDocs.scoreDocs.length); + } //Lower howMany to return if we've collected fewer documents. howMany = Math.min(howMany, mainScoreDocs.length); diff --git a/solr/core/src/java/org/apache/solr/search/ReRankRescorer.java b/solr/core/src/java/org/apache/solr/search/ReRankRescorer.java new file mode 100644 index 00000000000..46876b68df0 --- /dev/null +++ b/solr/core/src/java/org/apache/solr/search/ReRankRescorer.java @@ -0,0 +1,55 @@ +/* + * 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; + +import java.io.IOException; + +import org.apache.lucene.search.IndexSearcher; +import org.apache.lucene.search.Rescorer; +import org.apache.lucene.search.TopDocs; + +/** + * This class is a variant of Lucene's {@link Rescorer} class for use by the + * {@link ReRankCollector} to rescore all results in a {@link TopDocs} object + * from an original query. + */ +public abstract class ReRankRescorer extends Rescorer { + + /** + * Rescore an initial first-pass {@link TopDocs}. + * + * @param searcher {@link IndexSearcher} used to produce the first pass topDocs + * @param firstPassTopDocs Hits from the first pass search that are to be rescored. + * It's very important that these hits were produced by the provided searcher; + * otherwise the doc IDs will not match! + */ + public abstract TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs) + throws IOException; + + /** + * Throws an {@link UnsupportedOperationException} exception. + * @deprecated Use {@link #rescore(IndexSearcher, TopDocs)} instead. + * From Solr 9.1.0 onwards this method will be final. + */ + @Deprecated + public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs, int topN) + throws IOException + { + throw new UnsupportedOperationException(); + } + +} From 0f13f6234477812a174ee58f06a53ec126661846 Mon Sep 17 00:00:00 2001 From: Christine Poerschke Date: Thu, 23 Dec 2021 12:46:05 +0000 Subject: [PATCH 4/4] SOLR-15873: simplify LTR[Interleaving]Rescorer code --- .../java/org/apache/solr/ltr/LTRRescorer.java | 67 ++++++++++--------- .../interleaving/LTRInterleavingRescorer.java | 40 +++-------- .../solr/ltr/TestLTRReRankingPipeline.java | 29 +++----- .../apache/solr/search/ReRankRescorer.java | 6 +- 4 files changed, 55 insertions(+), 87 deletions(-) diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRRescorer.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRRescorer.java index 5e4054722b8..0f1fead8ac3 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRRescorer.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/LTRRescorer.java @@ -119,44 +119,23 @@ protected static void heapify(ScoreDoc[] hits, int size) { */ @Override public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs) throws IOException { - return rescore(searcher, firstPassTopDocs, firstPassTopDocs.scoreDocs.length); - } - - /** - * rescores the documents: - * - * @param searcher - * current IndexSearcher - * @param firstPassTopDocs - * documents to rerank; - * @param topN - * documents to return; - - * @deprecated Use {@link #rescore(IndexSearcher, TopDocs)} instead. - * From Solr 9.1.0 onwards this method will be removed. - */ - @Deprecated - @Override - public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs, - int topN) throws IOException { - if ((topN == 0) || (firstPassTopDocs.scoreDocs.length == 0)) { + if (firstPassTopDocs.scoreDocs.length == 0) { return firstPassTopDocs; } final ScoreDoc[] firstPassResults = getFirstPassDocsRanked(firstPassTopDocs); - topN = Math.toIntExact(Math.min(topN, firstPassTopDocs.totalHits.value)); - final ScoreDoc[] reranked = rerank(searcher, topN, firstPassResults); + final ScoreDoc[] reranked = rerank(searcher, firstPassResults); return new TopDocs(firstPassTopDocs.totalHits, reranked); } - private ScoreDoc[] rerank(IndexSearcher searcher, int topN, ScoreDoc[] firstPassResults) throws IOException { - final ScoreDoc[] reranked = new ScoreDoc[topN]; + private ScoreDoc[] rerank(IndexSearcher searcher, ScoreDoc[] firstPassResults) throws IOException { + final ScoreDoc[] reranked = new ScoreDoc[firstPassResults.length]; final List leaves = searcher.getIndexReader().leaves(); final LTRScoringQuery.ModelWeight modelWeight = (LTRScoringQuery.ModelWeight) searcher .createWeight(searcher.rewrite(scoringQuery), ScoreMode.COMPLETE, 1); - scoreFeatures(searcher,topN, modelWeight, firstPassResults, leaves, reranked); + scoreFeatures(searcher, modelWeight, firstPassResults, leaves, reranked); // Must sort all documents that we reranked, and then select the top Arrays.sort(reranked, scoreComparator); return reranked; @@ -170,8 +149,8 @@ protected static ScoreDoc[] getFirstPassDocsRanked(TopDocs firstPassTopDocs) { return hits; } - public void scoreFeatures(IndexSearcher indexSearcher, - int topN, LTRScoringQuery.ModelWeight modelWeight, ScoreDoc[] hits, List leaves, + private void scoreFeatures(IndexSearcher indexSearcher, + LTRScoringQuery.ModelWeight modelWeight, ScoreDoc[] hits, List leaves, ScoreDoc[] reranked) throws IOException { int readerUpto = -1; @@ -195,16 +174,14 @@ public void scoreFeatures(IndexSearcher indexSearcher, docBase = readerContext.docBase; scorer = modelWeight.scorer(readerContext); } - if (scoreSingleHit(topN, docBase, hitUpto, hit, docID, scorer, reranked)) { - logSingleHit(indexSearcher, modelWeight, hit.doc, scoringQuery); - } + reranked[hitUpto] = scoreSingleHit(docBase, hit, docID, scorer); + logSingleHit(indexSearcher, modelWeight, hit.doc, scoringQuery); hitUpto++; } } /** - * Call this method if the {@link #scoreSingleHit(int, int, int, ScoreDoc, int, org.apache.solr.ltr.LTRScoringQuery.ModelWeight.ModelScorer, ScoreDoc[])} - * method indicated that the document's feature info should be logged. + * Logs a document's feature info. */ protected static void logSingleHit(IndexSearcher indexSearcher, LTRScoringQuery.ModelWeight modelWeight, int docid, LTRScoringQuery scoringQuery) { final FeatureLogger featureLogger = scoringQuery.getFeatureLogger(); @@ -213,11 +190,35 @@ protected static void logSingleHit(IndexSearcher indexSearcher, LTRScoringQuery. } } + /** + * Scores a single document and returns it. + */ + protected static ScoreDoc scoreSingleHit(int docBase, ScoreDoc hit, int docID, LTRScoringQuery.ModelWeight.ModelScorer scorer) throws IOException { + // Scorer for a LTRScoringQuery.ModelWeight should never be null since we always have to + // call score + // even if no feature scorers match, since a model might use that info to + // return a + // non-zero score. Same applies for the case of advancing a LTRScoringQuery.ModelWeight.ModelScorer + // past the target + // doc since the model algorithm still needs to compute a potentially + // non-zero score from blank features. + assert (scorer != null); + final int targetDoc = docID - docBase; + scorer.docID(); + scorer.iterator().advance(targetDoc); + + scorer.getDocInfo().setOriginalDocScore(hit.score); + hit.score = scorer.score(); + return hit; + } + /** * Scores a single document and returns true if the document's feature info should be logged via the * {@link #logSingleHit(IndexSearcher, org.apache.solr.ltr.LTRScoringQuery.ModelWeight, int, LTRScoringQuery)} * method. Feature info logging is only necessary for the topN documents. + * @deprecated From Solr 9.2.0 onwards this method will be removed. */ + @Deprecated protected static boolean scoreSingleHit(int topN, int docBase, int hitUpto, ScoreDoc hit, int docID, LTRScoringQuery.ModelWeight.ModelScorer scorer, ScoreDoc[] reranked) throws IOException { // Scorer for a LTRScoringQuery.ModelWeight should never be null since we always have to // call score diff --git a/solr/contrib/ltr/src/java/org/apache/solr/ltr/interleaving/LTRInterleavingRescorer.java b/solr/contrib/ltr/src/java/org/apache/solr/ltr/interleaving/LTRInterleavingRescorer.java index 6f8b62d3bbe..17df60270fb 100644 --- a/solr/contrib/ltr/src/java/org/apache/solr/ltr/interleaving/LTRInterleavingRescorer.java +++ b/solr/contrib/ltr/src/java/org/apache/solr/ltr/interleaving/LTRInterleavingRescorer.java @@ -63,27 +63,7 @@ public LTRInterleavingRescorer( Interleaving interleavingAlgorithm, LTRInterleav */ @Override public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs) throws IOException { - return rescore(searcher, firstPassTopDocs, firstPassTopDocs.scoreDocs.length); - } - - /** - * rescores the documents: - * - * @param searcher - * current IndexSearcher - * @param firstPassTopDocs - * documents to rerank; - * @param topN - * documents to return; - - * @deprecated Use {@link #rescore(IndexSearcher, TopDocs)} instead. - * From Solr 9.1.0 onwards this method will be removed. - */ - @Deprecated - @Override - public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs, - int topN) throws IOException { - if ((topN == 0) || (firstPassTopDocs.scoreDocs.length == 0)) { + if (firstPassTopDocs.scoreDocs.length == 0) { return firstPassTopDocs; } @@ -92,9 +72,8 @@ public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs, firstPassResults = new ScoreDoc[firstPassTopDocs.scoreDocs.length]; System.arraycopy(firstPassTopDocs.scoreDocs, 0, firstPassResults, 0, firstPassTopDocs.scoreDocs.length); } - topN = Math.toIntExact(Math.min(topN, firstPassTopDocs.totalHits.value)); - ScoreDoc[][] reRankedPerModel = rerank(searcher,topN,getFirstPassDocsRanked(firstPassTopDocs)); + ScoreDoc[][] reRankedPerModel = rerank(searcher,getFirstPassDocsRanked(firstPassTopDocs)); if (originalRankingIndex != null) { reRankedPerModel[originalRankingIndex] = firstPassResults; } @@ -108,8 +87,8 @@ public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs, return new TopDocs(firstPassTopDocs.totalHits, interleavedResults); } - private ScoreDoc[][] rerank(IndexSearcher searcher, int topN, ScoreDoc[] firstPassResults) throws IOException { - ScoreDoc[][] reRankedPerModel = new ScoreDoc[rerankingQueries.length][topN]; + private ScoreDoc[][] rerank(IndexSearcher searcher, ScoreDoc[] firstPassResults) throws IOException { + ScoreDoc[][] reRankedPerModel = new ScoreDoc[rerankingQueries.length][firstPassResults.length]; final List leaves = searcher.getIndexReader().leaves(); LTRScoringQuery.ModelWeight[] modelWeights = new LTRScoringQuery.ModelWeight[rerankingQueries.length]; for (int i = 0; i < rerankingQueries.length; i++) { @@ -118,7 +97,7 @@ private ScoreDoc[][] rerank(IndexSearcher searcher, int topN, ScoreDoc[] firstPa .createWeight(searcher.rewrite(rerankingQueries[i]), ScoreMode.COMPLETE, 1); } } - scoreFeatures(searcher, topN, modelWeights, firstPassResults, leaves, reRankedPerModel); + scoreFeatures(searcher, modelWeights, firstPassResults, leaves, reRankedPerModel); for (int i = 0; i < rerankingQueries.length; i++) { if (originalRankingIndex == null || originalRankingIndex != i) { @@ -129,8 +108,8 @@ private ScoreDoc[][] rerank(IndexSearcher searcher, int topN, ScoreDoc[] firstPa return reRankedPerModel; } - public void scoreFeatures(IndexSearcher indexSearcher, - int topN, LTRScoringQuery.ModelWeight[] modelWeights, ScoreDoc[] hits, List leaves, + private void scoreFeatures(IndexSearcher indexSearcher, + LTRScoringQuery.ModelWeight[] modelWeights, ScoreDoc[] hits, List leaves, ScoreDoc[][] rerankedPerModel) throws IOException { int readerUpto = -1; @@ -160,9 +139,8 @@ public void scoreFeatures(IndexSearcher indexSearcher, for (int i = 0; i < rerankingQueries.length; i++) { if (modelWeights[i] != null) { final ScoreDoc hit_i = new ScoreDoc(hit.doc, hit.score, hit.shardIndex); - if (scoreSingleHit(topN, docBase, hitUpto, hit_i, docID, scorers[i], rerankedPerModel[i])) { - logSingleHit(indexSearcher, modelWeights[i], hit_i.doc, rerankingQueries[i]); - } + rerankedPerModel[i][hitUpto] = scoreSingleHit(docBase, hit_i, docID, scorers[i]); + logSingleHit(indexSearcher, modelWeights[i], hit_i.doc, rerankingQueries[i]); } } hitUpto++; diff --git a/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java b/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java index b27a236cde1..c1ee807aeab 100644 --- a/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java +++ b/solr/contrib/ltr/src/test/org/apache/solr/ltr/TestLTRReRankingPipeline.java @@ -184,12 +184,11 @@ public void testDifferentTopN() throws IOException { final LTRRescorer rescorer = new LTRRescorer(scoringQuery); // rerank @ 0 should not change the order - hits = rescorer.rescore(searcher, hits, 0); - assertEquals("0", searcher.doc(hits.scoreDocs[0].doc).get("id")); - assertEquals("1", searcher.doc(hits.scoreDocs[1].doc).get("id")); - assertEquals("2", searcher.doc(hits.scoreDocs[2].doc).get("id")); - assertEquals("3", searcher.doc(hits.scoreDocs[3].doc).get("id")); - assertEquals("4", searcher.doc(hits.scoreDocs[4].doc).get("id")); + { + final TopDocs noHits = new TopDocs(hits.totalHits, new ScoreDoc[0]); + final TopDocs noHitsRescored = rescorer.rescore(searcher, noHits); + assertEquals(0, noHitsRescored.scoreDocs.length); + } // test rerank with different topN cuts @@ -216,22 +215,14 @@ public void testDifferentTopN() throws IOException { } // use full firstPassTopDocs (possibly higher than topN) - for (int topN = 1; topN <= 5; topN++) { + for (int topN = 0; topN <= 5; topN++) { final TopDocs allHits = searcher.search(bqBuilder.build(), 10); log.info("rerank {} documents, return {} documents", allHits.scoreDocs.length, topN); - TopDocs rescoredHits = rescorer.rescore(searcher, allHits, topN); - assertEquals(topN, rescoredHits.scoreDocs.length); - for (int i = allHits.scoreDocs.length-1, j = 0; i >= 0 && j < topN; i--, j++) { - if (log.isInfoEnabled()) { - log.info("doc {} in pos {}", searcher.doc(rescoredHits.scoreDocs[j].doc) - .get("id"), j); - } - assertEquals(i, - Integer.parseInt(searcher.doc(rescoredHits.scoreDocs[j].doc).get("id"))); - assertEquals((i + 1) * features.size()*featureWeight, rescoredHits.scoreDocs[j].score, 0.00001); - - } + final int topNN = topN; + expectThrows(UnsupportedOperationException.class, () -> { + rescorer.rescore(searcher, allHits, topNN); + }); } } } diff --git a/solr/core/src/java/org/apache/solr/search/ReRankRescorer.java b/solr/core/src/java/org/apache/solr/search/ReRankRescorer.java index 46876b68df0..05cf4418dc8 100644 --- a/solr/core/src/java/org/apache/solr/search/ReRankRescorer.java +++ b/solr/core/src/java/org/apache/solr/search/ReRankRescorer.java @@ -42,11 +42,9 @@ public abstract TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs /** * Throws an {@link UnsupportedOperationException} exception. - * @deprecated Use {@link #rescore(IndexSearcher, TopDocs)} instead. - * From Solr 9.1.0 onwards this method will be final. + * Use {@link #rescore(IndexSearcher, TopDocs)} instead. */ - @Deprecated - public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs, int topN) + final public TopDocs rescore(IndexSearcher searcher, TopDocs firstPassTopDocs, int topN) throws IOException { throw new UnsupportedOperationException();