فهرست منبع

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
Nik Everett 10 سال پیش
والد
کامیت
cb89a14010

+ 4 - 1
docs/reference/query-dsl/queries/function-score-query.asciidoc

@@ -175,7 +175,8 @@ doing so would look like:
 "field_value_factor": {
   "field": "popularity",
   "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`,
  `log1p`, `log2p`, `ln`, `ln1p`, `ln2p`, `square`, `sqrt`, or `reciprocal`.
  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

+ 21 - 9
src/main/java/org/elasticsearch/common/lucene/search/function/FieldValueFactorFunction.java

@@ -36,14 +36,20 @@ public class FieldValueFactorFunction extends ScoreFunction {
     private final String field;
     private final float boostFactor;
     private final Modifier modifier;
+    /**
+     * Value used if the document is missing the field.
+     */
+    private final Double missing;
     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);
         this.field = field;
         this.boostFactor = boostFactor;
         this.modifier = modifierType;
         this.indexFieldData = indexFieldData;
+        this.missing = missing;
     }
 
     @Override
@@ -55,26 +61,32 @@ public class FieldValueFactorFunction extends ScoreFunction {
             public double score(int docId, float subQueryScore) {
                 values.setDocument(docId);
                 final int numValues = values.count();
+                double value;
                 if (numValues > 0) {
-                    double val = values.valueAt(0) * 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;
+                    value = values.valueAt(0);
+                } else if (missing != null) {
+                    value = missing;
                 } else {
                     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
             public Explanation explainScore(int docId, Explanation subQueryScore) {
                 String modifierStr = modifier != null ? modifier.toString() : "";
+                String defaultStr = missing != null ? "?:" + missing : "";
                 double score = score(docId, subQueryScore.getValue());
                 return Explanation.match(
                         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));
             }
         };
     }

+ 13 - 0
src/main/java/org/elasticsearch/index/query/functionscore/fieldvaluefactor/FieldValueFactorFunctionBuilder.java

@@ -33,6 +33,7 @@ import java.util.Locale;
 public class FieldValueFactorFunctionBuilder extends ScoreFunctionBuilder {
     private String field = null;
     private Float factor = null;
+    private Double missing = null;
     private FieldValueFactorFunction.Modifier modifier = null;
 
     public FieldValueFactorFunctionBuilder(String fieldName) {
@@ -49,6 +50,14 @@ public class FieldValueFactorFunctionBuilder extends ScoreFunctionBuilder {
         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) {
         this.modifier = modifier;
         return this;
@@ -65,6 +74,10 @@ public class FieldValueFactorFunctionBuilder extends ScoreFunctionBuilder {
             builder.field("factor", factor);
         }
 
+        if (missing != null) {
+            builder.field("missing", missing);
+        }
+
         if (modifier != null) {
             builder.field("modifier", modifier.toString().toLowerCase(Locale.ROOT));
         }

+ 6 - 2
src/main/java/org/elasticsearch/index/query/functionscore/fieldvaluefactor/FieldValueFactorFunctionParser.java

@@ -41,7 +41,8 @@ import java.util.Locale;
  *         "field_value_factor": {
  *             "field": "myfield",
  *             "factor": 1.5,
- *             "modifier": "square"
+ *             "modifier": "square",
+ *             "missing": 1
  *         }
  *     }
  * </pre>
@@ -56,6 +57,7 @@ public class FieldValueFactorFunctionParser implements ScoreFunctionParser {
         String field = null;
         float boostFactor = 1;
         FieldValueFactorFunction.Modifier modifier = FieldValueFactorFunction.Modifier.NONE;
+        Double missing = null;
         XContentParser.Token token;
         while ((token = parser.nextToken()) != XContentParser.Token.END_OBJECT) {
             if (token == XContentParser.Token.FIELD_NAME) {
@@ -67,6 +69,8 @@ public class FieldValueFactorFunctionParser implements ScoreFunctionParser {
                     boostFactor = parser.floatValue();
                 } else if ("modifier".equals(currentFieldName)) {
                     modifier = FieldValueFactorFunction.Modifier.valueOf(parser.text().toUpperCase(Locale.ROOT));
+                } else if ("missing".equals(currentFieldName)) {
+                    missing = parser.doubleValue();
                 } else {
                     throw new QueryParsingException(parseContext.index(), NAMES[0] + " query does not support [" + currentFieldName + "]");
                 }
@@ -84,7 +88,7 @@ public class FieldValueFactorFunctionParser implements ScoreFunctionParser {
         if (mapper == null) {
             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));
     }
 

+ 8 - 0
src/test/java/org/elasticsearch/search/functionscore/FunctionScoreFieldValueTests.java

@@ -88,6 +88,14 @@ public class FunctionScoreFieldValueTests extends ElasticsearchIntegrationTest {
             // 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.
         try {
             response = client().prepareSearch("test")