Browse Source

[DOCS] Add documentation for Painless field API (#83388)

* [DOCS] Add documentation for Painless field API

* Moving content to a new page and incorporating reviewer feedback

* Clarify note

* Incorporating review comments

* Remove Object so as not to confuse it with the object type

* Add section and table for supported mapped field types

* Update table based on review feedback
Adam Locke 3 years ago
parent
commit
d2e7b4caf5

+ 2 - 0
docs/reference/scripting.asciidoc

@@ -53,6 +53,8 @@ include::scripting/painless.asciidoc[]
 
 include::scripting/using.asciidoc[]
 
+include::scripting/access-fields.asciidoc[]
+
 include::scripting/common-script-uses.asciidoc[]
 
 include::scripting/fields.asciidoc[]

+ 127 - 0
docs/reference/scripting/access-fields.asciidoc

@@ -0,0 +1,127 @@
+[[script-fields-api]]
+== Access fields in a document with the `field` API
+++++
+<titleabbrev>Access fields in a document</titleabbrev>
+++++
+
+beta::["The `field` API is still in development and should be considered a beta feature. The API is subject to change and this iteration is likely not the final state. For feature status, refer to {es-issue}78920[#78920]."]
+
+Use the `field` API to access document fields:
+
+[source,painless]
+----
+field('my_field').get(<default_value>)
+----
+
+This API fundamentally changes how you access documents in Painless. Previously,
+you had to access the `doc` map with the field name that you wanted to access:
+
+[source,painless]
+----
+doc['my_field'].value
+----
+
+Accessing document fields this way didn't handle missing values or missing
+mappings, which meant that to write robust Painless scripts, you needed to
+include logic to check that both fields and values exist.
+
+Instead, use the `field` API, which is the preferred approach to access
+documents in Painless. The `field` API handles missing values, and will evolve
+to abstract access to `_source` and `doc_values`.
+
+NOTE: Some fields aren't yet compatible with the `fields` API, such as `text` or
+`geo` fields. Continue using `doc` to access field types that the `field` API
+doesn't support.
+
+The `field` API returns a `Field` object that iterates over fields with 
+multiple values, providing access to the underlying value through the
+`get(<default_value>)` method, as well as type conversion and helper methods. 
+
+The `field` API returns the default value that you specify, regardless of
+whether the field exists or has any values for the current document.
+This means that the `field` API can handle missing values without requiring 
+additional logic. For a reference type such as `keyword`, the default 
+value can be `null`. For a primitive type such as `boolean` or `long`, the
+default value must be a matching primitive type, such as `false` or `1`. 
+
+[discrete]
+=== Convenient, simpler access
+Instead of explicitly calling the `field` API with the `get()` method, you can
+include the `$` shortcut. Just include the `$` symbol, field name, and a default
+value, in case the field doesn't have a value:
+
+[source,painless]
+----
+$(‘field’, <default_value>)
+----
+
+With these enhanced capabilities and simplified syntax, you can write scripts
+that are shorter, less complex, and easier to read. For example, the following
+script uses the outdated syntax to determine the difference in milliseconds
+between two complex `datetime` values from an indexed document:
+
+[source,painless]
+----
+if (doc.containsKey('start') && doc.containsKey('end')) {
+   if (doc['start'].size() > 0 && doc['end'].size() > 0) {
+       ZonedDateTime start = doc['start'].value;
+       ZonedDateTime end = doc['end'].value;
+       return ChronoUnit.MILLIS.between(start, end);
+   } else {
+       return -1;
+   }
+} else {
+   return -1;
+}
+----
+
+Using the `field` API, you can write this same script much more succinctly,
+without requiring additional logic to determine whether fields exist before
+operating on them:
+
+[source,painless]
+----
+ZonedDateTime start = field('start').get(null);
+ZonedDateTime end = field('end').get(null);
+return start == null || end == null ? -1 : ChronoUnit.MILLIS.between(start, end)
+----
+
+[discrete]
+=== Supported mapped field types
+The following table indicates the mapped field types that the `field` API
+supports. For each supported type, values are listed that are returned by the
+`field` API (from the `get` and `as<Type>` methods) and the `doc` map (from the
+`getValue` and `get` methods). 
+
+NOTE: The `fields` API currently doesn't support some fields, but you can still
+access those fields through the `doc` map. For the most current list of
+supported fields, refer to {es-issue}79105[#79105].
+
+[cols="1,1,1,1,1",options="header",]
+|========
+|Mapped field type
+2+|Returned type from `field`
+2+|Returned type from `doc`
+h|              h|`get`      h|`as<Type>`    h|`getValue` h|`get`
+ |`binary`        |`ByteBuffer` |-             |`BytesRef`  |`BytesRef`
+ |`boolean`       |`boolean`    |-             |`boolean`   |`Boolean`
+ |`keyword`       |`String`     |-             |`String`    |`String`
+ |`long`          |`long`       |-             |`long`      |`Long`
+ |`integer`       |`int`        |-             |`long`      |`Long`
+ |`short`         |`short`      |-             |`long`      |`Long`
+ |`byte`          |`byte`       |-             |`long`      |`Long`
+ |`double`        |`double`     |-             |`double`    |`Double`
+ |`scaled_float`  |`double`     |-             |`double`    |`Double`
+ |`half_float`    |`float`      |-             |`double`    |`Double`
+ |`unsigned_long` |`long`       |`BigInteger`  |`long`      |`Long`
+ |`date`          |`ZonedDateTime` |-          |`ZonedDateTime` |`ZonedDateTime`
+ |`date_nanos`    |`ZonedDateTime` |-          |`ZonedDateTime` |`ZonedDateTime`
+ |`ip`            |`IpAddress`     |`String`  |`String`    |`String`
+ |`_version`      |`long`       |-             |`long`      |`Long`
+ |`_seq_no`       |`long`       |-             |`long`      |`Long`
+ |`version`       |`Version`    |`String`      |`String`    |`String`
+ |`murmur3`       |`long`       |-             |`long`      |`Long`
+ |`constant_keyword` |`String`  |-             |`String`    |`String`
+ |`wildcard`      |`String`     |-             |`String`    |`String`
+ |`flattened`     |`String`     |-             |`String`    |`String`
+|========

+ 2 - 2
docs/reference/scripting/using.asciidoc

@@ -147,7 +147,7 @@ GET my-index-000001/_search
     "my_doubled_field": {
       "script": {
         "lang":   "painless",
-        "source": "return doc['my_field'].value * params.get('multiplier');",
+        "source": "doc['my_field'].value * params.get('multiplier');",
         "params": {
           "multiplier": 2
         }
@@ -168,7 +168,7 @@ GET my-index-000001/_search
   "script_fields": {
     "my_doubled_field": {
       "script": {
-        "source": "doc['my_field'].value * params['multiplier']",
+        "source": "field('my_field').get(null) * params['multiplier']",
         "params": {
           "multiplier": 2
         }