Add support for ignoreCase in queries derived from method names.

Closes #984, #1481.
This commit is contained in:
Michael Reiche
2022-06-23 09:40:34 -07:00
parent 1d6c033a41
commit 62feffa89d
8 changed files with 219 additions and 102 deletions

View File

@@ -17,7 +17,6 @@ package com.querydsl.couchbase.document;
import java.util.Collection;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;
import org.springframework.data.couchbase.core.query.QueryCriteria;
@@ -48,8 +47,6 @@ import com.querydsl.core.types.Visitor;
*/
public abstract class CouchbaseDocumentSerializer implements Visitor<Object, Void> {
boolean workInProgress = true;
public Object handle(Expression<?> expression) {
return expression.accept(this, null);
}
@@ -167,25 +164,22 @@ public abstract class CouchbaseDocumentSerializer implements Visitor<Object, Voi
return QueryCriteria.where(asDBKey(expr, 0)).startingWith(asDBValue(expr, 1));
} else if (op == Ops.STARTS_WITH_IC) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression("^" + regexValue(expr, 1), "i"));
return QueryCriteria.where(asDBKey(expr, 0)).upper()
.startingWith(asDBValue(expr, 1).toString().toUpperCase(Locale.ROOT));
return QueryCriteria.where(asDBKey(expr, 0)).startingWith(true, asDBValue(expr, 1).toString());
} else if (op == Ops.ENDS_WITH) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(regexValue(expr, 1) + "$"));
return QueryCriteria.where(asDBKey(expr, 0)).endingWith(asDBValue(expr, 1));
} else if (op == Ops.ENDS_WITH_IC) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(regexValue(expr, 1) + "$", "i"));
return QueryCriteria.where(asDBKey(expr, 0)).upper()
.endingWith(asDBValue(expr, 1).toString().toUpperCase(Locale.ROOT));
return QueryCriteria.where(asDBKey(expr, 0)).endingWith(true, asDBValue(expr, 1).toString());
} else if (op == Ops.EQ_IGNORE_CASE) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression("^" + regexValue(expr, 1) + "$", "i"));
return QueryCriteria.where(asDBKey(expr, 0)).upper().eq(asDBValue(expr, 1).toString().toUpperCase(Locale.ROOT));
return QueryCriteria.where(asDBKey(expr, 0)).eq(true, asDBValue(expr, 1).toString());
} else if (op == Ops.STRING_CONTAINS) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(".*" + regexValue(expr, 1) + ".*"));
return QueryCriteria.where(asDBKey(expr, 0)).containing(asDBValue(expr, 1));
} else if (op == Ops.STRING_CONTAINS_IC) {
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(".*" + regexValue(expr, 1) + ".*", "i"));
return QueryCriteria.where(asDBKey(expr, 0)).upper()
.containing(asDBValue(expr, 1).toString().toUpperCase(Locale.ROOT));
return QueryCriteria.where(asDBKey(expr, 0)).containing(true, asDBValue(expr, 1).toString());
/*
} else if (op == Ops.MATCHES) {
//return asDocument(asDBKey(expr, 0), new CBRegularExpression(asDBValue(expr, 1).toString()));
@@ -201,7 +195,7 @@ public abstract class CouchbaseDocumentSerializer implements Visitor<Object, Voi
} else if (op == Ops.LIKE_IC) {
// String regex = ExpressionUtils.likeToRegex((Expression) expr.getArg(1)).toString();
// return asDocument(asDBKey(expr, 0), new CBRegularExpression(regex, "i"));
return QueryCriteria.where(asDBKey(expr, 0)).upper().like(asDBValue(expr, 1).toString().toUpperCase(Locale.ROOT));
return QueryCriteria.where(asDBKey(expr, 0)).like(true, asDBValue(expr, 1).toString());
} else if (op == Ops.BETWEEN) {
// Document value = new Document("$gte", this.asDBValue(expr, 1));
// value.append("$lte", this.asDBValue(expr, 2));

View File

@@ -507,6 +507,39 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
target.setExpiration((int) (entity.getExpiryDuration().getSeconds()));
writeToTargetDocument(target, entity, accessor, idProperty, versionProperty, prefixes, suffixes, idAttributes);
if (idProperty != null && target.getId() == null) {
String id = accessor.getProperty(idProperty, String.class);
if (idProperty.isAnnotationPresent(GeneratedValue.class) && (id == null || id.equals(""))) {
generatedValueInfo = idProperty.findAnnotation(GeneratedValue.class);
String generatedId = generateId(generatedValueInfo, prefixes, suffixes, idAttributes);
target.setId(generatedId);
// this is not effective if id is Immutable, and accessor.setProperty() returns a new object in getBean()
accessor.setProperty(idProperty, generatedId);
} else {
target.setId(id);
}
}
entity.doWithAssociations(new AssociationHandler<CouchbasePersistentProperty>() {
@Override
public void doWithAssociation(final Association<CouchbasePersistentProperty> association) {
CouchbasePersistentProperty inverseProp = association.getInverse();
Class<?> type = inverseProp.getType();
Object propertyObj = accessor.getProperty(inverseProp, type);
if (null != propertyObj) {
writePropertyInternal(propertyObj, target, inverseProp, false);
}
}
});
}
private void writeToTargetDocument(final CouchbaseDocument target, final CouchbasePersistentEntity<?> entity,
final ConvertingPropertyAccessor<Object> accessor, final CouchbasePersistentProperty idProperty,
final CouchbasePersistentProperty versionProperty, final TreeMap<Integer, String> prefixes,
final TreeMap<Integer, String> suffixes, final TreeMap<Integer, String> idAttributes) {
entity.doWithProperties(new PropertyHandler<CouchbasePersistentProperty>() {
@Override
public void doWithPersistentProperty(final CouchbasePersistentProperty prop) {
@@ -550,32 +583,6 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
}
}
});
if (idProperty != null && target.getId() == null) {
String id = accessor.getProperty(idProperty, String.class);
if (idProperty.isAnnotationPresent(GeneratedValue.class) && (id == null || id.equals(""))) {
generatedValueInfo = idProperty.findAnnotation(GeneratedValue.class);
String generatedId = generateId(generatedValueInfo, prefixes, suffixes, idAttributes);
target.setId(generatedId);
// this is not effective if id is Immutable, and accessor.setProperty() returns a new object in getBean()
accessor.setProperty(idProperty, generatedId);
} else {
target.setId(id);
}
}
entity.doWithAssociations(new AssociationHandler<CouchbasePersistentProperty>() {
@Override
public void doWithAssociation(final Association<CouchbasePersistentProperty> association) {
CouchbasePersistentProperty inverseProp = association.getInverse();
Class<?> type = inverseProp.getType();
Object propertyObj = accessor.getProperty(inverseProp, type);
if (null != propertyObj) {
writePropertyInternal(propertyObj, target, inverseProp, false);
}
}
});
}
/**

View File

@@ -22,12 +22,13 @@ import java.util.Collections;
import java.util.Formatter;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import com.couchbase.client.core.error.CouchbaseException;
import org.springframework.data.couchbase.core.convert.CouchbaseConverter;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import com.couchbase.client.core.error.CouchbaseException;
import com.couchbase.client.core.error.InvalidArgumentException;
import com.couchbase.client.java.json.JsonArray;
import com.couchbase.client.java.json.JsonObject;
@@ -158,7 +159,7 @@ public class QueryCriteria implements QueryCriteriaDefinition {
QueryCriteria qc = wrap(criteria);
newThis.criteriaChain.add(qc);
qc.setChainOperator(ChainOperator.AND);
newThis.chainOperator = ChainOperator.AND;// otherwise we get "A chain operator must be present when chaining"
newThis.chainOperator = ChainOperator.AND;// otherwise we get "A chain operator must be present when chaining"
return newThis;
}
@@ -200,54 +201,92 @@ public class QueryCriteria implements QueryCriteriaDefinition {
qc.criteriaChain = newThis.criteriaChain;
newThis.criteriaChain.add(qc);
qc.setChainOperator(ChainOperator.OR);
newThis.chainOperator = ChainOperator.OR;// otherwise we get "A chain operator must be present when chaining"
newThis.chainOperator = ChainOperator.OR;// otherwise we get "A chain operator must be present when chaining"
return newThis;
}
public QueryCriteria eq(@Nullable Object o) {
return is(o);
return eq(false, o);
}
public QueryCriteria eq(boolean ignoreCase, @Nullable Object o) {
return is(ignoreCase, o);
}
public QueryCriteria is(@Nullable Object o) {
return is(false, o);
};
public QueryCriteria is(boolean ignoreCase, @Nullable Object o) {
operator = "=";
value = new Object[] { o };
format = ignoreCase ? "lower(%1$s) %2$s %3$s" : "%1$s %2$s %3$s";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
return this;
}
public QueryCriteria ne(@Nullable Object o) {
return ne(false, o);
}
public QueryCriteria ne(boolean ignoreCase, @Nullable Object o) {
operator = "!=";
value = new Object[] { o };
format = ignoreCase ? "lower(%1$s) %2$s %3$s" : "%1$s %2$s %3$s";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
return this;
}
public QueryCriteria lt(@Nullable Object o) {
return lt(false, o);
}
public QueryCriteria lt(boolean ignoreCase, @Nullable Object o) {
operator = "<";
value = new Object[] { o };
format = ignoreCase ? "lower(%1$s) %2$s %3$s" : "%1$s %2$s %3$s";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
return this;
}
public QueryCriteria lte(@Nullable Object o) {
return lte(false, o);
}
public QueryCriteria lte(boolean ignoreCase, @Nullable Object o) {
operator = "<=";
value = new Object[] { o };
format = ignoreCase ? "lower(%1$s) %2$s %3$s" : "%1$s %2$s %3$s";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
return this;
}
public QueryCriteria gt(@Nullable Object o) {
return gt(false, o);
}
public QueryCriteria gt(boolean ignoreCase, @Nullable Object o) {
operator = ">";
value = new Object[] { o };
format = ignoreCase ? "lower(%1$s) %2$s %3$s" : "%1$s %2$s %3$s";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
return this;
}
public QueryCriteria gte(@Nullable Object o) {
return gte(false, o);
}
public QueryCriteria gte(boolean ignoreCase, @Nullable Object o) {
operator = ">=";
value = new Object[] { o };
format = ignoreCase ? "lower(%1$s) %2$s %3$s" : "%1$s %2$s %3$s";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
return this;
}
public QueryCriteria startingWith(@Nullable Object o) {
operator = "STARTING_WITH";
value = new Object[] { o };
format = "%1$s like (%3$s||\"%%\")";
return startingWith(false, o);
}
public QueryCriteria startingWith(boolean ignoreCase, @Nullable Object o) {
operator = "like";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
format = ignoreCase ? "lower(%1$s) %2$s (%3$s||\"%%\")" : "%1$s %2$s (%3$s||\"%%\")";
return this;
}
@@ -259,9 +298,13 @@ public class QueryCriteria implements QueryCriteriaDefinition {
}
public QueryCriteria endingWith(@Nullable Object o) {
operator = "ENDING_WITH";
value = new Object[] { o };
format = "%1$s like (\"%%\"||%3$s)";
return endingWith(false, o);
}
public QueryCriteria endingWith(boolean ignoreCase, @Nullable Object o) {
operator = "like";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
format = ignoreCase ? "lower(%1$s) %2$s (\"%%\"||%3$s)" : "%1$s %2$s (\"%%\"||%3$s)";
return this;
}
@@ -273,23 +316,38 @@ public class QueryCriteria implements QueryCriteriaDefinition {
}
public QueryCriteria containing(@Nullable Object o) {
operator = "CONTAINS";
value = new Object[] { o };
format = "contains(%1$s, %3$s)";
return containing(false, o);
}
public QueryCriteria containing(boolean ignoreCase, @Nullable Object o) {
operator = "contains";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
format = ignoreCase ? "%2$s(lower(%1$s), %3$s)" : "%2$s(%1$s, %3$s)";
return this;
}
public QueryCriteria arrayContaining(@Nullable Object o) {
operator = "ARRAY_CONTAINING";
return arrayContaining(false, o);
}
public QueryCriteria arrayContaining(boolean ignoreCase, @Nullable Object o) {
operator = "array_containing";
if (ignoreCase) {
throw new CouchbaseException("ignoreCase not supported in IN and NOT_IN");
}
value = new Object[] { o };
format = "array_containing(%1$s, %3$s)";
return this;
}
public QueryCriteria notContaining(@Nullable Object o) {
replaceThisAsWrapperOf(containing(o));
operator = "NOT";
format = "not( %3$s )";
return notContaining(false, o);
}
public QueryCriteria notContaining(boolean ignoreCase, @Nullable Object o) {
replaceThisAsWrapperOf(containing(ignoreCase, o));
operator = "not";
format = "%2$s %3$s";
return this;
}
@@ -297,8 +355,6 @@ public class QueryCriteria implements QueryCriteriaDefinition {
replaceThisAsWrapperOf(this);
operator = "NOT";
format = "not( %3$s )";
// criteriaChain = new LinkedList<>();
// criteriaChain.add(this);
return this;
}
@@ -310,16 +366,23 @@ public class QueryCriteria implements QueryCriteriaDefinition {
}
public QueryCriteria like(@Nullable Object o) {
operator = "LIKE";
value = new Object[] { o };
format = "%1$s like %3$s";
return like(false, o);
}
public QueryCriteria like(boolean ignoreCase, @Nullable Object o) {
operator = "like";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
format = ignoreCase ? "lower(%1$s) %2$s %3$s" : "%1$s %2$s %3$s";
return this;
}
public QueryCriteria notLike(@Nullable Object o) {
operator = "NOTLIKE";
value = new Object[] { o };
format = "not(%1$s like %3$s)";
return notLike(false, o);
}
public QueryCriteria notLike(boolean ignoreCase, @Nullable Object o) {
replaceThisAsWrapperOf(like(ignoreCase, o));
this.negate();
return this;
}
@@ -365,23 +428,35 @@ public class QueryCriteria implements QueryCriteriaDefinition {
return this;
}
public QueryCriteria within(@Nullable Object o) {
operator = "WITHIN";
value = new Object[] { o };
format = "%1$s within %3$s";
public QueryCriteria within(boolean ignoreCase, @Nullable Object o) {
operator = "within";
value = new Object[] { ignoreCase ? o.toString().toLowerCase(Locale.ROOT) : o };
format = ignoreCase ? "lower(%1$s) %2$s %3$s" : "%1$s %2$s %3$s";
return this;
}
public QueryCriteria between(@Nullable Object o1, @Nullable Object o2) {
return between(false, o1, o2);
}
public QueryCriteria between(boolean ignoreCase, @Nullable Object o1, @Nullable Object o2) {
operator = "BETWEEN";
value = new Object[] { o1, o2 };
format = "%1$s between %3$s and %4$s";
value = new Object[] { ignoreCase ? o1.toString().toLowerCase(Locale.ROOT) : o1,
ignoreCase ? o2.toString().toLowerCase(Locale.ROOT) : o2 };
format = ignoreCase ? "lower(%1$s) between %3$s and %4$s" : "%1$s between %3$s and %4$s";
return this;
}
public QueryCriteria in(@Nullable Object... o) {
operator = "IN";
format = "%1$s in %3$s";
return in(false, o);
}
public QueryCriteria in(boolean ignoreCase, @Nullable Object... o) {
operator = "in";
if (ignoreCase) {
throw new CouchbaseException("ignoreCase not supported in IN and NOT_IN");
}
format = "%1$s %2$s %3$s";
value = new Object[1];
if (o.length > 0) {
if (o[0] instanceof JsonArray || o[0] instanceof List || o[0] instanceof Object[]) {
@@ -405,7 +480,7 @@ public class QueryCriteria implements QueryCriteriaDefinition {
} else {
// see QueryCriteriaTests.testNestedNotIn() - if arg to notIn is not cast to Object
// notIn((Object) new String[] { "Alabama", "Florida" }));
throw new CouchbaseException("unhandled parameters "+o);
throw new CouchbaseException("unhandled parameters " + o);
}
}
@@ -414,7 +489,11 @@ public class QueryCriteria implements QueryCriteriaDefinition {
}
public QueryCriteria notIn(@Nullable Object... o) {
return in(o).negate();
return in(false, o).negate();
}
public QueryCriteria notIn(boolean ignoreCase, @Nullable Object... o) {
return in(ignoreCase, o).negate();
}
public QueryCriteria TRUE() { // true/false are reserved, use TRUE/FALSE

View File

@@ -30,6 +30,7 @@ import org.springframework.data.couchbase.core.mapping.CouchbasePersistentProper
import org.springframework.data.couchbase.core.query.N1QLExpression;
import org.springframework.data.couchbase.core.query.Query;
import org.springframework.data.couchbase.core.query.QueryCriteria;
import org.springframework.data.couchbase.repository.query.support.N1qlUtils;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.PersistentEntity;
@@ -126,7 +127,25 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
private QueryCriteria from(final Part part, final CouchbasePersistentProperty property, final QueryCriteria criteria,
final Iterator<Object> parameters) {
// deal with ignore case
PersistentPropertyPath<CouchbasePersistentProperty> path = N1qlUtils.getPathWithAlternativeFieldNames(converter,
part.getProperty());
// get the whole doted path with fieldNames instead of potentially wrong propNames
String fieldNamePath = N1qlUtils.getDottedPathWithAlternativeFieldNames(path);
boolean ignoreCase = false;
Class<?> leafType = converter.getWriteClassFor(path.getLeafProperty().getType());
boolean isString = leafType == String.class;
if (part.shouldIgnoreCase() == Part.IgnoreCaseType.WHEN_POSSIBLE) {
ignoreCase = isString;
} else if (part.shouldIgnoreCase() == Part.IgnoreCaseType.ALWAYS) {
if (!isString) {
throw new IllegalArgumentException(
String.format("Part %s must be of type String but was %s", fieldNamePath, leafType));
}
ignoreCase = true;
}
final Part.Type type = part.getType();
/*
NEAR(new String[]{"IsNear", "Near"}),
@@ -134,32 +153,32 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
switch (type) {
case GREATER_THAN:
case AFTER:
return criteria.gt(parameters.next());
return criteria.gt(ignoreCase, parameters.next());
case GREATER_THAN_EQUAL:
return criteria.gte(parameters.next());
return criteria.gte(ignoreCase, parameters.next());
case LESS_THAN:
case BEFORE:
return criteria.lt(parameters.next());
return criteria.lt(ignoreCase, parameters.next());
case LESS_THAN_EQUAL:
return criteria.lte(parameters.next());
return criteria.lte(ignoreCase, parameters.next());
case SIMPLE_PROPERTY:
return criteria.eq(parameters.next());
return criteria.eq(ignoreCase, parameters.next());
case NEGATING_SIMPLE_PROPERTY:
return criteria.ne(parameters.next());
return criteria.ne(ignoreCase, parameters.next());
case CONTAINING:
return criteria.containing(parameters.next());
return criteria.containing(ignoreCase, parameters.next());
case NOT_CONTAINING:
return criteria.notContaining(parameters.next());
return criteria.notContaining(ignoreCase, parameters.next());
case STARTING_WITH:
return criteria.startingWith(parameters.next());
return criteria.startingWith(ignoreCase, parameters.next());
case ENDING_WITH:
return criteria.endingWith(parameters.next());
return criteria.endingWith(ignoreCase, parameters.next());
case LIKE:
return criteria.like(parameters.next());
return criteria.like(ignoreCase, parameters.next());
case NOT_LIKE:
return criteria.notLike(parameters.next());
return criteria.notLike(ignoreCase, parameters.next());
case WITHIN:
return criteria.within(parameters.next());
return criteria.within(ignoreCase, parameters.next());
case IS_NULL:
return criteria.isNull(/*parameters.next()*/);
case IS_NOT_NULL:
@@ -173,11 +192,11 @@ public class N1qlQueryCreator extends AbstractQueryCreator<Query, QueryCriteria>
case REGEX:
return criteria.regex(parameters.next());
case BETWEEN:
return criteria.between(parameters.next(), parameters.next());
return criteria.between(ignoreCase, parameters.next(), parameters.next());
case IN:
return criteria.in(parameters.next());
return criteria.in(ignoreCase, new Object[] { parameters.next() });
case NOT_IN:
return criteria.notIn(parameters.next());
return criteria.notIn(ignoreCase, new Object[] { parameters.next() });
case TRUE:
return criteria.TRUE();
case FALSE:

View File

@@ -174,7 +174,7 @@ class QueryCriteriaTests {
@Test
void testNotContaining() {
QueryCriteria c = where(i("name")).notContaining("Elvis");
assertEquals("not( (contains(`name`, \"Elvis\")) )", c.export());
assertEquals("not (contains(`name`, \"Elvis\"))", c.export());
}
@Test
@@ -192,7 +192,7 @@ class QueryCriteriaTests {
@Test
void testNotLike() {
QueryCriteria c = where(i("name")).notLike("%Elvis%");
assertEquals("not(`name` like \"%Elvis%\")", c.export());
assertEquals("not( ( (`name` like \"%Elvis%\")) )", c.export());
}
@Test

View File

@@ -43,6 +43,8 @@ public interface UserRepository extends CouchbaseRepository<User, String> {
List<User> findByFirstname(String firstname);
List<User> findByFirstnameIgnoreCase(String firstname);
Stream<User> findByLastname(String lastname);
List<User> findByFirstnameIn(String... firstnames);

View File

@@ -255,7 +255,7 @@ public class CouchbaseRepositoryQuerydslIntegrationTests extends JavaIntegration
.filter(a -> a.getName().toUpperCase().startsWith(united.getName().toUpperCase().substring(0, 5)))
.toArray(Airline[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE UPPER(name) like ($1||\"%\")", bq(predicate));
assertEquals(" WHERE lower(name) like ($1||\"%\")", bq(predicate));
}
}
@@ -281,7 +281,7 @@ public class CouchbaseRepositoryQuerydslIntegrationTests extends JavaIntegration
.filter(a -> a.getName().toUpperCase().endsWith(united.getName().toUpperCase().substring(1)))
.toArray(Airline[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE UPPER(name) like (\"%\"||$1)", bq(predicate));
assertEquals(" WHERE lower(name) like (\"%\"||$1)", bq(predicate));
}
}
@@ -293,7 +293,7 @@ public class CouchbaseRepositoryQuerydslIntegrationTests extends JavaIntegration
assertNull(comprises(result,
Arrays.stream(saved).filter(a -> a.getName().equalsIgnoreCase(flyByNight.getName())).toArray(Airline[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE UPPER(name) = $1", bq(predicate));
assertEquals(" WHERE lower(name) = $1", bq(predicate));
}
{
BooleanExpression predicate = airline.name.equalsIgnoreCase(united.getName());
@@ -302,7 +302,7 @@ public class CouchbaseRepositoryQuerydslIntegrationTests extends JavaIntegration
comprises(result,
Arrays.stream(saved).filter(a -> a.getName().equalsIgnoreCase(united.getName())).toArray(Airline[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE UPPER(name) = $1", bq(predicate));
assertEquals(" WHERE lower(name) = $1", bq(predicate));
}
}
@@ -329,7 +329,7 @@ public class CouchbaseRepositoryQuerydslIntegrationTests extends JavaIntegration
.filter(a -> a.getName().toUpperCase(Locale.ROOT).contains("united".toUpperCase(Locale.ROOT)))
.toArray(Airline[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE contains(UPPER(name), $1)", bq(predicate));
assertEquals(" WHERE contains(lower(name), $1)", bq(predicate));
}
}
@@ -356,7 +356,7 @@ public class CouchbaseRepositoryQuerydslIntegrationTests extends JavaIntegration
.filter(a -> a.getName().toUpperCase(Locale.ROOT).endsWith("Airlines".toUpperCase(Locale.ROOT)))
.toArray(Airline[]::new)),
"[unexpected] -> [missing]");
assertEquals(" WHERE UPPER(name) like $1", bq(predicate));
assertEquals(" WHERE lower(name) like $1", bq(predicate));
}
}

View File

@@ -23,6 +23,7 @@ import static org.springframework.data.couchbase.core.query.QueryCriteria.where;
import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -81,6 +82,21 @@ class N1qlQueryCreatorTests {
assertEquals(query.export(), " WHERE " + where(i("firstname")).is("Oliver").export());
}
@Test
void createsQueryCorrectlyIgnoreCase() throws Exception {
String input = "findByFirstnameIgnoreCase";
PartTree tree = new PartTree(input, User.class);
Method method = UserRepository.class.getMethod(input, String.class);
QueryMethod queryMethod = new QueryMethod(method, new DefaultRepositoryMetadata(UserRepository.class),
new SpelAwareProxyProjectionFactory());
N1qlQueryCreator creator = new N1qlQueryCreator(tree, getAccessor(getParameters(method), "Oliver"), queryMethod,
converter, bucketName);
Query query = creator.createQuery();
assertEquals(query.export(),
" WHERE " + where("lower(" + i("firstname") + ")").is("Oliver".toLowerCase(Locale.ROOT)).export());
}
@Test
void createsQueryFieldAnnotationCorrectly() throws Exception {
String input = "findByMiddlename";