1

I'm trying to write a custom function in hibernate to use ts_vector (postgres), my code so far:

public class PostgresSQLFTSFunction implements SQLFunction {

    static final Logger LOG = LogManager.getLogger();

    @Override
    public boolean hasArguments() {
        return true;
    }

    @Override
    public boolean hasParenthesesIfNoArguments() {
        return false;
    }

    @Override
    public Type getReturnType(Type firstArgumentType, Mapping mapping) throws QueryException {
        return new BooleanType();
    }

    @Override
    public String render(Type firstArgumentType, List arguments, SessionFactoryImplementor factory) throws QueryException {
        if (arguments == null || arguments.size() < 2) {
            throw new IllegalArgumentException("The function must have at least 2 arguments");
        }

        String fragment, ftsConfig, field, value;

        if (arguments.size() == 3) {
            ftsConfig = (String) arguments.get(0);
            field = (String) arguments.get(1);
            value  = (String) arguments.get(2);
            fragment = "to_tsvector(" + ftsConfig + ", unaccent(" + field + ")) @@ to_tsquery(" + ftsConfig + ", unaccent(" + value + "))";
        } else {
            field = (String) arguments.get(0);
            value = (String) arguments.get(1);
            fragment = "to_tsvector(unaccent(" + field + ")) @@ " + "to_tsquery('" + value + ":*' )";
        }

        LOG.info(fragment);

        return fragment;
    }
}

My repo:

@Query(value = "from City c where fts_partial(c.name, :cityName) = true")
List<City> getPartialByName(@Param("cityName") String cityName);

And registering the function:

public class SqlFunctionsMetadataBuilderContributor implements MetadataBuilderContributor {

    @Override
    public void contribute(MetadataBuilder metadataBuilder) {
        metadataBuilder.applySqlFunction("fts_partial", new PostgresSQLFTSFunction());
    }
}

The output query generated by Hibernate is:

select city0_.id as id1_0_, city0_.created_at as created_2_0_, city0_.updated_at as updated_3_0_, city0_.version as version4_0_, city0_.lat as lat5_0_, city0_.lng as lng6_0_, city0_.name as name7_0_, city0_.state_id as state_id8_0_ from cities city0_ where to_tsvector(unaccent(city0_.name)) @@ to_tsquery('?:*' )=true

The problem here is that to_tsquery('?:*' ) does not translate the question mark to the named param (cityName), because it is included in single quotes, but the single quote here is necessary, otherwise the query does not work.

For instance, the query that works is:

select city0_.id as id1_0_, city0_.created_at as created_2_0_, city0_.updated_at as updated_3_0_, city0_.version as version4_0_, city0_.lat as lat5_0_, city0_.lng as lng6_0_, city0_.name as name7_0_, city0_.state_id as state_id8_0_ from cities city0_ where to_tsvector(unaccent(city0_.name)) @@ to_tsquery('vi:*')=true;

So, the cityName question mark should be translated by HQL to 'vi' in the above example. How can I achieve that?

Thanks!

Leonardo
  • 3,141
  • 3
  • 31
  • 60
  • Did you register your function using registerFunction in extended Dialect? – YoManTaMero Jun 24 '20 at 23:07
  • I did not, I took the other MetadataBuilderContributor approach. I've just edited the question with this snippet, thanks for that! BTW, the function is being called, the problem is that the ? is being interpreted as literal, not as a variable since it is inside quotes. – Leonardo Jun 25 '20 at 19:35

0 Answers0