agrestio / agrest

Server-side Java REST Framework for easy access to data graphs from various backends
https://agrest.io
Apache License 2.0
80 stars 34 forks source link

'cayenneExp' with date parameters results in exception in advanced includes #268

Open andrus opened 6 years ago

andrus commented 6 years ago

Consider the following two requests, filtering the contents of an advanced include by date:

/api?include={"path":"someRel","cayenneExp":"dateProperty>="2017-10-04"}
/api?include={"path":"someRel","cayenneExp":["dateProperty>=$d", "2017-10-04"]}

This results in the following exception:

java.lang.ClassCastException: java.lang.String cannot be cast to java.time.chrono.ChronoLocalDate
    at java.time.LocalDate.compareTo(LocalDate.java:137)
    at org.apache.cayenne.exp.parser.Evaluator$6.compare(Evaluator.java:284)
    at org.apache.cayenne.exp.parser.Evaluator$NonNullLhsEvaluator.compare(Evaluator.java:70)
    at org.apache.cayenne.exp.parser.ASTGreaterOrEqual.evaluateSubNode(ASTGreaterOrEqual.java:59)
....
Wrapped by: org.apache.cayenne.exp.ExpressionException: [v.4.0.B1 Jun 02 2017 15:11:18] Error evaluating expression 'dateProperty >= "2017-10-04"'
    at org.apache.cayenne.exp.parser.SimpleNode.evaluate(SimpleNode.java:421)
    at org.apache.cayenne.exp.Expression.match(Expression.java:491)
    at com.nhl.link.rest.encoder.ListEncoder.encode(ListEncoder.java:147)
    at com.nhl.link.rest.encoder.ListEncoder.encodeAndGetTotal(ListEncoder.java:86)
    at com.nhl.link.rest.encoder.CollectionEncoder.encode(CollectionEncoder.java:16)
    at com.nhl.link.rest.property.PropertyBuilder.encode(PropertyBuilder.java:71)
    at com.nhl.link.rest.encoder.EntityEncoder.encodeNonNullObject(EntityEncoder.java:41)
    at com.nhl.link.rest.encoder.AbstractEncoder.encode(AbstractEncoder.java:19)
    at com.nhl.link.rest.encoder.ListEncoder.encode(ListEncoder.java:148)
    at com.nhl.link.rest.encoder.ListEncoder.encodeAndGetTotal(ListEncoder.java:86)
    at com.nhl.link.rest.encoder.DataResponseEncoder.encodeObjectBody(DataResponseEncoder.java:40)
    at com.nhl.link.rest.encoder.DataResponseEncoder.encode(DataResponseEncoder.java:33)
    at com.nhl.link.rest.DataResponse.writeData(DataResponse.java:122)
CayenneRuntimeException java.lang.String cannot be cast to java.time.chrono.ChronoLocalDate

Advanced includes are filtered in memory (the same exp on the root entity that is translated to SQL works). "dateProperty" is a java.time.LocalDate. I suspect if we map it as java.util.Date it will work too. The problem may actually be on Cayenne side. But logging it here for documentation purposes and to test any possible fixes.

atomashpolskiy commented 6 years ago

I guess that's because in case with SQL the expression is translated to a string by collecting 'toString' representations of the value nodes, so it's guaranteed to work regardless of the node's type, and in-memory evaluation expects the respective nodes to have correct types, because it has to do the cast in order to call the evaluator. So somebody has to do the type conversion for values.

andrus commented 6 years ago

I need to verify my theory about java.util.Date (i.e. that we are already doing conversions for at least some types).