Skip to content

Commit

Permalink
Case insensitive like operator
Browse files Browse the repository at this point in the history
  • Loading branch information
Shredder121 committed Nov 13, 2015
1 parent 983aab4 commit 96698c2
Show file tree
Hide file tree
Showing 21 changed files with 202 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Pattern;

import com.google.common.base.Objects;
import com.mysema.query.types.Expression;
Expand Down Expand Up @@ -252,6 +253,33 @@ public static boolean like(String str, String like, char escape) {
return like(str, like);
}

public static boolean likeIgnoreCase(String str, String like) {
final StringBuilder pattern = new StringBuilder(like.length() + 4);
for (int i = 0; i < like.length(); i++) {
final char ch = like.charAt(i);
if (ch == '%') {
pattern.append(".*");
continue;
} else if (ch == '_') {
pattern.append('.');
continue;
} else if (ch == '.' || ch == '$' || ch == '^') {
pattern.append('\\');
}
pattern.append(ch);
}
if (pattern.toString().equals(like)) {
return str.equalsIgnoreCase(like);
} else {
return Pattern.compile(pattern.toString(), Pattern.CASE_INSENSITIVE)
.matcher(str).matches();
}
}

public static boolean likeIgnoreCase(String str, String like, char escape) {
return likeIgnoreCase(str, like);
}

public static <T> T get(Object parent, String f) {
try {
Field field = ReflectionUtils.getFieldOrNull(parent.getClass(), f);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ protected CollQueryTemplates() {

// String
add(Ops.LIKE, functions + ".like({0},{1})");
add(Ops.LIKE_IC, functions + ".likeIgnoreCase({0},{1})");
add(Ops.LIKE_ESCAPE, functions + ".like({0},{1},{2})");
add(Ops.LIKE_ESCAPE_IC, functions + ".likeIgnoreCase({0},{1},{2})");

// Path types
for (PathType type : new PathType[] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -757,6 +757,23 @@ public static Expression<?> toExpression(Object o) {
}
}

/**
* Converts the given expression to lower(expression)
*
* <p>Constants are lower()ed at creation time</p>
*
* @param stringExpression the string to lower()
* @return lower(stringExpression)
*/
public static Expression<String> toLower(Expression<String> stringExpression) {
if (stringExpression instanceof Constant) {
Constant<String> constantExpression = (Constant<String>) stringExpression;
return ConstantImpl.create(constantExpression.getConstant().toLowerCase(Locale.ENGLISH));
} else {
return operation(String.class, Ops.LOWER, stringExpression);
}
}

/**
* Create an expression out of the given order specifiers
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public JavaTemplates() {
add(Ops.TRIM, "{0}.trim()");
add(Ops.UPPER, "{0}.toUpperCase()");
add(Ops.MATCHES, "{0}.matches({1})");
add(Ops.MATCHES_IC, "{0}.matches({1})");
add(Ops.MATCHES_IC, "{0l}.matches({1l})");
add(Ops.STRING_LENGTH, "{0}.length()");
add(Ops.STRING_IS_EMPTY, "{0}.isEmpty()");
add(Ops.STRING_CONTAINS, "{0}.contains({1})");
Expand Down
4 changes: 4 additions & 0 deletions querydsl-core/src/main/java/com/mysema/query/types/Ops.java
Original file line number Diff line number Diff line change
Expand Up @@ -154,8 +154,12 @@ public final class Ops {

public static final Operator<Boolean> LIKE = new OperatorImpl<Boolean>(NS, "LIKE");

public static final Operator<Boolean> LIKE_IC = new OperatorImpl<Boolean>(NS, "LIKE_IC");

public static final Operator<Boolean> LIKE_ESCAPE = new OperatorImpl<Boolean>(NS, "LIKE_ESCAPE");

public static final Operator<Boolean> LIKE_ESCAPE_IC = new OperatorImpl<Boolean>(NS, "LIKE_ESCAPE_IC");

// case
public static final Operator<Object> CASE = new OperatorImpl<Object>(NS, "CASE");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
*/
package com.mysema.query.types;

import java.util.Arrays;
import java.util.IdentityHashMap;
import java.util.Map;

Expand Down Expand Up @@ -142,7 +143,9 @@ public String escapeForLike(String str) {
add(Ops.INDEX_OF_2ARGS, "indexOf({0},{1},{2})");
add(Ops.STRING_IS_EMPTY, "empty({0})");
add(Ops.LIKE, "{0} like {1}", Precedence.COMPARISON);
add(Ops.LIKE_IC, "{0l} like {1l}", Precedence.COMPARISON);
add(Ops.LIKE_ESCAPE, "{0} like {1} escape '{2s}'", Precedence.COMPARISON);
add(Ops.LIKE_ESCAPE_IC, "{0l} like {1l} escape '{2s}'", Precedence.COMPARISON);

add(Ops.StringOps.LEFT, "left({0},{1})");
add(Ops.StringOps.RIGHT, "right({0},{1})");
Expand Down Expand Up @@ -329,8 +332,12 @@ public final int getPrecedence(Operator<?> op) {
return precedence.get(op).intValue();
}

protected void setPrecedence(int p, Operator... ops) {
for (Operator op : ops) {
protected void setPrecedence(int p, Operator<?>... ops) {
setPrecedence(p, Arrays.asList(ops));
}

protected void setPrecedence(int p, Iterable<? extends Operator<?>> ops) {
for (Operator<?> op : ops) {
precedence.put(op, p);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,27 @@ public BooleanExpression like(String str) {
public BooleanExpression like(Expression<String> str) {
return BooleanOperation.create(Ops.LIKE, mixin, str);
}


/**
* Expr: {@code this like str} ignoring case.
*
* @param str string
* @return this like string
*/
public BooleanExpression likeIgnoreCase(String str) {
return BooleanOperation.create(Ops.LIKE_IC, mixin, ConstantImpl.create(str));
}

/**
* Expr: {@code this like str} ignoring case.
*
* @param str string
* @return this like string
*/
public BooleanExpression likeIgnoreCase(Expression<String> str) {
return BooleanOperation.create(Ops.LIKE_IC, mixin, str);
}

/**
* Expr: {@code this like str}
*
Expand All @@ -340,6 +360,28 @@ public BooleanExpression like(Expression<String> str, char escape) {
return BooleanOperation.create(Ops.LIKE_ESCAPE, mixin, str, ConstantImpl.create(escape));
}

/**
* Expr: {@code this like str} ignoring case
*
* @param str string
* @param escape escape character
* @return this like string
*/
public BooleanExpression likeIgnoreCase(String str, char escape) {
return BooleanOperation.create(Ops.LIKE_ESCAPE_IC, mixin, ConstantImpl.create(str), ConstantImpl.create(escape));
}

/**
* Expr: {@code this like str} ignoring case
*
* @param str string
* @param escape escape character
* @return this like string
*/
public BooleanExpression likeIgnoreCase(Expression<String> str, char escape) {
return BooleanOperation.create(Ops.LIKE_ESCAPE_IC, mixin, str, ConstantImpl.create(escape));
}

/**
* Get the position of the given String in this String, the first position is 1
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,14 @@ public Collection<Predicate> string(StringExpression expr, StringExpression othe
rv.add(expr.like("%"+knownValue.substring(1), '!'));
rv.add(expr.like("%"+knownValue.substring(1,2)+"%", '!'));

rv.add(expr.likeIgnoreCase(knownValue.substring(0, 1) + "%"));
rv.add(expr.likeIgnoreCase("%" + knownValue.substring(1)));
rv.add(expr.likeIgnoreCase("%" + knownValue.substring(1, 2) + "%"));

rv.add(expr.likeIgnoreCase(knownValue.substring(0, 1) + "%", '!'));
rv.add(expr.likeIgnoreCase("%" + knownValue.substring(1), '!'));
rv.add(expr.likeIgnoreCase("%" + knownValue.substring(1, 2) + "%", '!'));

rv.add(expr.notLike(knownValue.substring(0,1)+"%"));
rv.add(expr.notLike("%"+knownValue.substring(1)));
rv.add(expr.notLike("%"+knownValue.substring(1,2)+"%"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,11 +279,19 @@ protected void visitOperation(Class<?> type, Operator<?> operator, List<? extend
handle(args.get(0)).append(" instanceof ");
append(((Constant<Class<?>>) args.get(1)).getConstant().getName());

} else if (operator == Ops.LIKE || operator == Ops.LIKE_ESCAPE) {
super.visitOperation(type, Ops.MATCHES,
ImmutableList.of(args.get(0), ExpressionUtils.likeToRegex((Expression<String>) args.get(1), false)));

// exists
} else if (operator == Ops.LIKE || operator == Ops.LIKE_ESCAPE || operator == Ops.LIKE_IC || operator == Ops.LIKE_ESCAPE_IC) {
@SuppressWarnings("unchecked") //This is the expected type for like
Expression<String> string = (Expression<String>) args.get(0);
@SuppressWarnings("unchecked") //This is the expected type for like
Expression<String> regex = ExpressionUtils.likeToRegex((Expression<String>) args.get(1), false);
if (operator == Ops.LIKE_IC || operator == Ops.LIKE_ESCAPE_IC) {
string = ExpressionUtils.toLower(string);
regex = ExpressionUtils.toLower(regex);
}
super.visitOperation(type, Ops.MATCHES,
ImmutableList.of(string, regex));

// exists
} else if (operator == Ops.EXISTS && args.get(0) instanceof SubQueryExpression) {
final SubQueryExpression subQuery = (SubQueryExpression) args.get(0);
append("(");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,9 @@ protected JDOQLTemplates() {
add(Ops.EQ_IGNORE_CASE, "{0l}.equals({1l})");
add(Ops.STRING_IS_EMPTY, "{0} == \"\"", Precedence.EQUALITY);
add(Ops.LIKE, "{0}.like({1})");
add(Ops.LIKE_IC, "{0l}.like({1l})");
add(Ops.LIKE_ESCAPE, "{0}.like({1})");
add(Ops.LIKE_ESCAPE_IC, "{0}.like({1})");

add(Ops.STRING_CAST, "(String){0}");

Expand Down
19 changes: 13 additions & 6 deletions querydsl-jdo/src/test/java/com/mysema/query/jdo/BasicsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,17 @@
*/
package com.mysema.query.jdo;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

import java.io.IOException;

import javax.jdo.PersistenceManager;
import javax.jdo.Transaction;
import java.io.IOException;

import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

import com.google.common.collect.ImmutableList;
import com.mysema.query.BooleanBuilder;
Expand All @@ -26,11 +34,6 @@
import com.mysema.query.jdo.test.domain.QProduct;
import com.mysema.query.types.Expression;
import com.mysema.query.types.Projections;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

public class BasicsTest extends AbstractJDOTest {

Expand Down Expand Up @@ -222,6 +225,10 @@ public void Starts_With() {
@Test
public void Matches() {
assertEquals("matches", 1, query(product,product.name.matches("Sony.*")).size());
assertEquals(
query(product, product.name.matches("Sony.*")).size(),
query(product, product.name.likeIgnoreCase("sony%")).size()
);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ private <A,K,V> List<BooleanExpression> getFilters(
str.substring(1).eq(knownString),
str.substring(1,2).eq(knownString),
str.lower().eq(knownString),
str.likeIgnoreCase(knownString),
str.upper().eq(knownString),
str.matches(".*"),
// java.util.Collection
Expand Down
15 changes: 11 additions & 4 deletions querydsl-jpa/src/main/java/com/mysema/query/jpa/JPQLTemplates.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,11 @@
*/
package com.mysema.query.jpa;

import java.util.Set;

import javax.annotation.Nullable;

import com.google.common.collect.ImmutableSet;
import com.mysema.query.types.Operator;
import com.mysema.query.types.Ops;
import com.mysema.query.types.PathType;
Expand All @@ -34,6 +37,13 @@ public class JPQLTemplates extends Templates {

public static final JPQLTemplates DEFAULT = new JPQLTemplates();

protected static final Set<? extends Operator<?>> OTHER_LIKE_CASES
= ImmutableSet.<Operator<?>>of(Ops.MATCHES, Ops.MATCHES_IC,
Ops.ENDS_WITH, Ops.ENDS_WITH_IC,
Ops.LIKE_IC, Ops.LIKE_ESCAPE_IC,
Ops.STARTS_WITH, Ops.STARTS_WITH_IC,
Ops.STRING_CONTAINS, Ops.STRING_CONTAINS_IC);

private final QueryHandler queryHandler;

protected JPQLTemplates() {
Expand All @@ -52,10 +62,7 @@ protected JPQLTemplates(char escape, QueryHandler queryHandler) {
Ops.BETWEEN, Ops.COL_IS_EMPTY);

// other like cases
setPrecedence(Precedence.COMPARISON, Ops.MATCHES, Ops.MATCHES_IC,
Ops.ENDS_WITH, Ops.ENDS_WITH_IC,
Ops.STARTS_WITH, Ops.STARTS_WITH_IC,
Ops.STRING_CONTAINS, Ops.STRING_CONTAINS_IC);
setPrecedence(Precedence.COMPARISON, OTHER_LIKE_CASES);

add(Ops.CASE, "case {0} end");
add(Ops.CASE_WHEN, "when {0} then {1} {2}", 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,7 @@ public DB2Templates(char escape, boolean quote) {
setPrecedence(Precedence.COMPARISON, Ops.IS_NULL, Ops.IS_NOT_NULL, Ops.LIKE, Ops.LIKE_ESCAPE, Ops.BETWEEN,
Ops.IN, Ops.NOT_IN, Ops.EXISTS);

// other like cases
setPrecedence(Precedence.COMPARISON, Ops.ENDS_WITH, Ops.ENDS_WITH_IC,
Ops.STARTS_WITH, Ops.STARTS_WITH_IC,
Ops.STRING_CONTAINS, Ops.STRING_CONTAINS_IC);
setPrecedence(Precedence.COMPARISON, OTHER_LIKE_CASES);

add(SQLOps.NEXTVAL, "next value for {0s}");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,7 @@ public HSQLDBTemplates(char escape, boolean quote) {
setPrecedence(Precedence.COMPARISON + 1, Ops.IS_NULL, Ops.IS_NOT_NULL, Ops.LIKE, Ops.LIKE_ESCAPE, Ops.BETWEEN,
Ops.IN, Ops.NOT_IN, Ops.EXISTS);

// other like cases
setPrecedence(Precedence.COMPARISON + 1, Ops.ENDS_WITH, Ops.ENDS_WITH_IC,
Ops.STARTS_WITH, Ops.STARTS_WITH_IC,
Ops.STRING_CONTAINS, Ops.STRING_CONTAINS_IC);
setPrecedence(Precedence.COMPARISON + 1, OTHER_LIKE_CASES);

add(Ops.TRIM, "trim(both from {0})");
add(Ops.NEGATE, "{0} * -1", Precedence.ARITH_HIGH);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,7 @@ public OracleTemplates(char escape, boolean quote) {
setPrecedence(Precedence.COMPARISON + 1, Ops.IS_NULL, Ops.IS_NOT_NULL, Ops.LIKE, Ops.LIKE_ESCAPE, Ops.BETWEEN,
Ops.IN, Ops.NOT_IN, Ops.EXISTS);

// other like cases
setPrecedence(Precedence.COMPARISON + 1, Ops.ENDS_WITH, Ops.ENDS_WITH_IC,
Ops.STARTS_WITH, Ops.STARTS_WITH_IC,
Ops.STRING_CONTAINS, Ops.STRING_CONTAINS_IC);
setPrecedence(Precedence.COMPARISON + 1, OTHER_LIKE_CASES);

add(Ops.ALIAS, "{0} {1}");
add(SQLOps.NEXTVAL, "{0s}.nextval");
Expand Down
Loading

0 comments on commit 96698c2

Please sign in to comment.