Implement reading from null safe dereferences

Null safe dereferences make handling null or missing values shorter.
Compare without:
```
if (ctx._source.missing != null && ctx._source.missing.foo != null) {
  ctx._source.foo_length = ctx.source.missing.foo.length()
}
```

To with:
```
Integer length = ctx._source.missing?.foo?.length();
if (length != null) {
  ctx._source.foo_length = length
}
```

Combining this with the as of yet unimplemented elvis operator allows
for very concise defaults for nulls:
```
ctx._source.foo_length = ctx._source.missing?.foo?.length() ?: 0;
```

Since you have to start somewhere, we started with null safe dereferenes.

Anyway, this is a feature borrowed from groovy. Groovy allows writing to
null values like:
```
def v = null
v?.field = 'cat'
```
And the writes are simply ignored. Painless doesn't support this at this
point because it'd be complex to implement and maybe not all that useful.

There is no runtime cost for this feature if it is not used. When it is
used we implement it fairly efficiently, adding a jump rather than a
temporary variable.

This should also work fairly well with doc values.
This commit is contained in:
Nik Everett 2016-10-30 12:25:51 -04:00
parent b743ab0b07
commit d03b8e4abb
13 changed files with 699 additions and 295 deletions

View file

@ -130,6 +130,37 @@ using these characters:
|`x` | COMMENTS (aka extended) | `'a' ==~ /a #comment/x`
|=======================================================================
[float]
[[painless-deref]]
=== Dereferences
Like lots of languages, Painless uses `.` to reference fields and call methods:
[source,painless]
---------------------------------------------------------
String foo = 'foo';
TypeWithGetterOrPublicField bar = new TypeWithGetterOrPublicField()
return foo.length() + bar.x
---------------------------------------------------------
Like Groovy, Painless uses `?.` to perform null-safe references, with the
result being `null` if the left hand side is null:
[source,painless]
---------------------------------------------------------
String foo = null;
return foo?.length() // Returns null
---------------------------------------------------------
Unlike Groovy, Painless doesn't support writing to null values with this
operator:
[source,painless]
---------------------------------------------------------
TypeWithSetterOrPublicField foo = null;
foo?.x = 'bar' // Compile error
---------------------------------------------------------
[float]
[[painless-operators]]
=== Operators