Query using "CASE WHEN" statement in WHERE causes QuerySyntaxException: unexpected AST - jpa

I'm trying to make a query using Spring Data, but I cannot make it work:
#Query(SELECT t FROM Thing t WHERE name LIKE :name AND CASE WHEN (:minVal <= 0) THEN TRUE ELSE (val <= :minVal) END AND CASE WHEN (:maxVal <= 0) THEN TRUE ELSE (val >= :maxVal) END)
Page<Thing> getThings(#Param("name") String name, #Param("maxVal") int maxVal, #Param("minVal") minVal);
StackTrace:
Caused by: java.lang.IllegalArgumentException:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST
node: CASE near line 1, column 49 [SELECT t FROM Thing t WHERE name
LIKE :name AND CASE WHEN (:minVal <= 0) THEN TRUE ELSE (val <=
:minVal) END AND CASE WHEN (:maxVal <= 0) THEN TRUE ELSE (val >=
:maxVal) END] at
org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1750)
at
org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1677)
at
org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1683)
at
org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:331)
at sun.reflect.GeneratedMethodAccessor40.invoke(Unknown Source) at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606) at
org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:334)
at com.sun.proxy.$Proxy83.createQuery(Unknown Source) at
org.springframework.data.jpa.repository.query.SimpleJpaQuery.validateQuery(SimpleJpaQuery.java:78)
... 207 more
Caused by:
org.hibernate.hql.internal.ast.QuerySyntaxException: unexpected AST
node: CASE near line 1, column 49 [SELECT t FROM Thing t WHERE name
LIKE :name AND CASE WHEN (:minVal <= 0) THEN TRUE ELSE (val <=
:minVal) END AND CASE WHEN (:maxVal <= 0) THEN TRUE ELSE (val >=
:maxVal) END] at
org.hibernate.hql.internal.ast.QuerySyntaxException.convert(QuerySyntaxException.java:91)
at
org.hibernate.hql.internal.ast.ErrorCounter.throwQueryException(ErrorCounter.java:109)
at
org.hibernate.hql.internal.ast.QueryTranslatorImpl.analyze(QueryTranslatorImpl.java:284)
at
org.hibernate.hql.internal.ast.QueryTranslatorImpl.doCompile(QueryTranslatorImpl.java:206)
at
org.hibernate.hql.internal.ast.QueryTranslatorImpl.compile(QueryTranslatorImpl.java:158)
at
org.hibernate.engine.query.spi.HQLQueryPlan.(HQLQueryPlan.java:126)
at
org.hibernate.engine.query.spi.HQLQueryPlan.(HQLQueryPlan.java:88)
at
org.hibernate.engine.query.spi.QueryPlanCache.getHQLQueryPlan(QueryPlanCache.java:190)
at
org.hibernate.internal.AbstractSessionImpl.getHQLQueryPlan(AbstractSessionImpl.java:301)
at
org.hibernate.internal.AbstractSessionImpl.createQuery(AbstractSessionImpl.java:236)
at
org.hibernate.internal.SessionImpl.createQuery(SessionImpl.java:1800)
at
org.hibernate.jpa.spi.AbstractEntityManagerImpl.createQuery(AbstractEntityManagerImpl.java:328)
I'm using this because I want to make a longer query using five filters at least, and I want to simplify the effort of doing the filter combinations making differents querys.
Don't know if there is a different (and better) way to do what I want, glad to ear it if it is.
Thank you.
EDIT: Using native query works fine, but isn't compatible with pagination yet...

It looks like Hibernate cannot evaluate the result of a CASE expression when it returns a boolean literal directly. A workaround is to make the CASE expression part of another expression, e.g. by comparing it to another boolean literal.
So instead of:
... AND CASE WHEN (:minVal <= 0) THEN TRUE ELSE (val <= :minVal) END
Try:
... AND (CASE WHEN (:minVal <= 0) THEN TRUE ELSE (val <= :minVal) END) = TRUE
But looking at that expression, wouldn't it be simpler to just do:
... AND (:minVal <= 0 OR val <= :minVal)
Is it not equivalent?

Related

Drools rule with not condition with multiple condition causing error

When i use below condition with 'not' I am getting an error.
not(Obj1(value == 0) && Obj2(value <= 3))
However if i replace above condition as below I am not getting any casting exception
Obj1(value != 0) or Obj2(value > 3)
The rule looks like this:
rule "test_6"
salience 10
when
not(Obj1(value == 0) && Obj2(value <= 3))
then
.....
end
And this is the error I'm getting:
throwing error Error Message: org.drools.core.rule.GroupElement cannot be cast to org.drools.core.rule.Pattern
The && and || operators can only be used inside a single pattern. For example: Obj1( value > 3 && value < 10 || value == 0). According to the documentation, to separate Patterns, you have to use the and and or operators.
So, in your case, your rule should be:
rule "test_6"
salience 10
when
not(Obj1(value == 0) and Obj2(value <= 3))
then
.....
end
Note that it was not failing when you were using or because that was the right operator to use instead of ||.
Hope it helps,

How to resolve a stack overflow error while solving the coin change problem using recursion

I'm getting stack overflow error while running the below function for coin change problem.
{
def countchanger(m: Int, c:List[Int]): Int = {
if (money == 0 || coins.isEmpty) 1
else if (money < 0) 0
else countchanger(money - coins.head, coins) + countchanger(money, coins.tail)
}
countchanger(money, coins.sorted)
}
Appreciate any help in understanding why the stack overflow error and how this function can be written in a better way.
This is not an issue when countchanger function is removed from countChange function.
def countChange(money: Int, coins: List[Int]): Int =
{
if ((money < 0) || coins.isEmpty) 0
else if (money == 0) 1
else countChange(money - coins.head, coins) + countChange(money, coins.tail)
}
In your code you have called coins.tail and coins.head rather than c.tail and c.head. This means that the function is endlessly calling itself from the second iteration onward with the same input parameters, thus never completing and hitting the maximum depth of recursive calls allowed. A good example of why maintaining the scope of variable names is important!

Group typo3 condition

I need such a condition in ts:
([treeLevel = 0] && [globalVar = GP:R > 0]) || [PIDinRootline = {$pages.2018}]
I wanna show block if page has treelevel=0 and the get var R > 0, or if page id = $pages.2018
It looks like the similar code in php:
if(($treeLevel == 0 && $r > 0) || (pid == number))
The all expression in first brackets should be right, or in second.
Is it exist the method to group it like the previous record or I can only use userfunc?
There is no grouping in TS conditions, but if you need this particular condition from your post I think it is not needed because brackets around && are useless in this case.
(p && q) || r
is exactly the same as
p && q || r
Did you tested it?

geotools filter CQLException: Encountered "t"

I am querying a simple feature type schema:
r:Long:index=join,*g:Point:srid=4326,di:Integer:index=join,al:Float,s:Float,b:Float,an:Float,he:Float,ve:Float,t:Float,m:Boolean,i:Boolean,ts:Long;geomesa.table.sharing='true',geomesa.indices='attr:4:3,records:2:3,z2:3:3',geomesa.table.sharing.prefix='\\u0001'
with the query expression: r = 31 AND di = 5 AND BBOX(g, -38.857822, -76.111145, -74.64091, -38.61907) AND al <= 39.407307 AND s <= 1.6442835 AND b <= 83.14717 AND an <= 87.0774 AND he <= 40.89476 AND ve <= 88.761566 AND t <= 44.786507 AND m = true AND i = true.
but it throws an exception saying Encountered "t" at line 1, column 195.
Here is my exception log detail:
org.geotools.filter.text.cql2.CQLException: Encountered "t" at line 1, column 195.
Was expecting one of:
<NOT> ...
<IDENTIFIER> ...
"include" ...
"exclude" ...
"(" ...
"[" ...
Parsing : r = 31 AND di = 5 AND BBOX(g, -38.857822, -76.111145, -74.64091, -38.61907) AND al <= 39.407307 AND s <= 1.6442835 AND b <= 83.14717 AND an <= 87.0774 AND he <= 40.89476 AND ve <= 88.761566 AND t <= 44.786507 AND m = true AND i = true.
at org.geotools.filter.text.cql2.CQLCompiler.compileFilter(CQLCompiler.java:106)
at org.geotools.filter.text.commons.CompilerUtil.parseFilter(CompilerUtil.java:196)
at org.geotools.filter.text.cql2.CQL.toFilter(CQL.java:134)
at org.geotools.filter.text.cql2.CQL.toFilter(CQL.java:113)
at com.hps.GeomesaClient.query(GeomesaClient.java:134)
at com.hps.Reader.run(Reader.java:69)
at java.lang.Thread.run(Thread.java:745)
I am not able to determine, why it's throwing an exception on querying with the attribute named t. Whereas if I remove attribute t from the query, it works as expected. Is t a reserved key? or I am missing something.
Ok, this is a limitation in the ECQL query parser. The letter 't' by itself (ignoring case) is the UTC token.
https://github.com/geotools/geotools/blob/master/modules/library/cql/src/main/jjtree/ECQLGrammar.jjt#L180-L187
The options are to work with the GeoTools team to fix this corner case or pick a different attribute name. Nice find!

Set specific range for int in scala

class Test(val myInt :Int){}
I want that for myInt only 0 to 10 must be allowed.So how to specify range for member variable in scala with val type.
Give Refined a look. It allows you to create range types which are checked at compile time.
Your range would look like this:
type InMyRange = Interval.ClosedOpen[W.`0`.T, W.`10`.T]
and you can create a value of this type like so:
refineMV[InMyRange](0)
// Refined[Int, InMyRange] = 0
refineMV[InMyRange](9)
// Refined[Int, InMyRange] = 9
In the error cases:
refineMV[InMyRange](-1)
// Left predicate of (!(-1 < 0) && (-1 < 10)) failed: Predicate (-1 < 0) did not fail
refineMV[InMyRange](10)
// Right predicate of (!(10 < 0) && (10 < 10)) failed: Predicate failed: (10 < 10)
simply adding require or whatever which throws an exception when it gets invalid value solves your issue.
class Test(val myInt :Int){
require(0 <= myInt && myInt <= 10)
}