Skip to content

Commit 8657ed6

Browse files
committed
Use Hibernates case-insensitive pattern matching when available.
This commit provides case-insensitive pattern matching when using `=ilike=` or `=inotlike=` that relies on Hibernate's implementation for `ilike`. However, if JPA provider is different from Hibernate we fallback to old implementation. The reason behind this change is to allow Hibernate generate "proper" case-insensitive pattern matching query depending on database.
1 parent 6dfdc3d commit 8657ed6

File tree

2 files changed

+50
-6
lines changed

2 files changed

+50
-6
lines changed
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package io.github.perplexhub.rsql;
2+
3+
import jakarta.persistence.criteria.CriteriaBuilder;
4+
import jakarta.persistence.criteria.Expression;
5+
import jakarta.persistence.criteria.Predicate;
6+
import org.hibernate.query.criteria.HibernateCriteriaBuilder;
7+
import org.springframework.util.ClassUtils;
8+
9+
final class HibernateSupport {
10+
11+
private static final boolean isHibernatePresent = ClassUtils.isPresent(
12+
"org.hibernate.query.criteria.HibernateCriteriaBuilder", HibernateSupport.class.getClassLoader());
13+
14+
private HibernateSupport() {
15+
}
16+
17+
static boolean isHibernateCriteriaBuilder(CriteriaBuilder cb) {
18+
return isHibernatePresent && cb instanceof HibernateCriteriaBuilder;
19+
}
20+
21+
/**
22+
* Must be guarded with {@linkplain #isHibernatePresent} before invoking.
23+
*/
24+
static Predicate ilike(CriteriaBuilder cb, Expression<String> expression, String arg, Character escapeChar) {
25+
var hcb = (HibernateCriteriaBuilder) cb;
26+
var pattern = '%' + arg + '%';
27+
28+
return escapeChar != null
29+
? hcb.ilike(expression, pattern, escapeChar)
30+
: hcb.ilike(expression, pattern);
31+
}
32+
}

rsql-jpa/src/main/java/io/github/perplexhub/rsql/RSQLJPAPredicateConverter.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -330,21 +330,19 @@ private Predicate expressionPredicate(ComparisonNode node, ResolvedExpression.Pa
330330
return builder.notEqual(expression, argument);
331331
}
332332
if (op.equals(LIKE)) {
333-
return likePredicate(expression.as(String.class), "%" + argument.toString() + "%", builder);
333+
return likePredicate(expression, argument, false);
334334
}
335335
if (op.equals(NOT_LIKE)) {
336-
return likePredicate(expression.as(String.class), "%" + argument.toString() + "%", builder).not();
336+
return likePredicate(expression, argument, false).not();
337337
}
338338
if (op.equals(IGNORE_CASE)) {
339339
return builder.equal(builder.upper(expression), argument.toString().toUpperCase());
340340
}
341341
if (op.equals(IGNORE_CASE_LIKE)) {
342-
return likePredicate(builder.upper(expression), "%" + argument.toString()
343-
.toUpperCase() + "%", builder);
342+
return likePredicate(expression, argument, true);
344343
}
345344
if (op.equals(IGNORE_CASE_NOT_LIKE)) {
346-
return likePredicate(builder.upper(expression), "%" + argument.toString()
347-
.toUpperCase() + "%", builder).not();
345+
return likePredicate(expression, argument, true).not();
348346
}
349347
if (op.equals(EQUAL)) {
350348
return equalPredicate(expression, type, argument);
@@ -388,6 +386,20 @@ private Predicate likePredicate(Expression attributePath, String likeExpression,
388386
.orElseGet(() -> builder.like(attributePath, likeExpression));
389387
}
390388

389+
private Predicate likePredicate(Expression<?> expression, Object argument, boolean ignoreCase) {
390+
String argToUse = String.valueOf(argument);
391+
Expression<String> strExpression = expression.as(String.class);
392+
if (ignoreCase) {
393+
if (HibernateSupport.isHibernateCriteriaBuilder(builder)) {
394+
return HibernateSupport.ilike(builder, strExpression, argToUse, likeEscapeCharacter);
395+
}
396+
397+
return likePredicate(builder.upper(strExpression), "%" + argToUse.toUpperCase(Locale.ROOT) + "%", builder);
398+
}
399+
400+
return likePredicate(strExpression, "%" + argToUse + "%", builder);
401+
}
402+
391403
private Predicate equalPredicate(Expression expr, Class type, Object argument) {
392404
if (type.equals(String.class)) {
393405
String argStr = argument.toString();

0 commit comments

Comments
 (0)