ClassCastException when querying nested properties


(kamil-2) #1

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

--


(kamil-2) #2

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

--


(kamil-2) #3

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

--


(system) #4