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.
|
toString. These are advanced APIs and subject to change suddenly.
|
||||||
(Tim Smith via Mike McCandless)
|
(Tim Smith via Mike McCandless)
|
||||||
|
|
||||||
|
* LUCENE-2190: Removed deprecated customScore() and customExplain()
|
||||||
|
methods from experimental CustomScoreQuery. (Uwe Schindler)
|
||||||
|
|
||||||
Changes in runtime behavior
|
Changes in runtime behavior
|
||||||
|
|
||||||
* LUCENE-1923: Made IndexReader.toString() produce something
|
* LUCENE-1923: Made IndexReader.toString() produce something
|
||||||
|
@ -259,10 +262,16 @@ API Changes
|
||||||
* LUCENE-1972 (3.0.1 only): Restore SortField.getComparatorSource
|
* LUCENE-1972 (3.0.1 only): Restore SortField.getComparatorSource
|
||||||
(it was accidentally removed in 3.0.0) (John Wang via Uwe Schindler)
|
(it was accidentally removed in 3.0.0) (John Wang via Uwe Schindler)
|
||||||
|
|
||||||
* LUCENE-2190: Added setNextReader method to CustomScoreQuery, which
|
* LUCENE-2190: Added a new class CustomScoreProvider to function package
|
||||||
is necessary with per-segment searching to notify the subclass
|
that can be subclassed to provide custom scoring to CustomScoreQuery.
|
||||||
which reader the int doc, passed to customScore, refers to. (Paul
|
The methods in CustomScoreQuery that did this before were deprecated
|
||||||
chez Jamespot via Mike McCandless)
|
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
|
* LUCENE-2080: Deprecate Version.LUCENE_CURRENT, as using this constant
|
||||||
will cause backwards compatibility problems when upgrading Lucene. See
|
will cause backwards compatibility problems when upgrading Lucene. See
|
||||||
|
|
|
@ -43,7 +43,7 @@
|
||||||
<property name="dev.version" value="3.1-dev"/>
|
<property name="dev.version" value="3.1-dev"/>
|
||||||
<property name="version" value="${dev.version}"/>
|
<property name="version" value="${dev.version}"/>
|
||||||
<property name="backwards.branch" value="lucene_3_0_back_compat_tests"/>
|
<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="spec.version" value="${version}"/>
|
||||||
<property name="year" value="2000-${current.year}"/>
|
<property name="year" value="2000-${current.year}"/>
|
||||||
<property name="final.name" value="lucene-${name}-${version}"/>
|
<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.Document;
|
||||||
import org.apache.lucene.document.Field;
|
import org.apache.lucene.document.Field;
|
||||||
import org.apache.lucene.index.IndexWriter;
|
import org.apache.lucene.index.IndexWriter;
|
||||||
|
import org.apache.lucene.index.IndexReader;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.search.IndexSearcher;
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
import org.apache.lucene.search.Query;
|
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.TermQuery;
|
||||||
import org.apache.lucene.search.TopDocs;
|
import org.apache.lucene.search.TopDocs;
|
||||||
import org.apache.lucene.search.function.CustomScoreQuery;
|
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;
|
||||||
import org.apache.lucene.search.function.FieldScoreQuery.Type;
|
import org.apache.lucene.search.function.FieldScoreQuery.Type;
|
||||||
import org.apache.lucene.spatial.geohash.GeoHashUtils;
|
import org.apache.lucene.spatial.geohash.GeoHashUtils;
|
||||||
|
@ -189,20 +191,26 @@ public class TestCartesian extends TestCase{
|
||||||
CustomScoreQuery customScore = new CustomScoreQuery(dq.getQuery(tq),fsQuery){
|
CustomScoreQuery customScore = new CustomScoreQuery(dq.getQuery(tq),fsQuery){
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||||
System.out.println(doc);
|
return new CustomScoreProvider(reader) {
|
||||||
if (dq.distanceFilter.getDistance(doc) == null)
|
@Override // TODO: broken, as reader is not used!
|
||||||
return 0;
|
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);
|
double distance = dq.distanceFilter.getDistance(doc);
|
||||||
// boost score shouldn't exceed 1
|
// boost score shouldn't exceed 1
|
||||||
if (distance < 1.0d)
|
if (distance < 1.0d)
|
||||||
distance = 1.0d;
|
distance = 1.0d;
|
||||||
//boost by distance is invertly proportional to
|
//boost by distance is invertly proportional to
|
||||||
// to distance from center point to location
|
// to distance from center point to location
|
||||||
float score = (float) ((miles - distance) / miles );
|
float score = (float) ((miles - distance) / miles );
|
||||||
return score * subQueryScore;
|
return score * subQueryScore;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
// Create a distance sort
|
// Create a distance sort
|
||||||
// As the radius filter has performed the distance calculations
|
// 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){
|
CustomScoreQuery customScore = new CustomScoreQuery(dq.getQuery(tq),fsQuery){
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||||
System.out.println(doc);
|
return new CustomScoreProvider(reader) {
|
||||||
if (dq.distanceFilter.getDistance(doc) == null)
|
@Override // TODO: broken, as reader is not used!
|
||||||
return 0;
|
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);
|
double distance = dq.distanceFilter.getDistance(doc);
|
||||||
// boost score shouldn't exceed 1
|
// boost score shouldn't exceed 1
|
||||||
if (distance < 1.0d)
|
if (distance < 1.0d)
|
||||||
distance = 1.0d;
|
distance = 1.0d;
|
||||||
//boost by distance is invertly proportional to
|
//boost by distance is invertly proportional to
|
||||||
// to distance from center point to location
|
// to distance from center point to location
|
||||||
float score = (float) ((miles - distance) / miles );
|
float score = (float) ((miles - distance) / miles );
|
||||||
return score * subQueryScore;
|
return score * subQueryScore;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
// Create a distance sort
|
// Create a distance sort
|
||||||
// As the radius filter has performed the distance calculations
|
// 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);
|
FieldScoreQuery fsQuery = new FieldScoreQuery("geo_distance", Type.FLOAT);
|
||||||
|
|
||||||
CustomScoreQuery customScore = new CustomScoreQuery(dq.getQuery(tq),fsQuery){
|
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){
|
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||||
//System.out.println(doc);
|
//System.out.println(doc);
|
||||||
if (dq.distanceFilter.getDistance(doc) == null)
|
if (dq.distanceFilter.getDistance(doc) == null)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
double distance = dq.distanceFilter.getDistance(doc);
|
double distance = dq.distanceFilter.getDistance(doc);
|
||||||
// boost score shouldn't exceed 1
|
// boost score shouldn't exceed 1
|
||||||
if (distance < 1.0d)
|
if (distance < 1.0d)
|
||||||
distance = 1.0d;
|
distance = 1.0d;
|
||||||
//boost by distance is invertly proportional to
|
//boost by distance is invertly proportional to
|
||||||
// to distance from center point to location
|
// to distance from center point to location
|
||||||
float score = (float) ( (miles - distance) / miles );
|
float score = (float) ( (miles - distance) / miles );
|
||||||
return score * subQueryScore;
|
return score * subQueryScore;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
// Create a distance sort
|
// Create a distance sort
|
||||||
// As the radius filter has performed the distance calculations
|
// As the radius filter has performed the distance calculations
|
||||||
// already, pass in the filter to reuse the results.
|
// 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);
|
FieldScoreQuery fsQuery = new FieldScoreQuery("geo_distance", Type.FLOAT);
|
||||||
CustomScoreQuery customScore = new CustomScoreQuery(tq,fsQuery){
|
CustomScoreQuery customScore = new CustomScoreQuery(tq,fsQuery){
|
||||||
|
@Override
|
||||||
@Override
|
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||||
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
return new CustomScoreProvider(reader) {
|
||||||
//System.out.println(doc);
|
@Override // TODO: broken, as reader is not used!
|
||||||
if (dq.distanceFilter.getDistance(doc) == null)
|
public float customScore(int doc, float subQueryScore, float valSrcScore){
|
||||||
return 0;
|
//System.out.println(doc);
|
||||||
|
if (dq.distanceFilter.getDistance(doc) == null)
|
||||||
double distance = dq.distanceFilter.getDistance(doc);
|
return 0;
|
||||||
// boost score shouldn't exceed 1
|
|
||||||
if (distance < 1.0d)
|
double distance = dq.distanceFilter.getDistance(doc);
|
||||||
distance = 1.0d;
|
// boost score shouldn't exceed 1
|
||||||
//boost by distance is invertly proportional to
|
if (distance < 1.0d)
|
||||||
// to distance from center point to location
|
distance = 1.0d;
|
||||||
float score = (float) ( (miles - distance) / miles );
|
//boost by distance is invertly proportional to
|
||||||
return score * subQueryScore;
|
// to distance from center point to location
|
||||||
}
|
float score = (float) ( (miles - distance) / miles );
|
||||||
};
|
return score * subQueryScore;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
// Create a distance sort
|
// Create a distance sort
|
||||||
// As the radius filter has performed the distance calculations
|
// As the radius filter has performed the distance calculations
|
||||||
// already, pass in the filter to reuse the results.
|
// 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
|
* For most simple/convenient use cases this query is likely to be a
|
||||||
* {@link org.apache.lucene.search.function.FieldScoreQuery FieldScoreQuery}</li>
|
* {@link org.apache.lucene.search.function.FieldScoreQuery FieldScoreQuery}</li>
|
||||||
* </ol>
|
* </ol>
|
||||||
* Subclasses can modify the computation by overriding {@link #customScore(int, float, float)}.
|
* Subclasses can modify the computation by overriding {@link #getCustomScoreProvider}.
|
||||||
*
|
*
|
||||||
* @lucene.experimental
|
* @lucene.experimental
|
||||||
*/
|
*/
|
||||||
|
@ -80,7 +80,6 @@ public class CustomScoreQuery extends Query {
|
||||||
* This parameter is optional - it can be null or even an empty array.
|
* This parameter is optional - it can be null or even an empty array.
|
||||||
*/
|
*/
|
||||||
public CustomScoreQuery(Query subQuery, ValueSourceQuery... valSrcQueries) {
|
public CustomScoreQuery(Query subQuery, ValueSourceQuery... valSrcQueries) {
|
||||||
super();
|
|
||||||
this.subQuery = subQuery;
|
this.subQuery = subQuery;
|
||||||
this.valSrcQueries = valSrcQueries!=null?
|
this.valSrcQueries = valSrcQueries!=null?
|
||||||
valSrcQueries : new ValueSourceQuery[0];
|
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) */
|
/*(non-Javadoc) @see org.apache.lucene.search.Query#rewrite(org.apache.lucene.index.IndexReader) */
|
||||||
@Override
|
@Override
|
||||||
public Query rewrite(IndexReader reader) throws IOException {
|
public Query rewrite(IndexReader reader) throws IOException {
|
||||||
subQuery = subQuery.rewrite(reader);
|
CustomScoreQuery clone = null;
|
||||||
for(int i = 0; i < valSrcQueries.length; i++) {
|
|
||||||
valSrcQueries[i] = (ValueSourceQuery) valSrcQueries[i].rewrite(reader);
|
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) */
|
/*(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;
|
CustomScoreQuery other = (CustomScoreQuery)o;
|
||||||
if (this.getBoost() != other.getBoost() ||
|
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) {
|
this.valSrcQueries.length != other.valSrcQueries.length) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -150,132 +162,17 @@ public class CustomScoreQuery extends Query {
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return (getClass().hashCode() + subQuery.hashCode() + Arrays.hashCode(valSrcQueries))
|
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
|
* Returns a {@link CustomScoreProvider} that calculates the custom scores
|
||||||
* ValueSourceQuery scores.
|
* for the given {@link IndexReader}. The default implementation returns a default
|
||||||
* <p>
|
* implementation as specified in the docs of {@link CustomScoreProvider}.
|
||||||
* Subclasses can override this method to modify the custom score.
|
* @since 2.9.2
|
||||||
* <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[]) {
|
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) throws IOException {
|
||||||
if (valSrcScores.length == 1) {
|
return new CustomScoreProvider(reader);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//=========================== W E I G H T ============================
|
//=========================== W E I G H T ============================
|
||||||
|
@ -371,7 +268,7 @@ public class CustomScoreQuery extends Query {
|
||||||
for(int i = 0; i < valSrcWeights.length; i++) {
|
for(int i = 0; i < valSrcWeights.length; i++) {
|
||||||
valSrcExpls[i] = valSrcWeights[i].explain(reader, doc);
|
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();
|
float sc = getValue() * customExp.getValue();
|
||||||
Explanation res = new ComplexExplanation(
|
Explanation res = new ComplexExplanation(
|
||||||
true, sc, CustomScoreQuery.this.toString() + ", product of:");
|
true, sc, CustomScoreQuery.this.toString() + ", product of:");
|
||||||
|
@ -398,6 +295,7 @@ public class CustomScoreQuery extends Query {
|
||||||
private Scorer subQueryScorer;
|
private Scorer subQueryScorer;
|
||||||
private Scorer[] valSrcScorers;
|
private Scorer[] valSrcScorers;
|
||||||
private IndexReader reader;
|
private IndexReader reader;
|
||||||
|
private final CustomScoreProvider provider;
|
||||||
private float vScores[]; // reused in score() to avoid allocating this array for each doc
|
private float vScores[]; // reused in score() to avoid allocating this array for each doc
|
||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
|
@ -409,7 +307,7 @@ public class CustomScoreQuery extends Query {
|
||||||
this.valSrcScorers = valSrcScorers;
|
this.valSrcScorers = valSrcScorers;
|
||||||
this.reader = reader;
|
this.reader = reader;
|
||||||
this.vScores = new float[valSrcScorers.length];
|
this.vScores = new float[valSrcScorers.length];
|
||||||
setNextReader(reader);
|
this.provider = CustomScoreQuery.this.getCustomScoreProvider(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -434,7 +332,7 @@ public class CustomScoreQuery extends Query {
|
||||||
for (int i = 0; i < valSrcScorers.length; i++) {
|
for (int i = 0; i < valSrcScorers.length; i++) {
|
||||||
vScores[i] = valSrcScorers[i].score();
|
vScores[i] = valSrcScorers[i].score();
|
||||||
}
|
}
|
||||||
return qWeight * customScore(subQueryScorer.docID(), subQueryScorer.score(), vScores);
|
return qWeight * provider.customScore(subQueryScorer.docID(), subQueryScorer.score(), vScores);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -33,7 +33,6 @@ import org.apache.lucene.index.IndexReader;
|
||||||
/**
|
/**
|
||||||
* Test CustomScoreQuery search.
|
* Test CustomScoreQuery search.
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings({"MagicNumber"})
|
|
||||||
public class TestCustomScoreQuery extends FunctionTestSetup {
|
public class TestCustomScoreQuery extends FunctionTestSetup {
|
||||||
|
|
||||||
/* @override constructor */
|
/* @override constructor */
|
||||||
|
@ -96,23 +95,26 @@ public class TestCustomScoreQuery extends FunctionTestSetup {
|
||||||
public String name() {
|
public String name() {
|
||||||
return "customAdd";
|
return "customAdd";
|
||||||
}
|
}
|
||||||
|
|
||||||
/*(non-Javadoc) @see org.apache.lucene.search.function.CustomScoreQuery#customScore(int, float, float) */
|
|
||||||
@Override
|
@Override
|
||||||
public float customScore(int doc, float subQueryScore, float valSrcScore) {
|
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||||
return subQueryScore + valSrcScore;
|
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
|
||||||
@Override
|
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl) {
|
||||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpl) {
|
float valSrcScore = valSrcExpl == null ? 0 : valSrcExpl.getValue();
|
||||||
float valSrcScore = valSrcExpl == null ? 0 : valSrcExpl.getValue();
|
Explanation exp = new Explanation(valSrcScore + subQueryExpl.getValue(), "custom score: sum of:");
|
||||||
Explanation exp = new Explanation(valSrcScore + subQueryExpl.getValue(), "custom score: sum of:");
|
exp.addDetail(subQueryExpl);
|
||||||
exp.addDetail(subQueryExpl);
|
if (valSrcExpl != null) {
|
||||||
if (valSrcExpl != null) {
|
exp.addDetail(valSrcExpl);
|
||||||
exp.addDetail(valSrcExpl);
|
}
|
||||||
}
|
return exp;
|
||||||
return exp;
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,52 +132,55 @@ public class TestCustomScoreQuery extends FunctionTestSetup {
|
||||||
return "customMulAdd";
|
return "customMulAdd";
|
||||||
}
|
}
|
||||||
|
|
||||||
/*(non-Javadoc) @see org.apache.lucene.search.function.CustomScoreQuery#customScore(int, float, float) */
|
|
||||||
@Override
|
@Override
|
||||||
public float customScore(int doc, float subQueryScore, float valSrcScores[]) {
|
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) {
|
||||||
if (valSrcScores.length == 0) {
|
return new CustomScoreProvider(reader) {
|
||||||
return subQueryScore;
|
@Override
|
||||||
}
|
public float customScore(int doc, float subQueryScore, float valSrcScores[]) {
|
||||||
if (valSrcScores.length == 1) {
|
if (valSrcScores.length == 0) {
|
||||||
return subQueryScore + valSrcScores[0];
|
return subQueryScore;
|
||||||
// confirm that skipping beyond the last doc, on the
|
}
|
||||||
// previous reader, hits NO_MORE_DOCS
|
if (valSrcScores.length == 1) {
|
||||||
}
|
return subQueryScore + valSrcScores[0];
|
||||||
return (subQueryScore + valSrcScores[0]) * valSrcScores[1]; // we know there are two
|
// 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
|
||||||
@Override
|
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpls[]) {
|
||||||
public Explanation customExplain(int doc, Explanation subQueryExpl, Explanation valSrcExpls[]) {
|
if (valSrcExpls.length == 0) {
|
||||||
if (valSrcExpls.length == 0) {
|
return subQueryExpl;
|
||||||
return subQueryExpl;
|
}
|
||||||
}
|
Explanation exp = new Explanation(valSrcExpls[0].getValue() + subQueryExpl.getValue(), "sum of:");
|
||||||
Explanation exp = new Explanation(valSrcExpls[0].getValue() + subQueryExpl.getValue(), "sum of:");
|
exp.addDetail(subQueryExpl);
|
||||||
exp.addDetail(subQueryExpl);
|
exp.addDetail(valSrcExpls[0]);
|
||||||
exp.addDetail(valSrcExpls[0]);
|
if (valSrcExpls.length == 1) {
|
||||||
if (valSrcExpls.length == 1) {
|
exp.setDescription("CustomMulAdd, sum of:");
|
||||||
exp.setDescription("CustomMulAdd, sum of:");
|
return exp;
|
||||||
return exp;
|
}
|
||||||
}
|
Explanation exp2 = new Explanation(valSrcExpls[1].getValue() * exp.getValue(), "custom score: product of:");
|
||||||
Explanation exp2 = new Explanation(valSrcExpls[1].getValue() * exp.getValue(), "custom score: product of:");
|
exp2.addDetail(valSrcExpls[1]);
|
||||||
exp2.addDetail(valSrcExpls[1]);
|
exp2.addDetail(exp);
|
||||||
exp2.addDetail(exp);
|
return exp2;
|
||||||
return exp2;
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final class CustomExternalQuery extends CustomScoreQuery {
|
private final class CustomExternalQuery extends CustomScoreQuery {
|
||||||
private IndexReader reader;
|
|
||||||
private int[] values;
|
|
||||||
|
|
||||||
public float customScore(int doc, float subScore, float valSrcScore) {
|
@Override
|
||||||
assertTrue(doc <= reader.maxDoc());
|
protected CustomScoreProvider getCustomScoreProvider(IndexReader reader) throws IOException {
|
||||||
return (float) values[doc];
|
final int[] values = FieldCache.DEFAULT.getInts(reader, INT_FIELD);
|
||||||
}
|
return new CustomScoreProvider(reader) {
|
||||||
|
@Override
|
||||||
public void setNextReader(IndexReader r) throws IOException {
|
public float customScore(int doc, float subScore, float valSrcScore) throws IOException {
|
||||||
reader = r;
|
assertTrue(doc <= reader.maxDoc());
|
||||||
values = FieldCache.DEFAULT.getInts(r, INT_FIELD);
|
return (float) values[doc];
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public CustomExternalQuery(Query q) {
|
public CustomExternalQuery(Query q) {
|
||||||
|
|
Loading…
Reference in New Issue