Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fielddata accessors (.value/.values/.distance()/etc) #18169

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -38,7 +38,7 @@ public 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
Expand Down
9 changes: 4 additions & 5 deletions docs/reference/modules/scripting/painless.asciidoc
Expand Up @@ -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]
----------------------------------------------------------------
Expand All @@ -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"
}
}
}
Expand Down Expand Up @@ -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": {
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -119,9 +120,22 @@ public static void fieldStore(final Object owner, Object value, final String nam

@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;

Expand All @@ -143,21 +157,21 @@ public static Object fieldLoad(final Object owner, final String name, final Defi
}
} else {
throw new IllegalArgumentException("Unable to find dynamic field [" + name + "] " +
"for class [" + owner.getClass().getCanonicalName() + "].");
"for class [" + clazz.getCanonicalName() + "].");
}
} else {
handle = field.getter;
}

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);
}
}
}
Expand Down
Expand Up @@ -33,6 +33,9 @@
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 ),
Expand Down Expand Up @@ -393,6 +396,14 @@ private Transform(final Cast cast, Method method, final Type upcast, final Type
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<>();
Expand Down Expand Up @@ -471,6 +482,12 @@ public 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();
Expand Down Expand Up @@ -564,6 +581,12 @@ public 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() {
Expand Down Expand Up @@ -634,6 +657,12 @@ private void addDefaultStructs() {
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() {
Expand Down Expand Up @@ -670,6 +699,12 @@ private void addDefaultClasses() {
addClass("HashMap");

addClass("Exception");

addClass("GeoPoint");
addClass("Strings");
addClass("Longs");
addClass("Doubles");
addClass("GeoPoints");
}

private void addDefaultElements() {
Expand Down Expand Up @@ -1032,6 +1067,61 @@ private void addDefaultElements() {
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() {
Expand Down Expand Up @@ -1079,6 +1169,12 @@ private void copyDefaultStructs() {
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() {
Expand Down
Expand Up @@ -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"
Expand Down
Expand Up @@ -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:
Expand All @@ -51,15 +51,15 @@
query:
script:
script:
inline: "input.doc['num1'].0 > input.param1;"
inline: "input.doc['num1'].value > input.param1;"
lang: painless
params:
param1: 1

script_fields:
sNum1:
script:
inline: "return input.doc['num1'].0;"
inline: "return input.doc['num1'].value;"
lang: painless
sort:
num1:
Expand All @@ -76,15 +76,15 @@
query:
script:
script:
inline: "input.doc['num1'].0 > input.param1;"
inline: "input.doc['num1'].value > input.param1;"
lang: painless
params:
param1: -1

script_fields:
sNum1:
script:
inline: "input.doc['num1'].0;"
inline: "input.doc['num1'].value;"
lang: painless
sort:
num1:
Expand Down