diff --git a/docs/changelog/64422.yaml b/docs/changelog/64422.yaml new file mode 100644 index 0000000000000..c8e53d5a42637 --- /dev/null +++ b/docs/changelog/64422.yaml @@ -0,0 +1,6 @@ +pr: 64422 +summary: Add `scripted_metric` agg context to `unsigned_long` +area: Search +type: enhancement +issues: + - 64347 diff --git a/docs/reference/mapping/types/unsigned_long.asciidoc b/docs/reference/mapping/types/unsigned_long.asciidoc index 94029101ff2ff..3fb77961bf049 100644 --- a/docs/reference/mapping/types/unsigned_long.asciidoc +++ b/docs/reference/mapping/types/unsigned_long.asciidoc @@ -71,7 +71,7 @@ GET /my_index/_search "query": { "range" : { "my_counter" : { - "gte" : "9223372036854775808.5", + "gte" : "9223372036854775808", "lte" : "18446744073709551615" } } @@ -80,7 +80,7 @@ GET /my_index/_search -------------------------------- //TEST[continued] - +==== Sort values For queries with sort on an `unsigned_long` field, for a particular document {es} returns a sort value of the type `long` if the value of this document is within the range of long values, @@ -102,9 +102,6 @@ GET /my_index/_search //TEST[continued] -==== Unsigned long in scripts -Currently unsigned_long is not supported in scripts. - ==== Stored fields A stored field of `unsigned_long` is stored and returned as `String`. @@ -113,6 +110,67 @@ For `terms` aggregations, similarly to sort values, `Long` or `BigInteger` values are used. For other aggregations, values are converted to the `double` type. +==== Script values +By default, script values of an `unsigned_long` field are returned as +Java signed `Long`, which means that values that are greater than +`Long.MAX_VALUE` are shown as negative values. You can use +`Long.compareUnsigned(long, long)`, `Long.divideUnsigned(long, long)` +and `Long.remainderUnsigned(long, long)` to correctly work with +these values. + +For example, the script below returns a value of the counter +divided by 10. + +[source,console] +-------------------------------- +GET /my_index/_search +{ + "query": { + "match_all" : {} + }, + "script_fields": { + "count10" : { + "script": { + "source": "Long.divideUnsigned(doc['my_counter'].value, 10)" + } + } + } +} +-------------------------------- +//TEST[continued] + + +Alternatively, you can treat the unsigned long type as `BigInteger` +in your scripts by using the field API. For example, this script +treats `my_counter` as `BigInteger` with a default value of `BigInteger.ZERO`: + +[source,js] +-------------------------------------------------- +"script": { + "source": "field('my_counter').asBigInteger(BigInteger.ZERO)" +} +-------------------------------------------------- +// NOTCONSOLE + +For scripts that need to return float or double values, you +can further convert `BigInteger` values to double or float: + +[source,console] +-------------------------------- +GET /my_index/_search +{ + "query": { + "script_score": { + "query": {"match_all": {}}, + "script": { + "source": "field('my_counter').asBigInteger(BigInteger.ZERO).floatValue()" + } + } + } +} +-------------------------------- +//TEST[continued] + ==== Queries with mixed numeric types Searches with mixed numeric types one of which is `unsigned_long` are diff --git a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/DocValuesWhitelistExtension.java b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/DocValuesWhitelistExtension.java index f61380872744c..31c5a9b6290be 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/DocValuesWhitelistExtension.java +++ b/x-pack/plugin/mapper-unsigned-long/src/main/java/org/elasticsearch/xpack/unsignedlong/DocValuesWhitelistExtension.java @@ -17,12 +17,14 @@ import org.elasticsearch.script.NumberSortScript; import org.elasticsearch.script.ScoreScript; import org.elasticsearch.script.ScriptContext; +import org.elasticsearch.script.ScriptedMetricAggContexts; import org.elasticsearch.script.StringSortScript; import java.util.List; import java.util.Map; import static java.util.Collections.singletonList; +import static java.util.Map.entry; public class DocValuesWhitelistExtension implements PainlessExtension { @@ -34,21 +36,18 @@ public class DocValuesWhitelistExtension implements PainlessExtension { @Override public Map, List> getContextWhitelists() { List whitelist = singletonList(WHITELIST); - return Map.of( - FieldScript.CONTEXT, - whitelist, - ScoreScript.CONTEXT, - whitelist, - FilterScript.CONTEXT, - whitelist, - AggregationScript.CONTEXT, - whitelist, - NumberSortScript.CONTEXT, - whitelist, - StringSortScript.CONTEXT, - whitelist, - BucketAggregationSelectorScript.CONTEXT, - whitelist + return Map.ofEntries( + entry(FieldScript.CONTEXT, whitelist), + entry(ScoreScript.CONTEXT, whitelist), + entry(FilterScript.CONTEXT, whitelist), + entry(AggregationScript.CONTEXT, whitelist), + entry(NumberSortScript.CONTEXT, whitelist), + entry(StringSortScript.CONTEXT, whitelist), + entry(BucketAggregationSelectorScript.CONTEXT, whitelist), + entry(ScriptedMetricAggContexts.InitScript.CONTEXT, whitelist), + entry(ScriptedMetricAggContexts.MapScript.CONTEXT, whitelist), + entry(ScriptedMetricAggContexts.CombineScript.CONTEXT, whitelist), + entry(ScriptedMetricAggContexts.ReduceScript.CONTEXT, whitelist) ); } } diff --git a/x-pack/plugin/mapper-unsigned-long/src/yamlRestTest/resources/rest-api-spec/test/50_script_values.yml b/x-pack/plugin/mapper-unsigned-long/src/yamlRestTest/resources/rest-api-spec/test/50_script_values.yml index fdc016a7fda52..749b3c4cf9fdf 100644 --- a/x-pack/plugin/mapper-unsigned-long/src/yamlRestTest/resources/rest-api-spec/test/50_script_values.yml +++ b/x-pack/plugin/mapper-unsigned-long/src/yamlRestTest/resources/rest-api-spec/test/50_script_values.yml @@ -146,6 +146,8 @@ setup: script: source: "field('ul').isEmpty() == false" - match: { hits.total.value: 5 } + + - do: search: index: test1 @@ -185,6 +187,7 @@ setup: source: "field('ul').asBigInteger(BigInteger.valueOf(Long.parseUnsignedLong('18446744073709551614'))).doubleValue()" - match: { hits.total.value: 5 } + - do: search: index: test1 @@ -196,3 +199,36 @@ setup: source: "Math.abs(doc['ul'].value)" - match: { hits.total.value: 5 } + +--- +"Scripted Metric": + - skip: + version: " - 8.0.99" + reason: "context for scripted metric was added in 8.1" + + - do: + search: + index: test1 + body: + query: + term: + ul: 18446744073709551615 + aggs: + sqrt: + scripted_metric: + init_script: | + state.sqrd = [] + map_script: | + double v = field('ul').asBigInteger(BigInteger.ZERO).doubleValue(); + state.sqrd.add(v * v) + combine_script: | + state.sqrd + reduce_script: | + def sum = 0.0; + for (s in states) { + for (v in s) { + sum += v; + } + } + return sum + - match: { aggregations.sqrt.value: 3.4028236692093846E38 }