I am currently trying to implement a generic query builder that can be reused along different entities. I am using Blaze Persistence to assemble the query. One of the options the builder must support is to check if a List attribute contains a given value. So in pseudo-code this would mean Entity.Attribute.contains(passedValue). What I am struggling with now is get this query to work. I created a view that contains the following field
@Type(ListArrayType.class)
@Column(name = "co_creators", columnDefinition = "UUID[]")
private List<UUID> coCreators;
which is created like this
COALESCE(
(SELECT ARRAY_AGG(cc.cocreatorid)
FROM cocreator cc
WHERE cc.elementid = c.id
AND cc.elementtype = 'COURSE'),
'{}'
) AS co_creators
I then try to assemble the query the following ways, however nothing works as expected:
restrictionBuilder = criteria.where(queryParams.getValue().toString());
restrictionBuilder.in(queryParams.getFieldName());
This will result in
com.blazebit.persistence.parser.expression.SyntaxErrorException: Could not parse expression '7035bf82-a831-4a78-8615-6cc0e0c1ca26', line 1:4 mismatched input 'bf82' expecting {<EOF>, '+', '-', '*', '/', '%', '||'}
restrictionBuilder = criteria.where(":value");
restrictionBuilder.in(queryParams.getFieldName());
criteria.setParameter("value", queryParams.getValue().toString());
This will create a query like:
SELECT selectedObject FROM CourseView selectedObject WHERE :value IN (:param_0) ORDER BY selectedObject.uuid DESC
This seems to be correct however if I have an Entity with the coCreators field like:
{7035bf82-a831-4a78-8615-6cc0e0c1ca26}
And pass '7035bf82-a831-4a78-8615-6cc0e0c1ca26'
as my value it will return an empty list but I expect the above entry to be found.
I also tried
restrictionBuilder = criteria.where("FUNCTION('any', :value, coCreators)");
criteria.setParameter("value", queryParams.getValue().toString());
however this results in Resolved [java.lang.IllegalArgumentException: Parameter name "value" does not exist]
Then I tried the same without setParameter
, like
restrictionBuilder =
criteria.where("FUNCTION('any', " + queryParams.getValue().toString() + ", coCreators)");
criteria.setParameter("value", queryParams.getValue().toString());
Which will end up with the same SyntaxErrorException
as my first option
Also escaping the value like
restrictionBuilder = criteria.where("FUNCTION('any', '" + queryParams.getValue().toString() + "', coCreators)");
does not help. This will en up with
ExceptionHandlerExceptionResolver : Resolved [com.blazebit.persistence.impl.BuilderChainingException: A builder was not ended properly.]
eq(true)
I then tried this
restrictionBuilder =
criteria.where("FUNCTION('any', '" + queryParams.getValue().toString() + "', coCreators)");
restrictionBuilder.eq(true);
as any should return a boolean, however also this does not solve the issue as it results in:
Caused by: org.postgresql.util.PSQLException: ERROR: invalid input syntax for type boolean: "7035bf82-a831-4a78-8615-6cc0e0c1ca26"
Position: 562
I am completely lost here and would really appreciate some help. Is there any way to get this to work the way I need it using Blaze Persistence?
I think an option would be to use a native query, however it must be possible to only use a native query for this condition but combine it with other (non-native) conditions. However I also don't know how to include custom queries/conditions in Blaze Persistence.
Since you're trying to use PostgreSQL arrays, it's best to use a custom function to implement this. The parsing problems you are facing are potentially due to the use of the function name any
, since that is a keyword and not a real function.
You can take inspiration from other users use cases, which are similar to yours: How to Implement Full-Text Search with PostgreSQL Using Blaze Persistence CriteriaBuilder?
Essentially, you need a custom JPQLFunction
: https://persistence.blazebit.com/documentation/1.6/core/manual/en_US/index.html#custom-jpql-functions
Render the SQL parts that you need, but also stick a and 1
at the end or 1 and
at the start, so that you can compare the function against the literal 1
. This is a way to make predicates work nicely with the query optimizer while working within the limits of the JPQL/HQL query parser.
Then you can use it like
criteria.where("FUNCTION('my_function', :my_param, coCreators)").eqLiteral(1);
criteria.setParameter("my_param", queryParams.getValue());