mirror of https://github.com/apache/lucene.git
LUCENE-2190: Added a new class CustomScoreProvider to function package that can be subclassed to provide custom scoring to CustomScoreQuery
git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@912386 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
8b165b40b5
commit
30b45bf592
17
CHANGES.txt
17
CHANGES.txt
|
@ -20,6 +20,9 @@ Changes in backwards compatibility policy
|
|||
toString. These are advanced APIs and subject to change suddenly.
|
||||
(Tim Smith via Mike McCandless)
|
||||
|
||||
* LUCENE-2190: Removed deprecated customScore() and customExplain()
|
||||
methods from experimental CustomScoreQuery. (Uwe Schindler)
|
||||
|
||||
Changes in runtime behavior
|
||||
|
||||
* LUCENE-1923: Made IndexReader.toString() produce something
|
||||
|
@ -259,10 +262,16 @@ API Changes
|
|||
* LUCENE-1972 (3.0.1 only): Restore SortField.getComparatorSource
|
||||
(it was accidentally removed in 3.0.0) (John Wang via Uwe Schindler)
|
||||
|
||||
* LUCENE-2190: Added setNextReader method to CustomScoreQuery, which
|
||||
is necessary with per-segment searching to notify the subclass
|
||||
which reader the int doc, passed to customScore, refers to. (Paul
|
||||
chez Jamespot via Mike McCandless)
|
||||
* LUCENE-2190: Added a new class CustomScoreProvider to function package
|
||||
that can be subclassed to provide custom scoring to CustomScoreQuery.
|
||||
The methods in CustomScoreQuery that did this before were deprecated
|
||||
and replaced by a method getCustomScoreProvider(IndexReader) that
|
||||
returns a custom score implementation using the above class. The change
|
||||
is necessary with per-segment searching, as CustomScoreQuery is
|
||||
a stateless class (like all other Queries) and does not know about
|
||||
the currently searched segment. This API works similar to Filter's
|
||||
getDocIdSet(IndexReader). (Paul chez Jamespot via Mike McCandless,
|
||||
Uwe Schindler)
|
||||
|
||||
* LUCENE-2080: Deprecate Version.LUCENE_CURRENT, as using this constant
|
||||
will cause backwards compatibility problems when upgrading Lucene. See
|
||||
|
|
|
@ -43,7 +43,7 @@
|
|||
<property name="dev.version" value="3.1-dev"/>
|
||||
<property name="version" value="${dev.version}"/>
|
||||
<property name="backwards.branch" value="lucene_3_0_back_compat_tests"/>
|
||||
<property name="backwards.rev" value="912375"/>
|
||||
<property name="backwards.rev" value="912385"/>
|
||||
<property name="spec.version" value="${version}"/>
|
||||
<property name="year" value="2000-${current.year}"/>
|
||||
<property name="final.name" value="lucene-${name}-${version}"/>
|
||||
|
|
|
@ -27,6 +27,7 @@ import org.apache.lucene.analysis.WhitespaceAnalyzer;
|
|||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Field;
|
||||
import org.apache.lucene.index.IndexWriter;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.search.IndexSearcher;
|
||||
import org.apache.lucene.search.Query;
|
||||
|
@ -36,6 +37,7 @@ import org.apache.lucene.search.SortField;
|
|||
import org.apache.lucene.search.TermQuery;
|
||||
import org.apache.lucene.search.TopDocs;
|
||||
import org.apache.lucene.search.function.CustomScoreQuery;
|
||||
import org.apache.lucene.search.function.CustomScoreProvider;
|
||||
import org.apache.lucene.search.function.FieldScoreQuery;
|
||||
import org.apache.lucene.search.function.FieldScoreQuery.Type;
|
||||
import org.apache.lucene.spatial.geohash.GeoHashUtils;
|
||||
|
@ -189,20 +191,26 @@ public class TestCartesian extends TestCase{
|
|||
CustomScoreQuery customScore = new CustomScoreQuery(dq.getQuery(tq),fsQuery){
|
||||
|
||||
@Override
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||
System.out.println(doc);
|
||||
if (dq.distanceFilter.getDistance(doc) == null)
|
||||
return 0;
|
||||
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||
return new CustomScoreProvider(reader) {
|
||||
@Override // TODO: broken, as reader is not used!
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||
System.out.println(doc);
|
||||
if (dq.distanceFilter.getDistance(doc) == null)
|
||||
return 0;
|
||||
|
||||
double distance = dq.distanceFilter.getDistance(doc);
|
||||
// boost score shouldn't exceed 1
|
||||
if (distance < 1.0d)
|
||||
distance = 1.0d;
|
||||
//boost by distance is invertly proportional to
|
||||
// to distance from center point to location
|
||||
float score = (float) ((miles - distance) / miles );
|
||||
return score * subQueryScore;
|
||||
double distance = dq.distanceFilter.getDistance(doc);
|
||||
// boost score shouldn't exceed 1
|
||||
if (distance < 1.0d)
|
||||
distance = 1.0d;
|
||||
//boost by distance is invertly proportional to
|
||||
// to distance from center point to location
|
||||
float score = (float) ((miles - distance) / miles );
|
||||
return score * subQueryScore;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
// Create a distance sort
|
||||
// As the radius filter has performed the distance calculations
|
||||
|
@ -276,20 +284,26 @@ public class TestCartesian extends TestCase{
|
|||
CustomScoreQuery customScore = new CustomScoreQuery(dq.getQuery(tq),fsQuery){
|
||||
|
||||
@Override
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||
System.out.println(doc);
|
||||
if (dq.distanceFilter.getDistance(doc) == null)
|
||||
return 0;
|
||||
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||
return new CustomScoreProvider(reader) {
|
||||
@Override // TODO: broken, as reader is not used!
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||
System.out.println(doc);
|
||||
if (dq.distanceFilter.getDistance(doc) == null)
|
||||
return 0;
|
||||
|
||||
double distance = dq.distanceFilter.getDistance(doc);
|
||||
// boost score shouldn't exceed 1
|
||||
if (distance < 1.0d)
|
||||
distance = 1.0d;
|
||||
//boost by distance is invertly proportional to
|
||||
// to distance from center point to location
|
||||
float score = (float) ((miles - distance) / miles );
|
||||
return score * subQueryScore;
|
||||
double distance = dq.distanceFilter.getDistance(doc);
|
||||
// boost score shouldn't exceed 1
|
||||
if (distance < 1.0d)
|
||||
distance = 1.0d;
|
||||
//boost by distance is invertly proportional to
|
||||
// to distance from center point to location
|
||||
float score = (float) ((miles - distance) / miles );
|
||||
return score * subQueryScore;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
};
|
||||
// Create a distance sort
|
||||
// As the radius filter has performed the distance calculations
|
||||
|
@ -363,23 +377,27 @@ public class TestCartesian extends TestCase{
|
|||
FieldScoreQuery fsQuery = new FieldScoreQuery("geo_distance", Type.FLOAT);
|
||||
|
||||
CustomScoreQuery customScore = new CustomScoreQuery(dq.getQuery(tq),fsQuery){
|
||||
|
||||
@Override
|
||||
@Override
|
||||
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||
return new CustomScoreProvider(reader) {
|
||||
@Override // TODO: broken, as reader is not used!
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||
//System.out.println(doc);
|
||||
if (dq.distanceFilter.getDistance(doc) == null)
|
||||
return 0;
|
||||
|
||||
double distance = dq.distanceFilter.getDistance(doc);
|
||||
// boost score shouldn't exceed 1
|
||||
if (distance < 1.0d)
|
||||
distance = 1.0d;
|
||||
//boost by distance is invertly proportional to
|
||||
// to distance from center point to location
|
||||
float score = (float) ( (miles - distance) / miles );
|
||||
return score * subQueryScore;
|
||||
}
|
||||
};
|
||||
//System.out.println(doc);
|
||||
if (dq.distanceFilter.getDistance(doc) == null)
|
||||
return 0;
|
||||
|
||||
double distance = dq.distanceFilter.getDistance(doc);
|
||||
// boost score shouldn't exceed 1
|
||||
if (distance < 1.0d)
|
||||
distance = 1.0d;
|
||||
//boost by distance is invertly proportional to
|
||||
// to distance from center point to location
|
||||
float score = (float) ( (miles - distance) / miles );
|
||||
return score * subQueryScore;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
// Create a distance sort
|
||||
// As the radius filter has performed the distance calculations
|
||||
// already, pass in the filter to reuse the results.
|
||||
|
@ -452,23 +470,27 @@ public class TestCartesian extends TestCase{
|
|||
|
||||
FieldScoreQuery fsQuery = new FieldScoreQuery("geo_distance", Type.FLOAT);
|
||||
CustomScoreQuery customScore = new CustomScoreQuery(tq,fsQuery){
|
||||
|
||||
@Override
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||
//System.out.println(doc);
|
||||
if (dq.distanceFilter.getDistance(doc) == null)
|
||||
return 0;
|
||||
|
||||
double distance = dq.distanceFilter.getDistance(doc);
|
||||
// boost score shouldn't exceed 1
|
||||
if (distance < 1.0d)
|
||||
distance = 1.0d;
|
||||
//boost by distance is invertly proportional to
|
||||
// to distance from center point to location
|
||||
float score = (float) ( (miles - distance) / miles );
|
||||
return score * subQueryScore;
|
||||
}
|
||||
};
|
||||
@Override
|
||||
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||
return new CustomScoreProvider(reader) {
|
||||
@Override // TODO: broken, as reader is not used!
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||
//System.out.println(doc);
|
||||
if (dq.distanceFilter.getDistance(doc) == null)
|
||||
return 0;
|
||||
|
||||
double distance = dq.distanceFilter.getDistance(doc);
|
||||
// boost score shouldn't exceed 1
|
||||
if (distance < 1.0d)
|
||||
distance = 1.0d;
|
||||
//boost by distance is invertly proportional to
|
||||
// to distance from center point to location
|
||||
float score = (float) ( (miles - distance) / miles );
|
||||
return score * subQueryScore;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
// Create a distance sort
|
||||
// As the radius filter has performed the distance calculations
|
||||
// already, pass in the filter to reuse the results.
|
||||
|
|
|
@ -0,0 +1,163 @@
|
|||
package org.apache.lucene.search.function;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
import org.apache.lucene.search.ComplexExplanation;
|
||||
import org.apache.lucene.search.Explanation;
|
||||
import org.apache.lucene.search.FieldCache; // for javadocs
|
||||
|
||||
/**
|
||||
* An instance of this subclass should be returned by
|
||||
* {@link CustomScoreQuery#getCustomScoreProvider}, if you want
|
||||
* to modify the custom score calculation of a {@link CustomScoreQuery}.
|
||||
* <p>Since Lucene 2.9, queries operate on each segment of an index separately,
|
||||
* so the protected {@link #reader} field can be used to resolve doc IDs,
|
||||
* as the supplied <code>doc</code> ID is per-segment and without knowledge
|
||||
* of the IndexReader you cannot access the document or {@link FieldCache}.
|
||||
*
|
||||
* @lucene.experimental
|
||||
* @since 2.9.2
|
||||
*/
|
||||
public class CustomScoreProvider {
|
||||
|
||||
protected final IndexReader reader;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the provider class for the given {@link IndexReader}.
|
||||
*/
|
||||
public CustomScoreProvider(IndexReader reader) {
|
||||
this.reader = reader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a custom score by the subQuery score and a number of
|
||||
* {@link ValueSourceQuery} scores.
|
||||
* <p>
|
||||
* Subclasses can override this method to modify the custom score.
|
||||
* <p>
|
||||
* If your custom scoring is different than the default herein you
|
||||
* should override at least one of the two customScore() methods.
|
||||
* If the number of ValueSourceQueries is always < 2 it is
|
||||
* sufficient to override the other
|
||||
* {@link #customScore(int, float, float) customScore()}
|
||||
* method, which is simpler.
|
||||
* <p>
|
||||
* The default computation herein is a multiplication of given scores:
|
||||
* <pre>
|
||||
* ModifiedScore = valSrcScore * valSrcScores[0] * valSrcScores[1] * ...
|
||||
* </pre>
|
||||
*
|
||||
* @param doc id of scored doc.
|
||||
* @param subQueryScore score of that doc by the subQuery.
|
||||
* @param valSrcScores scores of that doc by the ValueSourceQuery.
|
||||
* @return custom score.
|
||||
*/
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScores[]) throws IOException {
|
||||
if (valSrcScores.length == 1) {
|
||||
return customScore(doc, subQueryScore, valSrcScores[0]);
|
||||
}
|
||||
if (valSrcScores.length == 0) {
|
||||
return customScore(doc, subQueryScore, 1);
|
||||
}
|
||||
float score = subQueryScore;
|
||||
for(int i = 0; i < valSrcScores.length; i++) {
|
||||
score *= valSrcScores[i];
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a custom score by the subQuery score and the ValueSourceQuery score.
|
||||
* <p>
|
||||
* Subclasses can override this method to modify the custom score.
|
||||
* <p>
|
||||
* If your custom scoring is different than the default herein you
|
||||
* should override at least one of the two customScore() methods.
|
||||
* If the number of ValueSourceQueries is always < 2 it is
|
||||
* sufficient to override this customScore() method, which is simpler.
|
||||
* <p>
|
||||
* The default computation herein is a multiplication of the two scores:
|
||||
* <pre>
|
||||
* ModifiedScore = subQueryScore * valSrcScore
|
||||
* </pre>
|
||||
*
|
||||
* @param doc id of scored doc.
|
||||
* @param subQueryScore score of that doc by the subQuery.
|
||||
* @param valSrcScore score of that doc by the ValueSourceQuery.
|
||||
* @return custom score.
|
||||
*/
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore) throws IOException {
|
||||
return subQueryScore * valSrcScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explain the custom score.
|
||||
* Whenever overriding {@link #customScore(int, float, float[])},
|
||||
* this method should also be overridden to provide the correct explanation
|
||||
* for the part of the custom scoring.
|
||||
*
|
||||
* @param doc doc being explained.
|
||||
* @param subQueryExpl explanation for the sub-query part.
|
||||
* @param valSrcExpls explanation for the value source part.
|
||||
* @return an explanation for the custom score
|
||||
*/
|
||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpls[]) throws IOException {
|
||||
if (valSrcExpls.length == 1) {
|
||||
return customExplain(doc, subQueryExpl, valSrcExpls[0]);
|
||||
}
|
||||
if (valSrcExpls.length == 0) {
|
||||
return subQueryExpl;
|
||||
}
|
||||
float valSrcScore = 1;
|
||||
for (int i = 0; i < valSrcExpls.length; i++) {
|
||||
valSrcScore *= valSrcExpls[i].getValue();
|
||||
}
|
||||
Explanation exp = new Explanation( valSrcScore * subQueryExpl.getValue(), "custom score: product of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
for (int i = 0; i < valSrcExpls.length; i++) {
|
||||
exp.addDetail(valSrcExpls[i]);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explain the custom score.
|
||||
* Whenever overriding {@link #customScore(int, float, float)},
|
||||
* this method should also be overridden to provide the correct explanation
|
||||
* for the part of the custom scoring.
|
||||
*
|
||||
* @param doc doc being explained.
|
||||
* @param subQueryExpl explanation for the sub-query part.
|
||||
* @param valSrcExpl explanation for the value source part.
|
||||
* @return an explanation for the custom score
|
||||
*/
|
||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl) throws IOException {
|
||||
float valSrcScore = 1;
|
||||
if (valSrcExpl != null) {
|
||||
valSrcScore *= valSrcExpl.getValue();
|
||||
}
|
||||
Explanation exp = new Explanation( valSrcScore * subQueryExpl.getValue(), "custom score: product of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
exp.addDetail(valSrcExpl);
|
||||
return exp;
|
||||
}
|
||||
|
||||
}
|
|
@ -40,7 +40,7 @@ import org.apache.lucene.util.ToStringUtils;
|
|||
* For most simple/convenient use cases this query is likely to be a
|
||||
* {@link org.apache.lucene.search.function.FieldScoreQuery FieldScoreQuery}</li>
|
||||
* </ol>
|
||||
* Subclasses can modify the computation by overriding {@link #customScore(int, float, float)}.
|
||||
* Subclasses can modify the computation by overriding {@link #getCustomScoreProvider}.
|
||||
*
|
||||
* @lucene.experimental
|
||||
*/
|
||||
|
@ -80,7 +80,6 @@ public class CustomScoreQuery extends Query {
|
|||
* This parameter is optional - it can be null or even an empty array.
|
||||
*/
|
||||
public CustomScoreQuery(Query subQuery, ValueSourceQuery... valSrcQueries) {
|
||||
super();
|
||||
this.subQuery = subQuery;
|
||||
this.valSrcQueries = valSrcQueries!=null?
|
||||
valSrcQueries : new ValueSourceQuery[0];
|
||||
|
@ -90,11 +89,23 @@ public class CustomScoreQuery extends Query {
|
|||
/*(non-Javadoc) @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) */
|
||||
@Override
|
||||
public Query rewrite(IndexReader reader) throws IOException {
|
||||
subQuery = subQuery.rewrite(reader);
|
||||
for(int i = 0; i < valSrcQueries.length; i++) {
|
||||
valSrcQueries[i] = (ValueSourceQuery) valSrcQueries[i].rewrite(reader);
|
||||
CustomScoreQuery clone = null;
|
||||
|
||||
final Query sq = subQuery.rewrite(reader);
|
||||
if (sq != subQuery) {
|
||||
clone = (CustomScoreQuery) clone();
|
||||
clone.subQuery = sq;
|
||||
}
|
||||
return this;
|
||||
|
||||
for(int i = 0; i < valSrcQueries.length; i++) {
|
||||
final ValueSourceQuery v = (ValueSourceQuery) valSrcQueries[i].rewrite(reader);
|
||||
if (v != valSrcQueries[i]) {
|
||||
if (clone == null) clone = (CustomScoreQuery) clone();
|
||||
clone.valSrcQueries[i] = v;
|
||||
}
|
||||
}
|
||||
|
||||
return (clone == null) ? this : clone;
|
||||
}
|
||||
|
||||
/*(non-Javadoc) @see org.apache.lucene.search.Query#extractTerms(java.util.Set) */
|
||||
|
@ -139,7 +150,8 @@ public class CustomScoreQuery extends Query {
|
|||
}
|
||||
CustomScoreQuery other = (CustomScoreQuery)o;
|
||||
if (this.getBoost() != other.getBoost() ||
|
||||
!this.subQuery.equals(other.subQuery)||
|
||||
!this.subQuery.equals(other.subQuery) ||
|
||||
this.strict != other.strict ||
|
||||
this.valSrcQueries.length != other.valSrcQueries.length) {
|
||||
return false;
|
||||
}
|
||||
|
@ -150,132 +162,17 @@ public class CustomScoreQuery extends Query {
|
|||
@Override
|
||||
public int hashCode() {
|
||||
return (getClass().hashCode() + subQuery.hashCode() + Arrays.hashCode(valSrcQueries))
|
||||
^ Float.floatToIntBits(getBoost());
|
||||
}
|
||||
^ Float.floatToIntBits(getBoost()) ^ (strict ? 1234 : 4321);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a custom score by the subQuery score and a number of
|
||||
* ValueSourceQuery scores.
|
||||
* <p>
|
||||
* Subclasses can override this method to modify the custom score.
|
||||
* <p>
|
||||
* If your custom scoring is different than the default herein you
|
||||
* should override at least one of the two customScore() methods.
|
||||
* If the number of ValueSourceQueries is always < 2 it is
|
||||
* sufficient to override the other
|
||||
* {@link #customScore(int, float, float) customScore()}
|
||||
* method, which is simpler.
|
||||
* <p>
|
||||
* The default computation herein is a multiplication of given scores:
|
||||
* <pre>
|
||||
* ModifiedScore = valSrcScore * valSrcScores[0] * valSrcScores[1] * ...
|
||||
* </pre>
|
||||
*
|
||||
* @param doc id of scored doc.
|
||||
* @param subQueryScore score of that doc by the subQuery.
|
||||
* @param valSrcScores scores of that doc by the ValueSourceQuery.
|
||||
* @return custom score.
|
||||
* Returns a {@link CustomScoreProvider} that calculates the custom scores
|
||||
* for the given {@link IndexReader}. The default implementation returns a default
|
||||
* implementation as specified in the docs of {@link CustomScoreProvider}.
|
||||
* @since 2.9.2
|
||||
*/
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScores[]) {
|
||||
if (valSrcScores.length == 1) {
|
||||
return customScore(doc, subQueryScore, valSrcScores[0]);
|
||||
}
|
||||
if (valSrcScores.length == 0) {
|
||||
return customScore(doc, subQueryScore, 1);
|
||||
}
|
||||
float score = subQueryScore;
|
||||
for(int i = 0; i < valSrcScores.length; i++) {
|
||||
score *= valSrcScores[i];
|
||||
}
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute a custom score by the subQuery score and the ValueSourceQuery score.
|
||||
* <p>
|
||||
* Subclasses can override this method to modify the custom score.
|
||||
* <p>
|
||||
* If your custom scoring is different than the default herein you
|
||||
* should override at least one of the two customScore() methods.
|
||||
* If the number of ValueSourceQueries is always < 2 it is
|
||||
* sufficient to override this customScore() method, which is simpler.
|
||||
* <p>
|
||||
* The default computation herein is a multiplication of the two scores:
|
||||
* <pre>
|
||||
* ModifiedScore = subQueryScore * valSrcScore
|
||||
* </pre>
|
||||
*
|
||||
* <p><b>NOTE</b>: The doc is relative to the current
|
||||
* reader, last passed to {@link #setNextReader}.
|
||||
*
|
||||
* @param doc id of scored doc.
|
||||
* @param subQueryScore score of that doc by the subQuery.
|
||||
* @param valSrcScore score of that doc by the ValueSourceQuery.
|
||||
* @return custom score.
|
||||
*/
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore) {
|
||||
return subQueryScore * valSrcScore;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the scoring switches to another reader.
|
||||
*
|
||||
* @param reader
|
||||
* next IndexReader
|
||||
*/
|
||||
public void setNextReader(IndexReader reader) throws IOException {
|
||||
}
|
||||
|
||||
/**
|
||||
* Explain the custom score.
|
||||
* Whenever overriding {@link #customScore(int, float, float[])},
|
||||
* this method should also be overridden to provide the correct explanation
|
||||
* for the part of the custom scoring.
|
||||
*
|
||||
* @param doc doc being explained.
|
||||
* @param subQueryExpl explanation for the sub-query part.
|
||||
* @param valSrcExpls explanation for the value source part.
|
||||
* @return an explanation for the custom score
|
||||
*/
|
||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpls[]) {
|
||||
if (valSrcExpls.length == 1) {
|
||||
return customExplain(doc, subQueryExpl, valSrcExpls[0]);
|
||||
}
|
||||
if (valSrcExpls.length == 0) {
|
||||
return subQueryExpl;
|
||||
}
|
||||
float valSrcScore = 1;
|
||||
for (int i = 0; i < valSrcExpls.length; i++) {
|
||||
valSrcScore *= valSrcExpls[i].getValue();
|
||||
}
|
||||
Explanation exp = new Explanation( valSrcScore * subQueryExpl.getValue(), "custom score: product of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
for (int i = 0; i < valSrcExpls.length; i++) {
|
||||
exp.addDetail(valSrcExpls[i]);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explain the custom score.
|
||||
* Whenever overriding {@link #customScore(int, float, float)},
|
||||
* this method should also be overridden to provide the correct explanation
|
||||
* for the part of the custom scoring.
|
||||
*
|
||||
* @param doc doc being explained.
|
||||
* @param subQueryExpl explanation for the sub-query part.
|
||||
* @param valSrcExpl explanation for the value source part.
|
||||
* @return an explanation for the custom score
|
||||
*/
|
||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl) {
|
||||
float valSrcScore = 1;
|
||||
if (valSrcExpl != null) {
|
||||
valSrcScore *= valSrcExpl.getValue();
|
||||
}
|
||||
Explanation exp = new Explanation( valSrcScore * subQueryExpl.getValue(), "custom score: product of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
exp.addDetail(valSrcExpl);
|
||||
return exp;
|
||||
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) throws IOException {
|
||||
return new CustomScoreProvider(reader);
|
||||
}
|
||||
|
||||
//=========================== W E I G H T ============================
|
||||
|
@ -371,7 +268,7 @@ public class CustomScoreQuery extends Query {
|
|||
for(int i = 0; i < valSrcWeights.length; i++) {
|
||||
valSrcExpls[i] = valSrcWeights[i].explain(reader, doc);
|
||||
}
|
||||
Explanation customExp = customExplain(doc,subQueryExpl,valSrcExpls);
|
||||
Explanation customExp = CustomScoreQuery.this.getCustomScoreProvider(reader).customExplain(doc,subQueryExpl,valSrcExpls);
|
||||
float sc = getValue() * customExp.getValue();
|
||||
Explanation res = new ComplexExplanation(
|
||||
true, sc, CustomScoreQuery.this.toString() + ", product of:");
|
||||
|
@ -398,6 +295,7 @@ public class CustomScoreQuery extends Query {
|
|||
private Scorer subQueryScorer;
|
||||
private Scorer[] valSrcScorers;
|
||||
private IndexReader reader;
|
||||
private final CustomScoreProvider provider;
|
||||
private float vScores[]; // reused in score() to avoid allocating this array for each doc
|
||||
|
||||
// constructor
|
||||
|
@ -409,7 +307,7 @@ public class CustomScoreQuery extends Query {
|
|||
this.valSrcScorers = valSrcScorers;
|
||||
this.reader = reader;
|
||||
this.vScores = new float[valSrcScorers.length];
|
||||
setNextReader(reader);
|
||||
this.provider = CustomScoreQuery.this.getCustomScoreProvider(reader);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -434,7 +332,7 @@ public class CustomScoreQuery extends Query {
|
|||
for (int i = 0; i < valSrcScorers.length; i++) {
|
||||
vScores[i] = valSrcScorers[i].score();
|
||||
}
|
||||
return qWeight * customScore(subQueryScorer.docID(), subQueryScorer.score(), vScores);
|
||||
return qWeight * provider.customScore(subQueryScorer.docID(), subQueryScorer.score(), vScores);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -33,7 +33,6 @@ import org.apache.lucene.index.IndexReader;
|
|||
/**
|
||||
* Test CustomScoreQuery search.
|
||||
*/
|
||||
@SuppressWarnings({"MagicNumber"})
|
||||
public class TestCustomScoreQuery extends FunctionTestSetup {
|
||||
|
||||
/* @override constructor */
|
||||
|
@ -96,23 +95,26 @@ public class TestCustomScoreQuery extends FunctionTestSetup {
|
|||
public String name() {
|
||||
return "customAdd";
|
||||
}
|
||||
|
||||
/*(non-Javadoc) @see org.apache.lucene.search.function.CustomScoreQuery#customScore(int, float, float) */
|
||||
|
||||
@Override
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore) {
|
||||
return subQueryScore + valSrcScore;
|
||||
}
|
||||
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||
return new CustomScoreProvider(reader) {
|
||||
@Override
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScore) {
|
||||
return subQueryScore + valSrcScore;
|
||||
}
|
||||
|
||||
/* (non-Javadoc)@see org.apache.lucene.search.function.CustomScoreQuery#customExplain(int, org.apache.lucene.search.Explanation, org.apache.lucene.search.Explanation)*/
|
||||
@Override
|
||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl) {
|
||||
float valSrcScore = valSrcExpl == null ? 0 : valSrcExpl.getValue();
|
||||
Explanation exp = new Explanation(valSrcScore + subQueryExpl.getValue(), "custom score: sum of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
if (valSrcExpl != null) {
|
||||
exp.addDetail(valSrcExpl);
|
||||
}
|
||||
return exp;
|
||||
@Override
|
||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl) {
|
||||
float valSrcScore = valSrcExpl == null ? 0 : valSrcExpl.getValue();
|
||||
Explanation exp = new Explanation(valSrcScore + subQueryExpl.getValue(), "custom score: sum of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
if (valSrcExpl != null) {
|
||||
exp.addDetail(valSrcExpl);
|
||||
}
|
||||
return exp;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,52 +132,55 @@ public class TestCustomScoreQuery extends FunctionTestSetup {
|
|||
return "customMulAdd";
|
||||
}
|
||||
|
||||
/*(non-Javadoc) @see org.apache.lucene.search.function.CustomScoreQuery#customScore(int, float, float) */
|
||||
@Override
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScores[]) {
|
||||
if (valSrcScores.length == 0) {
|
||||
return subQueryScore;
|
||||
}
|
||||
if (valSrcScores.length == 1) {
|
||||
return subQueryScore + valSrcScores[0];
|
||||
// confirm that skipping beyond the last doc, on the
|
||||
// previous reader, hits NO_MORE_DOCS
|
||||
}
|
||||
return (subQueryScore + valSrcScores[0]) * valSrcScores[1]; // we know there are two
|
||||
}
|
||||
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||
return new CustomScoreProvider(reader) {
|
||||
@Override
|
||||
public float customScore(int doc, float subQueryScore, float valSrcScores[]) {
|
||||
if (valSrcScores.length == 0) {
|
||||
return subQueryScore;
|
||||
}
|
||||
if (valSrcScores.length == 1) {
|
||||
return subQueryScore + valSrcScores[0];
|
||||
// confirm that skipping beyond the last doc, on the
|
||||
// previous reader, hits NO_MORE_DOCS
|
||||
}
|
||||
return (subQueryScore + valSrcScores[0]) * valSrcScores[1]; // we know there are two
|
||||
}
|
||||
|
||||
/* (non-Javadoc)@see org.apache.lucene.search.function.CustomScoreQuery#customExplain(int, org.apache.lucene.search.Explanation, org.apache.lucene.search.Explanation)*/
|
||||
@Override
|
||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpls[]) {
|
||||
if (valSrcExpls.length == 0) {
|
||||
return subQueryExpl;
|
||||
}
|
||||
Explanation exp = new Explanation(valSrcExpls[0].getValue() + subQueryExpl.getValue(), "sum of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
exp.addDetail(valSrcExpls[0]);
|
||||
if (valSrcExpls.length == 1) {
|
||||
exp.setDescription("CustomMulAdd, sum of:");
|
||||
return exp;
|
||||
}
|
||||
Explanation exp2 = new Explanation(valSrcExpls[1].getValue() * exp.getValue(), "custom score: product of:");
|
||||
exp2.addDetail(valSrcExpls[1]);
|
||||
exp2.addDetail(exp);
|
||||
return exp2;
|
||||
@Override
|
||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpls[]) {
|
||||
if (valSrcExpls.length == 0) {
|
||||
return subQueryExpl;
|
||||
}
|
||||
Explanation exp = new Explanation(valSrcExpls[0].getValue() + subQueryExpl.getValue(), "sum of:");
|
||||
exp.addDetail(subQueryExpl);
|
||||
exp.addDetail(valSrcExpls[0]);
|
||||
if (valSrcExpls.length == 1) {
|
||||
exp.setDescription("CustomMulAdd, sum of:");
|
||||
return exp;
|
||||
}
|
||||
Explanation exp2 = new Explanation(valSrcExpls[1].getValue() * exp.getValue(), "custom score: product of:");
|
||||
exp2.addDetail(valSrcExpls[1]);
|
||||
exp2.addDetail(exp);
|
||||
return exp2;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private final class CustomExternalQuery extends CustomScoreQuery {
|
||||
private IndexReader reader;
|
||||
private int[] values;
|
||||
|
||||
public float customScore(int doc, float subScore, float valSrcScore) {
|
||||
assertTrue(doc <= reader.maxDoc());
|
||||
return (float) values[doc];
|
||||
}
|
||||
|
||||
public void setNextReader(IndexReader r) throws IOException {
|
||||
reader = r;
|
||||
values = FieldCache.DEFAULT.getInts(r, INT_FIELD);
|
||||
@Override
|
||||
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) throws IOException {
|
||||
final int[] values = FieldCache.DEFAULT.getInts(reader, INT_FIELD);
|
||||
return new CustomScoreProvider(reader) {
|
||||
@Override
|
||||
public float customScore(int doc, float subScore, float valSrcScore) throws IOException {
|
||||
assertTrue(doc <= reader.maxDoc());
|
||||
return (float) values[doc];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public CustomExternalQuery(Query q) {
|
||||
|
|
Loading…
Reference in New Issue