ClassCastException when querying nested properties

I'm trying to query some properties in a nested object with a script sort
and I'm running into some strange behaviour. I managed to contrive a small
example that reproduces this.

First, create a new index and apply the attached mapping:

curl -X PUT http://localhost:9200/widgets
curl -X PUT http://localhost:9200/widgets/widget/_mapping -d @widget.json

Then add some objects to the index:

curl -X PUT http://localhost:9200/widgets/widget/1 -d
'{"id":1,"type":"pegasus","components":[{"id":1,"type":"honda
civic"},{"id":2,"type":"mazda rx-8"}]}'
curl -X PUT http://localhost:9200/widgets/widget/2 -d
'{"id":2,"type":"pegasus","components":[{"id":1,"type":"honda
accord"},{"id":2,"type":""}]}'
curl -X PUT http://localhost:9200/widgets/widget/3 -d
'{"id":3,"type":"unicorn","components":[{"id":3,"type":"toyota supra"}]}'
curl -X PUT http://localhost:9200/widgets/widget/4 -d
'{"id":4,"type":"unicorn","components":[]}'

I'm trying to find all widgets that have a component with "id" 1 and I want
to sort by the "type" value. Anything without that value should get sorted
to the bottom (I'm using 'Z' for that below, though in practice I use
character '\uffe0'). I use the following script:

ids = doc['components.id'];
if (ids.empty || ids.values.length == 0)
return 'Z';

types = doc['components.type'];
if (!ids.multiValued)
return types.value;

for (i = 0; i < ids.values.length; i++)
if (ids.values[i] == 2)
return type.values[i];

return 'Z';

I'm running it with curl like so:

curl -X GET http://localhost:9200/widgets/widget/_search?pretty -d
'{"sort":{"_script":{"script":"ids=doc['''components.id'''];if(ids.empty||ids.values.length==0)return
'''Z''';type=doc['''components.type'''];if(!ids.multiValued)return
type.value;for(i=0;i<ids.values.length;i++)if(ids.values[i]==2)return
type.values[i];return '''Z''';","type":"string","order":"asc"}}}'

When I run this, some of the shards fail with a ClassCastException (full
stack trace attached):

Caused by: java.lang.ClassCastException: [J cannot be cast to [Ljava.lang.
Object;
at org.elasticsearch.common.mvel2.optimizers.impl.refl.nodes.
ArrayAccessorNest.getValue(ArrayAccessorNest.java:53)
at org.elasticsearch.common.mvel2.optimizers.impl.refl.nodes.
GetterAccessor.getValue(GetterAccessor.java:40)
... 31 more

Any ideas about what I may be doing wrong or how I can fix this? Thanks.

Kamil

--

My guess is that somewhere along the way through to the MVEL compiler
elasticsearch is sending a long along. ArrayAccessorNest.getValue()
attempts to convert this into a Object and this is where it fails. The
following sample Java program illustrates the same issue:

public class LongArrayToObjectArray {
public static void main(String args) {
Object O = (Object) (Object) new long[0];
}
}

Compile and run it and you'll see:

$ java LongArrayToObjectArray
Exception in thread "main" java.lang.ClassCastException: [J cannot be cast
to [Ljava.lang.Object;
at LongArrayToObjectArray.main(LongArrayToObjectArray.java:5)

I'm not sure where the fix should live since I don't know where the long
comes from, but presumably the calling code should pass Long or the MVEL
compiler should be checking if the object is an array of primitive types,
e.g.

if (obj instanceof long) {
// ....
} else if (obj instanceof int) {
// ...
} else if (...) {
// ....
} else if (obj instanceof Object) {
// ....
}

Kamil

On Wednesday, September 26, 2012 12:03:42 PM UTC-7, Kamil wrote:

I'm trying to query some properties in a nested object with a script sort
and I'm running into some strange behaviour. I managed to contrive a small
example that reproduces this.

First, create a new index and apply the attached mapping:

curl -X PUT http://localhost:9200/widgets
curl -X PUT http://localhost:9200/widgets/widget/_mapping -d @widget.json

Then add some objects to the index:

curl -X PUT http://localhost:9200/widgets/widget/1 -d
'{"id":1,"type":"pegasus","components":[{"id":1,"type":"honda
civic"},{"id":2,"type":"mazda rx-8"}]}'
curl -X PUT http://localhost:9200/widgets/widget/2 -d
'{"id":2,"type":"pegasus","components":[{"id":1,"type":"honda
accord"},{"id":2,"type":""}]}'
curl -X PUT http://localhost:9200/widgets/widget/3 -d
'{"id":3,"type":"unicorn","components":[{"id":3,"type":"toyota supra"}]}'
curl -X PUT http://localhost:9200/widgets/widget/4 -d
'{"id":4,"type":"unicorn","components":}'

I'm trying to find all widgets that have a component with "id" 1 and I
want to sort by the "type" value. Anything without that value should get
sorted to the bottom (I'm using 'Z' for that below, though in practice I
use character '\uffe0'). I use the following script:

ids = doc['components.id'];
if (ids.empty || ids.values.length == 0)
return 'Z';

types = doc['components.type'];
if (!ids.multiValued)
return types.value;

for (i = 0; i < ids.values.length; i++)
if (ids.values[i] == 2)
return type.values[i];

return 'Z';

I'm running it with curl like so:

curl -X GET http://localhost:9200/widgets/widget/_search?pretty -d
'{"sort":{"_script":{"script":"ids=doc['''components.id'''];if(ids.empty||ids.values.length==0)return
'''Z''';type=doc['''components.type'''];if(!ids.multiValued)return
type.value;for(i=0;i<ids.values.length;i++)if(ids.values[i]==2)return
type.values[i];return '''Z''';","type":"string","order":"asc"}}}'

When I run this, some of the shards fail with a ClassCastException (full
stack trace attached):

Caused by: java.lang.ClassCastException: [J cannot be cast to [Ljava.lang.
Object;
at org.elasticsearch.common.mvel2.optimizers.impl.refl.nodes.
ArrayAccessorNest.getValue(ArrayAccessorNest.java:53)
at org.elasticsearch.common.mvel2.optimizers.impl.refl.nodes.
GetterAccessor.getValue(GetterAccessor.java:40)
... 31 more

Any ideas about what I may be doing wrong or how I can fix this? Thanks.

Kamil

--

After some digging through the source I found that this change alleviates
the problem:
https://github.com/kjiwa/elasticsearch/commit/569f86399b83184904682147c0a979a15542cfa0

Not saying this is the correct solution but it does help. I assume the same
will need to be applied for other fields that use double, boolean, etc.

Kamil

On Wednesday, September 26, 2012 4:22:51 PM UTC-7, Kamil wrote:

My guess is that somewhere along the way through to the MVEL compiler
elasticsearch is sending a long along. ArrayAccessorNest.getValue()
attempts to convert this into a Object and this is where it fails. The
following sample Java program illustrates the same issue:

public class LongArrayToObjectArray {
public static void main(String args) {
Object O = (Object) (Object) new long[0];
}
}

Compile and run it and you'll see:

$ java LongArrayToObjectArray
Exception in thread "main" java.lang.ClassCastException: [J cannot be
cast to [Ljava.lang.Object;
at LongArrayToObjectArray.main(LongArrayToObjectArray.java:5)

I'm not sure where the fix should live since I don't know where the long
comes from, but presumably the calling code should pass Long or the MVEL
compiler should be checking if the object is an array of primitive types,
e.g.

if (obj instanceof long) {
// ....
} else if (obj instanceof int) {
// ...
} else if (...) {
// ....
} else if (obj instanceof Object) {
// ....
}

Kamil

On Wednesday, September 26, 2012 12:03:42 PM UTC-7, Kamil wrote:

I'm trying to query some properties in a nested object with a script sort
and I'm running into some strange behaviour. I managed to contrive a small
example that reproduces this.

First, create a new index and apply the attached mapping:

curl -X PUT http://localhost:9200/widgets
curl -X PUT http://localhost:9200/widgets/widget/_mapping -d @widget.json

Then add some objects to the index:

curl -X PUT http://localhost:9200/widgets/widget/1 -d
'{"id":1,"type":"pegasus","components":[{"id":1,"type":"honda
civic"},{"id":2,"type":"mazda rx-8"}]}'
curl -X PUT http://localhost:9200/widgets/widget/2 -d
'{"id":2,"type":"pegasus","components":[{"id":1,"type":"honda
accord"},{"id":2,"type":""}]}'
curl -X PUT http://localhost:9200/widgets/widget/3 -d
'{"id":3,"type":"unicorn","components":[{"id":3,"type":"toyota supra"}]}'
curl -X PUT http://localhost:9200/widgets/widget/4 -d
'{"id":4,"type":"unicorn","components":}'

I'm trying to find all widgets that have a component with "id" 1 and I
want to sort by the "type" value. Anything without that value should get
sorted to the bottom (I'm using 'Z' for that below, though in practice I
use character '\uffe0'). I use the following script:

ids = doc['components.id'];
if (ids.empty || ids.values.length == 0)
return 'Z';

types = doc['components.type'];
if (!ids.multiValued)
return types.value;

for (i = 0; i < ids.values.length; i++)
if (ids.values[i] == 2)
return type.values[i];

return 'Z';

I'm running it with curl like so:

curl -X GET http://localhost:9200/widgets/widget/_search?pretty -d
'{"sort":{"_script":{"script":"ids=doc['''components.id'''];if(ids.empty||ids.values.length==0)return
'''Z''';type=doc['''components.type'''];if(!ids.multiValued)return
type.value;for(i=0;i<ids.values.length;i++)if(ids.values[i]==2)return
type.values[i];return '''Z''';","type":"string","order":"asc"}}}'

When I run this, some of the shards fail with a ClassCastException (full
stack trace attached):

Caused by: java.lang.ClassCastException: [J cannot be cast to [Ljava.lang
.Object;
at org.elasticsearch.common.mvel2.optimizers.impl.refl.nodes.
ArrayAccessorNest.getValue(ArrayAccessorNest.java:53)
at org.elasticsearch.common.mvel2.optimizers.impl.refl.nodes.
GetterAccessor.getValue(GetterAccessor.java:40)
... 31 more

Any ideas about what I may be doing wrong or how I can fix this? Thanks.

Kamil

--