mirror of https://github.com/apache/lucene.git
LUCENE-7723: Add hashCode and equals to DoubleValuesSource
This commit is contained in:
parent
808171ac00
commit
1a278ae89b
|
@ -89,6 +89,9 @@ API Changes
|
||||||
* LUCENE-7867: The deprecated Token class is now only available in the test
|
* LUCENE-7867: The deprecated Token class is now only available in the test
|
||||||
framework (Alan Woodward, Adrien Grand)
|
framework (Alan Woodward, Adrien Grand)
|
||||||
|
|
||||||
|
* LUCENE-7723: DoubleValuesSource enforces implementation of equals() and
|
||||||
|
hashCode() (Alan Woodward)
|
||||||
|
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
|
|
||||||
* LUCENE-7626: IndexWriter will no longer accept broken token offsets
|
* LUCENE-7626: IndexWriter will no longer accept broken token offsets
|
||||||
|
|
|
@ -20,9 +20,7 @@ package org.apache.lucene.search;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.function.DoubleToLongFunction;
|
import java.util.function.DoubleToLongFunction;
|
||||||
import java.util.function.DoubleUnaryOperator;
|
|
||||||
import java.util.function.LongToDoubleFunction;
|
import java.util.function.LongToDoubleFunction;
|
||||||
import java.util.function.ToDoubleBiFunction;
|
|
||||||
|
|
||||||
import org.apache.lucene.index.DocValues;
|
import org.apache.lucene.index.DocValues;
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
|
@ -76,14 +74,33 @@ public abstract class DoubleValuesSource {
|
||||||
return new DoubleValuesSortField(this, reverse);
|
return new DoubleValuesSortField(this, reverse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract int hashCode();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract boolean equals(Object obj);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract String toString();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert to a LongValuesSource by casting the double values to longs
|
* Convert to a LongValuesSource by casting the double values to longs
|
||||||
*/
|
*/
|
||||||
public final LongValuesSource toLongValuesSource() {
|
public final LongValuesSource toLongValuesSource() {
|
||||||
return new LongValuesSource() {
|
return new LongDoubleValuesSource(this);
|
||||||
@Override
|
}
|
||||||
|
|
||||||
|
private static class LongDoubleValuesSource extends LongValuesSource {
|
||||||
|
|
||||||
|
private final DoubleValuesSource inner;
|
||||||
|
|
||||||
|
private LongDoubleValuesSource(DoubleValuesSource inner) {
|
||||||
|
this.inner = inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||||
DoubleValues in = DoubleValuesSource.this.getValues(ctx, scores);
|
DoubleValues in = inner.getValues(ctx, scores);
|
||||||
return new LongValues() {
|
return new LongValues() {
|
||||||
@Override
|
@Override
|
||||||
public long longValue() throws IOException {
|
public long longValue() throws IOException {
|
||||||
|
@ -99,9 +116,27 @@ public abstract class DoubleValuesSource {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean needsScores() {
|
public boolean needsScores() {
|
||||||
return DoubleValuesSource.this.needsScores();
|
return inner.needsScores();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
LongDoubleValuesSource that = (LongDoubleValuesSource) o;
|
||||||
|
return Objects.equals(inner, that.inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(inner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "long(" + inner.toString() + ")";
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -164,115 +199,80 @@ public abstract class DoubleValuesSource {
|
||||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) {
|
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) {
|
||||||
return scoreExplanation;
|
return scoreExplanation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj == this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "scores";
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a DoubleValuesSource that always returns a constant value
|
* Creates a DoubleValuesSource that always returns a constant value
|
||||||
*/
|
*/
|
||||||
public static DoubleValuesSource constant(double value) {
|
public static DoubleValuesSource constant(double value) {
|
||||||
return new DoubleValuesSource() {
|
return new ConstantValuesSource(value);
|
||||||
@Override
|
|
||||||
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
|
||||||
return new DoubleValues() {
|
|
||||||
@Override
|
|
||||||
public double doubleValue() throws IOException {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean advanceExact(int doc) throws IOException {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needsScores() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) {
|
|
||||||
return Explanation.match((float) value, "constant(" + value + ")");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "constant(" + value + ")";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static class ConstantValuesSource extends DoubleValuesSource {
|
||||||
* Creates a DoubleValuesSource that is a function of another DoubleValuesSource
|
|
||||||
*/
|
|
||||||
public static DoubleValuesSource function(DoubleValuesSource in, String description, DoubleUnaryOperator function) {
|
|
||||||
return new DoubleValuesSource() {
|
|
||||||
@Override
|
|
||||||
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
|
||||||
DoubleValues inputs = in.getValues(ctx, scores);
|
|
||||||
return new DoubleValues() {
|
|
||||||
@Override
|
|
||||||
public double doubleValue() throws IOException {
|
|
||||||
return function.applyAsDouble(inputs.doubleValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private final double value;
|
||||||
public boolean advanceExact(int doc) throws IOException {
|
|
||||||
return inputs.advanceExact(doc);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private ConstantValuesSource(double value) {
|
||||||
public boolean needsScores() {
|
this.value = value;
|
||||||
return in.needsScores();
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||||
Explanation inner = in.explain(ctx, docId, scoreExplanation);
|
return new DoubleValues() {
|
||||||
return Explanation.match((float) function.applyAsDouble(inner.getValue()), description + ", computed from:", inner, scoreExplanation);
|
@Override
|
||||||
}
|
public double doubleValue() throws IOException {
|
||||||
};
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Creates a DoubleValuesSource that is a function of another DoubleValuesSource and a score
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
* @param in the DoubleValuesSource to use as an input
|
return true;
|
||||||
* @param description a description of the function
|
}
|
||||||
* @param function a function of the form (source, score) == result
|
};
|
||||||
*/
|
}
|
||||||
public static DoubleValuesSource scoringFunction(DoubleValuesSource in, String description, ToDoubleBiFunction<Double, Double> function) {
|
|
||||||
return new DoubleValuesSource() {
|
|
||||||
@Override
|
|
||||||
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
|
||||||
DoubleValues inputs = in.getValues(ctx, scores);
|
|
||||||
return new DoubleValues() {
|
|
||||||
@Override
|
|
||||||
public double doubleValue() throws IOException {
|
|
||||||
return function.applyAsDouble(inputs.doubleValue(), scores.doubleValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean advanceExact(int doc) throws IOException {
|
public boolean needsScores() {
|
||||||
return inputs.advanceExact(doc);
|
return false;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean needsScores() {
|
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) {
|
||||||
return true;
|
return Explanation.match((float) value, "constant(" + value + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
public int hashCode() {
|
||||||
Explanation inner = in.explain(ctx, docId, scoreExplanation);
|
return Objects.hash(value);
|
||||||
return Explanation.match((float) function.applyAsDouble((double)inner.getValue(), (double)scoreExplanation.getValue()),
|
}
|
||||||
description + ", computed from:", inner, scoreExplanation);
|
|
||||||
}
|
@Override
|
||||||
};
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
ConstantValuesSource that = (ConstantValuesSource) o;
|
||||||
|
return Double.compare(that.value, value) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "constant(" + value + ")";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -312,6 +312,11 @@ public abstract class DoubleValuesSource {
|
||||||
Objects.equals(decoder, that.decoder);
|
Objects.equals(decoder, that.decoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "double(" + field + ")";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(field, decoder);
|
return Objects.hash(field, decoder);
|
||||||
|
@ -342,9 +347,9 @@ public abstract class DoubleValuesSource {
|
||||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
||||||
DoubleValues values = getValues(ctx, null);
|
DoubleValues values = getValues(ctx, null);
|
||||||
if (values.advanceExact(docId))
|
if (values.advanceExact(docId))
|
||||||
return Explanation.match((float)values.doubleValue(), "double(" + field + ")");
|
return Explanation.match((float)values.doubleValue(), this.toString());
|
||||||
else
|
else
|
||||||
return Explanation.noMatch("double(" + field + ")");
|
return Explanation.noMatch(this.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,6 +52,15 @@ public abstract class LongValuesSource {
|
||||||
*/
|
*/
|
||||||
public abstract boolean needsScores();
|
public abstract boolean needsScores();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract int hashCode();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract boolean equals(Object obj);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public abstract String toString();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a sort field based on the value of this producer
|
* Create a sort field based on the value of this producer
|
||||||
* @param reverse true if the sort should be decreasing
|
* @param reverse true if the sort should be decreasing
|
||||||
|
@ -78,27 +87,55 @@ public abstract class LongValuesSource {
|
||||||
* Creates a LongValuesSource that always returns a constant value
|
* Creates a LongValuesSource that always returns a constant value
|
||||||
*/
|
*/
|
||||||
public static LongValuesSource constant(long value) {
|
public static LongValuesSource constant(long value) {
|
||||||
return new LongValuesSource() {
|
return new ConstantLongValuesSource(value);
|
||||||
@Override
|
}
|
||||||
public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
|
||||||
return new LongValues() {
|
|
||||||
@Override
|
|
||||||
public long longValue() throws IOException {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private static class ConstantLongValuesSource extends LongValuesSource {
|
||||||
public boolean advanceExact(int doc) throws IOException {
|
|
||||||
return true;
|
private final long value;
|
||||||
}
|
|
||||||
};
|
private ConstantLongValuesSource(long value) {
|
||||||
}
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||||
|
return new LongValues() {
|
||||||
|
@Override
|
||||||
|
public long longValue() throws IOException {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsScores() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
ConstantLongValuesSource that = (ConstantLongValuesSource) o;
|
||||||
|
return value == that.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "constant(" + value + ")";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needsScores() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class FieldValuesSource extends LongValuesSource {
|
private static class FieldValuesSource extends LongValuesSource {
|
||||||
|
@ -117,6 +154,11 @@ public abstract class LongValuesSource {
|
||||||
return Objects.equals(field, that.field);
|
return Objects.equals(field, that.field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "long(" + field + ")";
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return Objects.hash(field);
|
return Objects.hash(field);
|
||||||
|
|
|
@ -92,6 +92,14 @@ public class TestDoubleValuesSource extends LuceneTestCase {
|
||||||
assertEquals(vs1.hashCode(), vs2.hashCode());
|
assertEquals(vs1.hashCode(), vs2.hashCode());
|
||||||
DoubleValuesSource v3 = DoubleValuesSource.fromLongField("long");
|
DoubleValuesSource v3 = DoubleValuesSource.fromLongField("long");
|
||||||
assertFalse(vs1.equals(v3));
|
assertFalse(vs1.equals(v3));
|
||||||
|
|
||||||
|
assertEquals(DoubleValuesSource.constant(5), DoubleValuesSource.constant(5));
|
||||||
|
assertEquals(DoubleValuesSource.constant(5).hashCode(), DoubleValuesSource.constant(5).hashCode());
|
||||||
|
assertFalse((DoubleValuesSource.constant(5).equals(DoubleValuesSource.constant(6))));
|
||||||
|
|
||||||
|
assertEquals(DoubleValuesSource.SCORES, DoubleValuesSource.SCORES);
|
||||||
|
assertFalse(DoubleValuesSource.constant(5).equals(DoubleValuesSource.SCORES));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSimpleFieldSortables() throws Exception {
|
public void testSimpleFieldSortables() throws Exception {
|
||||||
|
@ -184,13 +192,6 @@ public class TestDoubleValuesSource extends LuceneTestCase {
|
||||||
testExplanations(q, DoubleValuesSource.fromDoubleField("double"));
|
testExplanations(q, DoubleValuesSource.fromDoubleField("double"));
|
||||||
testExplanations(q, DoubleValuesSource.fromDoubleField("onefield"));
|
testExplanations(q, DoubleValuesSource.fromDoubleField("onefield"));
|
||||||
testExplanations(q, DoubleValuesSource.constant(5.45));
|
testExplanations(q, DoubleValuesSource.constant(5.45));
|
||||||
testExplanations(q, DoubleValuesSource.function(
|
|
||||||
DoubleValuesSource.fromDoubleField("double"), "v * 4 + 73",
|
|
||||||
v -> v * 4 + 73
|
|
||||||
));
|
|
||||||
testExplanations(q, DoubleValuesSource.scoringFunction(
|
|
||||||
DoubleValuesSource.fromDoubleField("double"), "v * score", (v, s) -> v * s
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,4 +228,5 @@ public class TestDoubleValuesSource extends LuceneTestCase {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -706,6 +706,51 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class PlusOneValuesSource extends DoubleValuesSource {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||||
|
return new DoubleValues() {
|
||||||
|
int doc = -1;
|
||||||
|
@Override
|
||||||
|
public double doubleValue() throws IOException {
|
||||||
|
return doc + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
this.doc = doc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsScores() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
||||||
|
return Explanation.match(docId + 1, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return obj.getClass() == PlusOneValuesSource.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void testCustomDoubleValuesSource() throws Exception {
|
public void testCustomDoubleValuesSource() throws Exception {
|
||||||
Directory dir = newDirectory();
|
Directory dir = newDirectory();
|
||||||
RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
|
RandomIndexWriter writer = new RandomIndexWriter(random(), dir);
|
||||||
|
@ -718,35 +763,7 @@ public class TestRangeFacetCounts extends FacetTestCase {
|
||||||
// Test wants 3 docs in one segment:
|
// Test wants 3 docs in one segment:
|
||||||
writer.forceMerge(1);
|
writer.forceMerge(1);
|
||||||
|
|
||||||
final DoubleValuesSource vs = new DoubleValuesSource() {
|
final DoubleValuesSource vs = new PlusOneValuesSource();
|
||||||
|
|
||||||
@Override
|
|
||||||
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
|
||||||
return new DoubleValues() {
|
|
||||||
int doc = -1;
|
|
||||||
@Override
|
|
||||||
public double doubleValue() throws IOException {
|
|
||||||
return doc + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean advanceExact(int doc) throws IOException {
|
|
||||||
this.doc = doc;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needsScores() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
|
||||||
return Explanation.match(docId + 1, "");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
FacetsConfig config = new FacetsConfig();
|
FacetsConfig config = new FacetsConfig();
|
||||||
|
|
||||||
|
|
|
@ -121,15 +121,6 @@ public final class FunctionScoreQuery extends Query {
|
||||||
Explanation.match(boost, "boost"), expl);
|
Explanation.match(boost, "boost"), expl);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Explanation scoreExplanation(LeafReaderContext context, int doc, DoubleValues scores) throws IOException {
|
|
||||||
if (valueSource.needsScores() == false)
|
|
||||||
return Explanation.match((float) scores.doubleValue(), valueSource.toString());
|
|
||||||
float score = (float) scores.doubleValue();
|
|
||||||
return Explanation.match(score, "computed from:",
|
|
||||||
Explanation.match(score, valueSource.toString()),
|
|
||||||
inner.explain(context, doc));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Scorer scorer(LeafReaderContext context) throws IOException {
|
public Scorer scorer(LeafReaderContext context) throws IOException {
|
||||||
Scorer in = inner.scorer(context);
|
Scorer in = inner.scorer(context);
|
||||||
|
|
|
@ -20,6 +20,7 @@ import java.io.IOException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.IdentityHashMap;
|
import java.util.IdentityHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.apache.lucene.index.LeafReaderContext;
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.search.DocIdSetIterator;
|
import org.apache.lucene.search.DocIdSetIterator;
|
||||||
|
@ -120,85 +121,141 @@ public abstract class ValueSource {
|
||||||
* Expose this ValueSource as a LongValuesSource
|
* Expose this ValueSource as a LongValuesSource
|
||||||
*/
|
*/
|
||||||
public LongValuesSource asLongValuesSource() {
|
public LongValuesSource asLongValuesSource() {
|
||||||
return new LongValuesSource() {
|
return new WrappedLongValuesSource(this);
|
||||||
@Override
|
}
|
||||||
public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
|
||||||
Map context = new IdentityHashMap<>();
|
|
||||||
FakeScorer scorer = new FakeScorer();
|
|
||||||
context.put("scorer", scorer);
|
|
||||||
final FunctionValues fv = ValueSource.this.getValues(context, ctx);
|
|
||||||
return new LongValues() {
|
|
||||||
|
|
||||||
@Override
|
private static class WrappedLongValuesSource extends LongValuesSource {
|
||||||
public long longValue() throws IOException {
|
|
||||||
return fv.longVal(scorer.current);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private final ValueSource in;
|
||||||
public boolean advanceExact(int doc) throws IOException {
|
|
||||||
scorer.current = doc;
|
private WrappedLongValuesSource(ValueSource in) {
|
||||||
if (scores != null && scores.advanceExact(doc))
|
this.in = in;
|
||||||
scorer.score = (float) scores.doubleValue();
|
}
|
||||||
else
|
|
||||||
scorer.score = 0;
|
@Override
|
||||||
return fv.exists(doc);
|
public LongValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||||
}
|
Map context = new IdentityHashMap<>();
|
||||||
};
|
FakeScorer scorer = new FakeScorer();
|
||||||
}
|
context.put("scorer", scorer);
|
||||||
|
final FunctionValues fv = in.getValues(context, ctx);
|
||||||
|
return new LongValues() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long longValue() throws IOException {
|
||||||
|
return fv.longVal(scorer.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
scorer.current = doc;
|
||||||
|
if (scores != null && scores.advanceExact(doc))
|
||||||
|
scorer.score = (float) scores.doubleValue();
|
||||||
|
else
|
||||||
|
scorer.score = 0;
|
||||||
|
return fv.exists(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsScores() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
WrappedLongValuesSource that = (WrappedLongValuesSource) o;
|
||||||
|
return Objects.equals(in, that.in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return in.toString();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean needsScores() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expose this ValueSource as a DoubleValuesSource
|
* Expose this ValueSource as a DoubleValuesSource
|
||||||
*/
|
*/
|
||||||
public DoubleValuesSource asDoubleValuesSource() {
|
public DoubleValuesSource asDoubleValuesSource() {
|
||||||
return new DoubleValuesSource() {
|
return new WrappedDoubleValuesSource(this);
|
||||||
@Override
|
}
|
||||||
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
|
||||||
Map context = new HashMap<>();
|
|
||||||
FakeScorer scorer = new FakeScorer();
|
|
||||||
context.put("scorer", scorer);
|
|
||||||
FunctionValues fv = ValueSource.this.getValues(context, ctx);
|
|
||||||
return new DoubleValues() {
|
|
||||||
|
|
||||||
@Override
|
private static class WrappedDoubleValuesSource extends DoubleValuesSource {
|
||||||
public double doubleValue() throws IOException {
|
|
||||||
return fv.doubleVal(scorer.current);
|
private final ValueSource in;
|
||||||
|
|
||||||
|
private WrappedDoubleValuesSource(ValueSource in) {
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||||
|
Map context = new HashMap<>();
|
||||||
|
FakeScorer scorer = new FakeScorer();
|
||||||
|
context.put("scorer", scorer);
|
||||||
|
FunctionValues fv = in.getValues(context, ctx);
|
||||||
|
return new DoubleValues() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double doubleValue() throws IOException {
|
||||||
|
return fv.doubleVal(scorer.current);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
scorer.current = doc;
|
||||||
|
if (scores != null && scores.advanceExact(doc)) {
|
||||||
|
scorer.score = (float) scores.doubleValue();
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
scorer.score = 0;
|
||||||
|
return fv.exists(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean advanceExact(int doc) throws IOException {
|
public boolean needsScores() {
|
||||||
scorer.current = doc;
|
return true; // be on the safe side
|
||||||
if (scores != null && scores.advanceExact(doc)) {
|
}
|
||||||
scorer.score = (float) scores.doubleValue();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
scorer.score = 0;
|
|
||||||
return fv.exists(doc);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean needsScores() {
|
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
||||||
return true; // be on the safe side
|
Map context = new HashMap<>();
|
||||||
}
|
FakeScorer scorer = new FakeScorer();
|
||||||
|
scorer.score = scoreExplanation.getValue();
|
||||||
|
context.put("scorer", scorer);
|
||||||
|
FunctionValues fv = in.getValues(context, ctx);
|
||||||
|
return fv.explain(docId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (o == null || getClass() != o.getClass()) return false;
|
||||||
|
WrappedDoubleValuesSource that = (WrappedDoubleValuesSource) o;
|
||||||
|
return Objects.equals(in, that.in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return Objects.hash(in);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return in.toString();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Explanation explain(LeafReaderContext ctx, int docId, Explanation scoreExplanation) throws IOException {
|
|
||||||
Map context = new HashMap<>();
|
|
||||||
FakeScorer scorer = new FakeScorer();
|
|
||||||
scorer.score = scoreExplanation.getValue();
|
|
||||||
context.put("scorer", scorer);
|
|
||||||
FunctionValues fv = ValueSource.this.getValues(context, ctx);
|
|
||||||
return fv.explain(docId);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -59,19 +59,15 @@ public class TestFunctionScoreExplanations extends BaseExplanationTestCase {
|
||||||
|
|
||||||
public void testExplanationsIncludingScore() throws Exception {
|
public void testExplanationsIncludingScore() throws Exception {
|
||||||
|
|
||||||
DoubleValuesSource scores = DoubleValuesSource.function(DoubleValuesSource.SCORES, "v * 2", v -> v * 2);
|
|
||||||
|
|
||||||
Query q = new TermQuery(new Term(FIELD, "w1"));
|
Query q = new TermQuery(new Term(FIELD, "w1"));
|
||||||
FunctionScoreQuery csq = new FunctionScoreQuery(q, scores);
|
FunctionScoreQuery csq = new FunctionScoreQuery(q, DoubleValuesSource.SCORES);
|
||||||
|
|
||||||
qtest(csq, new int[] { 0, 1, 2, 3 });
|
qtest(csq, new int[] { 0, 1, 2, 3 });
|
||||||
|
|
||||||
Explanation e1 = searcher.explain(q, 0);
|
Explanation e1 = searcher.explain(q, 0);
|
||||||
Explanation e = searcher.explain(csq, 0);
|
Explanation e = searcher.explain(csq, 0);
|
||||||
|
|
||||||
assertEquals(e.getDetails().length, 2);
|
assertEquals(e, e1);
|
||||||
|
|
||||||
assertEquals(e1.getValue() * 2, e.getValue(), 0.00001);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSubExplanations() throws IOException {
|
public void testSubExplanations() throws IOException {
|
||||||
|
|
|
@ -17,12 +17,18 @@
|
||||||
|
|
||||||
package org.apache.lucene.queries.function;
|
package org.apache.lucene.queries.function;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.function.DoubleUnaryOperator;
|
||||||
|
import java.util.function.ToDoubleBiFunction;
|
||||||
|
|
||||||
import org.apache.lucene.index.DirectoryReader;
|
import org.apache.lucene.index.DirectoryReader;
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.index.LeafReaderContext;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.search.BooleanClause;
|
import org.apache.lucene.search.BooleanClause;
|
||||||
import org.apache.lucene.search.BooleanQuery;
|
import org.apache.lucene.search.BooleanQuery;
|
||||||
import org.apache.lucene.search.BoostQuery;
|
import org.apache.lucene.search.BoostQuery;
|
||||||
|
import org.apache.lucene.search.DoubleValues;
|
||||||
import org.apache.lucene.search.DoubleValuesSource;
|
import org.apache.lucene.search.DoubleValuesSource;
|
||||||
import org.apache.lucene.search.IndexSearcher;
|
import org.apache.lucene.search.IndexSearcher;
|
||||||
import org.apache.lucene.search.Query;
|
import org.apache.lucene.search.Query;
|
||||||
|
@ -70,7 +76,7 @@ public class TestFunctionScoreQuery extends FunctionTestSetup {
|
||||||
public void testScoreModifyingSource() throws Exception {
|
public void testScoreModifyingSource() throws Exception {
|
||||||
|
|
||||||
DoubleValuesSource iii = DoubleValuesSource.fromIntField("iii");
|
DoubleValuesSource iii = DoubleValuesSource.fromIntField("iii");
|
||||||
DoubleValuesSource score = DoubleValuesSource.scoringFunction(iii, "v * s", (v, s) -> v * s);
|
DoubleValuesSource score = scoringFunction(iii, (v, s) -> v * s);
|
||||||
|
|
||||||
BooleanQuery bq = new BooleanQuery.Builder()
|
BooleanQuery bq = new BooleanQuery.Builder()
|
||||||
.add(new TermQuery(new Term(TEXT_FIELD, "first")), BooleanClause.Occur.SHOULD)
|
.add(new TermQuery(new Term(TEXT_FIELD, "first")), BooleanClause.Occur.SHOULD)
|
||||||
|
@ -95,8 +101,7 @@ public class TestFunctionScoreQuery extends FunctionTestSetup {
|
||||||
// check boosts with non-distributive score source
|
// check boosts with non-distributive score source
|
||||||
public void testBoostsAreAppliedLast() throws Exception {
|
public void testBoostsAreAppliedLast() throws Exception {
|
||||||
|
|
||||||
DoubleValuesSource scores
|
DoubleValuesSource scores = function(DoubleValuesSource.SCORES, v -> Math.log(v + 4));
|
||||||
= DoubleValuesSource.function(DoubleValuesSource.SCORES, "ln(v + 4)", v -> Math.log(v + 4));
|
|
||||||
|
|
||||||
Query q1 = new FunctionScoreQuery(new TermQuery(new Term(TEXT_FIELD, "text")), scores);
|
Query q1 = new FunctionScoreQuery(new TermQuery(new Term(TEXT_FIELD, "text")), scores);
|
||||||
TopDocs plain = searcher.search(q1, 5);
|
TopDocs plain = searcher.search(q1, 5);
|
||||||
|
@ -111,4 +116,84 @@ public class TestFunctionScoreQuery extends FunctionTestSetup {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static DoubleValuesSource function(DoubleValuesSource in, DoubleUnaryOperator function) {
|
||||||
|
return new DoubleValuesSource() {
|
||||||
|
@Override
|
||||||
|
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||||
|
DoubleValues v = in.getValues(ctx, scores);
|
||||||
|
return new DoubleValues() {
|
||||||
|
@Override
|
||||||
|
public double doubleValue() throws IOException {
|
||||||
|
return function.applyAsDouble(v.doubleValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
return v.advanceExact(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsScores() {
|
||||||
|
return in.needsScores();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "fn";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static DoubleValuesSource scoringFunction(DoubleValuesSource in, ToDoubleBiFunction<Double, Double> function) {
|
||||||
|
return new DoubleValuesSource() {
|
||||||
|
@Override
|
||||||
|
public DoubleValues getValues(LeafReaderContext ctx, DoubleValues scores) throws IOException {
|
||||||
|
DoubleValues v = in.getValues(ctx, scores);
|
||||||
|
return new DoubleValues() {
|
||||||
|
@Override
|
||||||
|
public double doubleValue() throws IOException {
|
||||||
|
return function.applyAsDouble(v.doubleValue(), scores.doubleValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advanceExact(int doc) throws IOException {
|
||||||
|
return v.advanceExact(doc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean needsScores() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "fn";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -171,6 +171,21 @@ public class DocumentValueSourceDictionaryTest extends LuceneTestCase {
|
||||||
public boolean needsScores() {
|
public boolean needsScores() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue