Painless: add fielddata accessors (.value/.values/.distance()/etc)
This gives better coverage and consistency with the scripting APIs, by whitelisting the primary search scripting API classes and using them instead of only Map and List methods. For example, accessing fields can now be done with `.value` instead of `.0` because `getValue()` is whitelisted. For now, access to a document's fields in this way (loads) are fast-pathed in the code, to avoid dynamic overhead. Access to geo fields and geo distance functions is now supported. TODO: date support (e.g. whitelist ReadableDateTime methods as a start) TODO: improve docs (like expressions and groovy have for document's fields) TODO: remove fast-path hack Closes #18169 Squashed commit of the following: commit ec9f24b2424891a7429bb4c0a03f9868cba0a213 Author: Robert Muir <rmuir@apache.org> Date: Thu May 5 17:59:37 2016 -0400 cutover to <Def> instead of <Object> here commit 9edb1550438acd209733bc36f0d2e0aecf190ecb Author: Robert Muir <rmuir@apache.org> Date: Thu May 5 17:03:02 2016 -0400 add fast-path for docvalues field loads commit f8e38c0932fccc0cfa217516130ad61522e59fe5 Author: Robert Muir <rmuir@apache.org> Date: Thu May 5 16:47:31 2016 -0400 Painless: add fielddata accessors (.value/.values/.distance()/etc)
This commit is contained in:
parent
c4090a1841
commit
e3ce6c9048
|
@ -38,7 +38,7 @@ public final class GeoPoint {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a new Geopointform a string. This String must either be a geohash
|
||||
* Create a new Geopoint from a string. This String must either be a geohash
|
||||
* or a lat-lon tuple.
|
||||
*
|
||||
* @param value String to create the point from
|
||||
|
|
|
@ -115,9 +115,8 @@ GET hockey/_search
|
|||
----------------------------------------------------------------
|
||||
// AUTOSENSE
|
||||
|
||||
You must always specify the index of the field value you want, even if there's only a single item in the field.
|
||||
All fields in Elasticsearch are multi-valued and Painless does not provide a `.value` shortcut. The following example uses a Painless script to sort the players by their combined first and last names. The names are accessed using
|
||||
`input.doc['first'].0` and `input.doc['last'].0`.
|
||||
The following example uses a Painless script to sort the players by their combined first and last names. The names are accessed using
|
||||
`input.doc['first'].value` and `input.doc['last'].value`.
|
||||
|
||||
[source,js]
|
||||
----------------------------------------------------------------
|
||||
|
@ -132,7 +131,7 @@ GET hockey/_search
|
|||
"order": "asc",
|
||||
"script": {
|
||||
"lang": "painless",
|
||||
"inline": "input.doc['first'].0 + ' ' + input.doc['last'].0"
|
||||
"inline": "input.doc['first'].value + ' ' + input.doc['last'].value"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +217,7 @@ GET hockey/_search
|
|||
"full_name_dynamic": {
|
||||
"script": {
|
||||
"lang": "painless",
|
||||
"inline": "def first = input.doc['first'].0; def last = input.doc['last'].0; return first + ' ' + last;"
|
||||
"inline": "def first = input.doc['first'].value; def last = input.doc['last'].value; return first + ' ' + last;"
|
||||
}
|
||||
},
|
||||
"full_name_static": {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package org.elasticsearch.painless;
|
||||
|
||||
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
||||
import org.elasticsearch.painless.Definition.Cast;
|
||||
import org.elasticsearch.painless.Definition.Field;
|
||||
import org.elasticsearch.painless.Definition.Method;
|
||||
|
@ -119,9 +120,22 @@ public class Def {
|
|||
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static Object fieldLoad(final Object owner, final String name, final Definition definition) {
|
||||
if (owner.getClass().isArray() && "length".equals(name)) {
|
||||
final Class<?> clazz = owner.getClass();
|
||||
if (clazz.isArray() && "length".equals(name)) {
|
||||
return Array.getLength(owner);
|
||||
} else {
|
||||
// TODO: remove this fast-path, once we speed up dynamics some more
|
||||
if ("value".equals(name) && owner instanceof ScriptDocValues) {
|
||||
if (clazz == ScriptDocValues.Doubles.class) {
|
||||
return ((ScriptDocValues.Doubles)owner).getValue();
|
||||
} else if (clazz == ScriptDocValues.Longs.class) {
|
||||
return ((ScriptDocValues.Longs)owner).getValue();
|
||||
} else if (clazz == ScriptDocValues.Strings.class) {
|
||||
return ((ScriptDocValues.Strings)owner).getValue();
|
||||
} else if (clazz == ScriptDocValues.GeoPoints.class) {
|
||||
return ((ScriptDocValues.GeoPoints)owner).getValue();
|
||||
}
|
||||
}
|
||||
final Field field = getField(owner, name, definition);
|
||||
MethodHandle handle;
|
||||
|
||||
|
@ -143,7 +157,7 @@ public class Def {
|
|||
}
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unable to find dynamic field [" + name + "] " +
|
||||
"for class [" + owner.getClass().getCanonicalName() + "].");
|
||||
"for class [" + clazz.getCanonicalName() + "].");
|
||||
}
|
||||
} else {
|
||||
handle = field.getter;
|
||||
|
@ -151,13 +165,13 @@ public class Def {
|
|||
|
||||
if (handle == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Unable to read from field [" + name + "] with owner class [" + owner.getClass() + "].");
|
||||
"Unable to read from field [" + name + "] with owner class [" + clazz + "].");
|
||||
} else {
|
||||
try {
|
||||
return handle.invoke(owner);
|
||||
} catch (final Throwable throwable) {
|
||||
throw new IllegalArgumentException("Error loading value from " +
|
||||
"field [" + name + "] with owner class [" + owner.getClass() + "].", throwable);
|
||||
"field [" + name + "] with owner class [" + clazz + "].", throwable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,9 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.elasticsearch.common.geo.GeoPoint;
|
||||
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
||||
|
||||
class Definition {
|
||||
enum Sort {
|
||||
VOID( void.class , 0 , true , false , false , false ),
|
||||
|
@ -393,6 +396,14 @@ class Definition {
|
|||
final Type iargexcepType;
|
||||
final Type istateexceptType;
|
||||
final Type nfexcepType;
|
||||
|
||||
// docvalues accessors
|
||||
final Type geoPointType;
|
||||
final Type stringsType;
|
||||
// TODO: add ReadableDateTime? or don't expose the joda stuff?
|
||||
final Type longsType;
|
||||
final Type doublesType;
|
||||
final Type geoPointsType;
|
||||
|
||||
public Definition() {
|
||||
structs = new HashMap<>();
|
||||
|
@ -471,6 +482,12 @@ class Definition {
|
|||
istateexceptType = getType("IllegalStateException");
|
||||
nfexcepType = getType("NumberFormatException");
|
||||
|
||||
geoPointType = getType("GeoPoint");
|
||||
stringsType = getType("Strings");
|
||||
longsType = getType("Longs");
|
||||
doublesType = getType("Doubles");
|
||||
geoPointsType = getType("GeoPoints");
|
||||
|
||||
addDefaultElements();
|
||||
copyDefaultStructs();
|
||||
addDefaultTransforms();
|
||||
|
@ -564,6 +581,12 @@ class Definition {
|
|||
iargexcepType = definition.iargexcepType;
|
||||
istateexceptType = definition.istateexceptType;
|
||||
nfexcepType = definition.nfexcepType;
|
||||
|
||||
geoPointType = definition.geoPointType;
|
||||
stringsType = definition.stringsType;
|
||||
longsType = definition.longsType;
|
||||
doublesType = definition.doublesType;
|
||||
geoPointsType = definition.geoPointsType;
|
||||
}
|
||||
|
||||
private void addDefaultStructs() {
|
||||
|
@ -634,6 +657,12 @@ class Definition {
|
|||
addStruct( "IllegalArgumentException" , IllegalArgumentException.class);
|
||||
addStruct( "IllegalStateException" , IllegalStateException.class);
|
||||
addStruct( "NumberFormatException" , NumberFormatException.class);
|
||||
|
||||
addStruct( "GeoPoint" , GeoPoint.class);
|
||||
addStruct( "Strings" , ScriptDocValues.Strings.class);
|
||||
addStruct( "Longs" , ScriptDocValues.Longs.class);
|
||||
addStruct( "Doubles" , ScriptDocValues.Doubles.class);
|
||||
addStruct( "GeoPoints" , ScriptDocValues.GeoPoints.class);
|
||||
}
|
||||
|
||||
private void addDefaultClasses() {
|
||||
|
@ -670,6 +699,12 @@ class Definition {
|
|||
addClass("HashMap");
|
||||
|
||||
addClass("Exception");
|
||||
|
||||
addClass("GeoPoint");
|
||||
addClass("Strings");
|
||||
addClass("Longs");
|
||||
addClass("Doubles");
|
||||
addClass("GeoPoints");
|
||||
}
|
||||
|
||||
private void addDefaultElements() {
|
||||
|
@ -1032,6 +1067,61 @@ class Definition {
|
|||
addConstructor("IllegalStateException", "new", new Type[] {stringType}, null);
|
||||
|
||||
addConstructor("NumberFormatException", "new", new Type[] {stringType}, null);
|
||||
|
||||
addMethod("GeoPoint", "getLat", null, false, doubleType, new Type[] {}, null, null);
|
||||
addMethod("GeoPoint", "getLon", null, false, doubleType, new Type[] {}, null, null);
|
||||
addMethod("Strings", "getValue", null, false, stringType, new Type[] {}, null, null);
|
||||
addMethod("Strings", "getValues", null, false, slistType, new Type[] {}, null, null);
|
||||
addMethod("Longs", "getValue", null, false, longType, new Type[] {}, null, null);
|
||||
addMethod("Longs", "getValues", null, false, olistType, new Type[] {}, null, null);
|
||||
// TODO: add better date support for Longs here? (carefully?)
|
||||
addMethod("Doubles", "getValue", null, false, doubleType, new Type[] {}, null, null);
|
||||
addMethod("Doubles", "getValues", null, false, olistType, new Type[] {}, null, null);
|
||||
addMethod("GeoPoints", "getValue", null, false, geoPointType, new Type[] {}, null, null);
|
||||
addMethod("GeoPoints", "getValues", null, false, olistType, new Type[] {}, null, null);
|
||||
addMethod("GeoPoints", "getLat", null, false, doubleType, new Type[] {}, null, null);
|
||||
addMethod("GeoPoints", "getLon", null, false, doubleType, new Type[] {}, null, null);
|
||||
addMethod("GeoPoints", "getLats", null, false, getType(doubleType.struct, 1), new Type[] {}, null, null);
|
||||
addMethod("GeoPoints", "getLons", null, false, getType(doubleType.struct, 1), new Type[] {}, null, null);
|
||||
// geo distance functions... so many...
|
||||
addMethod("GeoPoints", "factorDistance", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "factorDistanceWithDefault", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "factorDistance02", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "factorDistance13", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "arcDistance", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "arcDistanceWithDefault", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "arcDistanceInKm", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "arcDistanceInKmWithDefault", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "arcDistanceInMiles", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "arcDistanceInMilesWithDefault", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "distance", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "distanceWithDefault", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "distanceInKm", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "distanceInKmWithDefault", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "distanceInMiles", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "distanceInMilesWithDefault", null, false, doubleType,
|
||||
new Type[] { doubleType, doubleType, doubleType }, null, null);
|
||||
addMethod("GeoPoints", "geohashDistance", null, false, doubleType,
|
||||
new Type[] { stringType }, null, null);
|
||||
addMethod("GeoPoints", "geohashDistanceInKm", null, false, doubleType,
|
||||
new Type[] { stringType }, null, null);
|
||||
addMethod("GeoPoints", "geohashDistanceInMiles", null, false, doubleType,
|
||||
new Type[] { stringType }, null, null);
|
||||
}
|
||||
|
||||
private void copyDefaultStructs() {
|
||||
|
@ -1079,6 +1169,12 @@ class Definition {
|
|||
copyStruct("IllegalArgumentException", "Exception", "Object");
|
||||
copyStruct("IllegalStateException", "Exception", "Object");
|
||||
copyStruct("NumberFormatException", "Exception", "Object");
|
||||
|
||||
copyStruct("GeoPoint", "Object");
|
||||
copyStruct("Strings", "List<String>", "Collection<String>", "Object");
|
||||
copyStruct("Longs", "List", "Collection", "Object");
|
||||
copyStruct("Doubles", "List", "Collection", "Object");
|
||||
copyStruct("GeoPoints", "List", "Collection", "Object");
|
||||
}
|
||||
|
||||
private void addDefaultTransforms() {
|
||||
|
|
|
@ -28,7 +28,7 @@ setup:
|
|||
script_fields:
|
||||
bar:
|
||||
script:
|
||||
inline: "input.doc['foo'].0 + input.x;"
|
||||
inline: "input.doc['foo'].value + input.x;"
|
||||
lang: painless
|
||||
params:
|
||||
x: "bbb"
|
||||
|
|
|
@ -29,12 +29,12 @@
|
|||
query:
|
||||
script:
|
||||
script:
|
||||
inline: "input.doc['num1'].0 > 1;"
|
||||
inline: "input.doc['num1'].value > 1;"
|
||||
lang: painless
|
||||
script_fields:
|
||||
sNum1:
|
||||
script:
|
||||
inline: "input.doc['num1'].0;"
|
||||
inline: "input.doc['num1'].value;"
|
||||
lang: painless
|
||||
sort:
|
||||
num1:
|
||||
|
@ -51,7 +51,7 @@
|
|||
query:
|
||||
script:
|
||||
script:
|
||||
inline: "input.doc['num1'].0 > input.param1;"
|
||||
inline: "input.doc['num1'].value > input.param1;"
|
||||
lang: painless
|
||||
params:
|
||||
param1: 1
|
||||
|
@ -59,7 +59,7 @@
|
|||
script_fields:
|
||||
sNum1:
|
||||
script:
|
||||
inline: "return input.doc['num1'].0;"
|
||||
inline: "return input.doc['num1'].value;"
|
||||
lang: painless
|
||||
sort:
|
||||
num1:
|
||||
|
@ -76,7 +76,7 @@
|
|||
query:
|
||||
script:
|
||||
script:
|
||||
inline: "input.doc['num1'].0 > input.param1;"
|
||||
inline: "input.doc['num1'].value > input.param1;"
|
||||
lang: painless
|
||||
params:
|
||||
param1: -1
|
||||
|
@ -84,7 +84,7 @@
|
|||
script_fields:
|
||||
sNum1:
|
||||
script:
|
||||
inline: "input.doc['num1'].0;"
|
||||
inline: "input.doc['num1'].value;"
|
||||
lang: painless
|
||||
sort:
|
||||
num1:
|
||||
|
|
Loading…
Reference in New Issue