javaspring-boothibernatepsqlblaze-persistence

Blaze Persistence check List contains entry


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:

  1. where(value).in(fieldName)
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>, '+', '-', '*', '/', '%', '||'}
  1. Set value as a param
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.

  1. Use FUNCTION

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]

  1. Use FUNCTION without param

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 SyntaxErrorExceptionas my first option

  1. Escape value

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.]
  1. Escape value with 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.


Solution

  • 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());