mirror of
https://github.com/honeymoose/OpenSearch.git
synced 2025-02-20 03:45:02 +00:00
Add default to field_value_factor
field_value_factor now takes a default that is used if the document doesn't have a value for that field. It looks like: "field_value_factor": { "field": "popularity", "missing": 1 } Closes #10841
This commit is contained in:
parent
240bcc3f08
commit
cb89a14010
@ -175,7 +175,8 @@ doing so would look like:
|
|||||||
"field_value_factor": {
|
"field_value_factor": {
|
||||||
"field": "popularity",
|
"field": "popularity",
|
||||||
"factor": 1.2,
|
"factor": 1.2,
|
||||||
"modifier": "sqrt"
|
"modifier": "sqrt",
|
||||||
|
"missing": 1
|
||||||
}
|
}
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
|
|
||||||
@ -193,6 +194,8 @@ There are a number of options for the `field_value_factor` function:
|
|||||||
|`modifier` |Modifier to apply to the field value, can be one of: `none`, `log`,
|
|`modifier` |Modifier to apply to the field value, can be one of: `none`, `log`,
|
||||||
`log1p`, `log2p`, `ln`, `ln1p`, `ln2p`, `square`, `sqrt`, or `reciprocal`.
|
`log1p`, `log2p`, `ln`, `ln1p`, `ln2p`, `square`, `sqrt`, or `reciprocal`.
|
||||||
Defaults to `none`.
|
Defaults to `none`.
|
||||||
|
|`missing` |Value used if the document doesn't have that field. The modifier
|
||||||
|
and factor are still applied to it as though it were read from the document.
|
||||||
|=======================================================================
|
|=======================================================================
|
||||||
|
|
||||||
Keep in mind that taking the log() of 0, or the square root of a negative number
|
Keep in mind that taking the log() of 0, or the square root of a negative number
|
||||||
|
@ -36,14 +36,20 @@ public class FieldValueFactorFunction extends ScoreFunction {
|
|||||||
private final String field;
|
private final String field;
|
||||||
private final float boostFactor;
|
private final float boostFactor;
|
||||||
private final Modifier modifier;
|
private final Modifier modifier;
|
||||||
|
/**
|
||||||
|
* Value used if the document is missing the field.
|
||||||
|
*/
|
||||||
|
private final Double missing;
|
||||||
private final IndexNumericFieldData indexFieldData;
|
private final IndexNumericFieldData indexFieldData;
|
||||||
|
|
||||||
public FieldValueFactorFunction(String field, float boostFactor, Modifier modifierType, IndexNumericFieldData indexFieldData) {
|
public FieldValueFactorFunction(String field, float boostFactor, Modifier modifierType, Double missing,
|
||||||
|
IndexNumericFieldData indexFieldData) {
|
||||||
super(CombineFunction.MULT);
|
super(CombineFunction.MULT);
|
||||||
this.field = field;
|
this.field = field;
|
||||||
this.boostFactor = boostFactor;
|
this.boostFactor = boostFactor;
|
||||||
this.modifier = modifierType;
|
this.modifier = modifierType;
|
||||||
this.indexFieldData = indexFieldData;
|
this.indexFieldData = indexFieldData;
|
||||||
|
this.missing = missing;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -55,26 +61,32 @@ public class FieldValueFactorFunction extends ScoreFunction {
|
|||||||
public double score(int docId, float subQueryScore) {
|
public double score(int docId, float subQueryScore) {
|
||||||
values.setDocument(docId);
|
values.setDocument(docId);
|
||||||
final int numValues = values.count();
|
final int numValues = values.count();
|
||||||
|
double value;
|
||||||
if (numValues > 0) {
|
if (numValues > 0) {
|
||||||
double val = values.valueAt(0) * boostFactor;
|
value = values.valueAt(0);
|
||||||
double result = modifier.apply(val);
|
} else if (missing != null) {
|
||||||
if (Double.isNaN(result) || Double.isInfinite(result)) {
|
value = missing;
|
||||||
throw new ElasticsearchException("Result of field modification [" + modifier.toString() +
|
|
||||||
"(" + val + ")] must be a number");
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
} else {
|
} else {
|
||||||
throw new ElasticsearchException("Missing value for field [" + field + "]");
|
throw new ElasticsearchException("Missing value for field [" + field + "]");
|
||||||
}
|
}
|
||||||
|
double val = value * boostFactor;
|
||||||
|
double result = modifier.apply(val);
|
||||||
|
if (Double.isNaN(result) || Double.isInfinite(result)) {
|
||||||
|
throw new ElasticsearchException("Result of field modification [" + modifier.toString() + "(" + val
|
||||||
|
+ ")] must be a number");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Explanation explainScore(int docId, Explanation subQueryScore) {
|
public Explanation explainScore(int docId, Explanation subQueryScore) {
|
||||||
String modifierStr = modifier != null ? modifier.toString() : "";
|
String modifierStr = modifier != null ? modifier.toString() : "";
|
||||||
|
String defaultStr = missing != null ? "?:" + missing : "";
|
||||||
double score = score(docId, subQueryScore.getValue());
|
double score = score(docId, subQueryScore.getValue());
|
||||||
return Explanation.match(
|
return Explanation.match(
|
||||||
CombineFunction.toFloat(score),
|
CombineFunction.toFloat(score),
|
||||||
"field value function: " + modifierStr + "(" + "doc['" + field + "'].value * factor=" + boostFactor + ")");
|
String.format(Locale.ROOT,
|
||||||
|
"field value function: %s(doc['%s'].value%s * factor=%s)", modifierStr, field, defaultStr, boostFactor));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ import java.util.Locale;
|
|||||||
public class FieldValueFactorFunctionBuilder extends ScoreFunctionBuilder {
|
public class FieldValueFactorFunctionBuilder extends ScoreFunctionBuilder {
|
||||||
private String field = null;
|
private String field = null;
|
||||||
private Float factor = null;
|
private Float factor = null;
|
||||||
|
private Double missing = null;
|
||||||
private FieldValueFactorFunction.Modifier modifier = null;
|
private FieldValueFactorFunction.Modifier modifier = null;
|
||||||
|
|
||||||
public FieldValueFactorFunctionBuilder(String fieldName) {
|
public FieldValueFactorFunctionBuilder(String fieldName) {
|
||||||
@ -49,6 +50,14 @@ public class FieldValueFactorFunctionBuilder extends ScoreFunctionBuilder {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value used instead of the field value for documents that don't have that field defined.
|
||||||
|
*/
|
||||||
|
public FieldValueFactorFunctionBuilder missing(double missing) {
|
||||||
|
this.missing = missing;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public FieldValueFactorFunctionBuilder modifier(FieldValueFactorFunction.Modifier modifier) {
|
public FieldValueFactorFunctionBuilder modifier(FieldValueFactorFunction.Modifier modifier) {
|
||||||
this.modifier = modifier;
|
this.modifier = modifier;
|
||||||
return this;
|
return this;
|
||||||
@ -65,6 +74,10 @@ public class FieldValueFactorFunctionBuilder extends ScoreFunctionBuilder {
|
|||||||
builder.field("factor", factor);
|
builder.field("factor", factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (missing != null) {
|
||||||
|
builder.field("missing", missing);
|
||||||
|
}
|
||||||
|
|
||||||
if (modifier != null) {
|
if (modifier != null) {
|
||||||
builder.field("modifier", modifier.toString().toLowerCase(Locale.ROOT));
|
builder.field("modifier", modifier.toString().toLowerCase(Locale.ROOT));
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,8 @@ import java.util.Locale;
|
|||||||
* "field_value_factor": {
|
* "field_value_factor": {
|
||||||
* "field": "myfield",
|
* "field": "myfield",
|
||||||
* "factor": 1.5,
|
* "factor": 1.5,
|
||||||
* "modifier": "square"
|
* "modifier": "square",
|
||||||
|
* "missing": 1
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* </pre>
|
* </pre>
|
||||||
@ -56,6 +57,7 @@ public class FieldValueFactorFunctionParser implements ScoreFunctionParser {
|
|||||||
String field = null;
|
String field = null;
|
||||||
float boostFactor = 1;
|
float boostFactor = 1;
|
||||||
FieldValueFactorFunction.Modifier modifier = FieldValueFactorFunction.Modifier.NONE;
|
FieldValueFactorFunction.Modifier modifier = FieldValueFactorFunction.Modifier.NONE;
|
||||||
|
Double missing = null;
|
||||||
XContentParser.Token token;
|
XContentParser.Token token;
|
||||||
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
|
||||||
if (token == XContentParser.Token.FIELD_NAME) {
|
if (token == XContentParser.Token.FIELD_NAME) {
|
||||||
@ -67,6 +69,8 @@ public class FieldValueFactorFunctionParser implements ScoreFunctionParser {
|
|||||||
boostFactor = parser.floatValue();
|
boostFactor = parser.floatValue();
|
||||||
} else if ("modifier".equals(currentFieldName)) {
|
} else if ("modifier".equals(currentFieldName)) {
|
||||||
modifier = FieldValueFactorFunction.Modifier.valueOf(parser.text().toUpperCase(Locale.ROOT));
|
modifier = FieldValueFactorFunction.Modifier.valueOf(parser.text().toUpperCase(Locale.ROOT));
|
||||||
|
} else if ("missing".equals(currentFieldName)) {
|
||||||
|
missing = parser.doubleValue();
|
||||||
} else {
|
} else {
|
||||||
throw new QueryParsingException(parseContext.index(), NAMES[0] + " query does not support [" + currentFieldName + "]");
|
throw new QueryParsingException(parseContext.index(), NAMES[0] + " query does not support [" + currentFieldName + "]");
|
||||||
}
|
}
|
||||||
@ -84,7 +88,7 @@ public class FieldValueFactorFunctionParser implements ScoreFunctionParser {
|
|||||||
if (mapper == null) {
|
if (mapper == null) {
|
||||||
throw new ElasticsearchException("Unable to find a field mapper for field [" + field + "]");
|
throw new ElasticsearchException("Unable to find a field mapper for field [" + field + "]");
|
||||||
}
|
}
|
||||||
return new FieldValueFactorFunction(field, boostFactor, modifier,
|
return new FieldValueFactorFunction(field, boostFactor, modifier, missing,
|
||||||
(IndexNumericFieldData)searchContext.fieldData().getForField(mapper));
|
(IndexNumericFieldData)searchContext.fieldData().getForField(mapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,6 +88,14 @@ public class FunctionScoreFieldValueTests extends ElasticsearchIntegrationTest {
|
|||||||
// We are expecting an exception, because 3 has no field
|
// We are expecting an exception, because 3 has no field
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// doc 3 doesn't have a "test" field but we're defaulting it to 100 so it should be last
|
||||||
|
response = client().prepareSearch("test")
|
||||||
|
.setExplain(randomBoolean())
|
||||||
|
.setQuery(functionScoreQuery(matchAllQuery(),
|
||||||
|
fieldValueFactorFunction("test").modifier(FieldValueFactorFunction.Modifier.RECIPROCAL).missing(100)))
|
||||||
|
.get();
|
||||||
|
assertOrderedSearchHits(response, "1", "2", "3");
|
||||||
|
|
||||||
// n divided by 0 is infinity, which should provoke an exception.
|
// n divided by 0 is infinity, which should provoke an exception.
|
||||||
try {
|
try {
|
||||||
response = client().prepareSearch("test")
|
response = client().prepareSearch("test")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user