From aecfd45968c5e6622c00e458ee4437ddf1493026 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 13 Jun 2023 08:26:43 +0200 Subject: [PATCH] Use exact matching for IN clause with ignore case. Prior to this change the generated pattern would have matched more entries than it should have. The behavior is now aligned to its counterpart not using the IgnoreCase flag. Closes #4404 Original pull request: #4412 --- .../mongodb/core/query/MongoRegexCreator.java | 2 ++ .../repository/query/MongoQueryCreator.java | 16 ++++++++++++++-- ...AbstractPersonRepositoryIntegrationTests.java | 9 ++++++++- .../query/MongoQueryCreatorUnitTests.java | 12 ++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java index 23511d760..f77cf9c88 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/MongoRegexCreator.java @@ -107,7 +107,9 @@ public enum MongoRegexCreator { * @param source * @return * @since 2.2.14 + * @deprecated since 4.1.1 */ + @Deprecated(since = "4.1.1", forRemoval = true) public Object toCaseInsensitiveMatch(Object source) { return source instanceof String stringValue ? new BsonRegularExpression(Pattern.quote(stringValue), "i") : source; } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java index f9cc596e6..b4f903b22 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java @@ -25,7 +25,7 @@ import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; - +import org.bson.BsonRegularExpression; import org.springframework.data.domain.Range; import org.springframework.data.domain.Range.Bound; import org.springframework.data.domain.Sort; @@ -390,7 +390,18 @@ class MongoQueryCreator extends AbstractQueryCreator { Streamable streamable = asStreamable(iterator.next()); if (!isSimpleComparisionPossible(part)) { - streamable = streamable.map(MongoRegexCreator.INSTANCE::toCaseInsensitiveMatch); + + MatchMode matchMode = toMatchMode(part.getType()); + String regexOptions = toRegexOptions(part); + + streamable = streamable.map(it -> { + if (it instanceof String value) { + + return new BsonRegularExpression(MongoRegexCreator.INSTANCE.toRegularExpression(value, matchMode), + regexOptions); + } + return it; + }); } return streamable.toList(); @@ -481,6 +492,7 @@ class MongoQueryCreator extends AbstractQueryCreator { return MatchMode.REGEX; case NEGATING_SIMPLE_PROPERTY: case SIMPLE_PROPERTY: + case IN: return MatchMode.EXACT; default: return MatchMode.DEFAULT; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java index 646a268e0..ac278c8ba 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java @@ -1510,9 +1510,16 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie assertThat(result.get(0).getId().equals(bart.getId())); } - @Test // GH-3395 + @Test // GH-3395, GH-4404 void caseInSensitiveInClause() { + assertThat(repository.findByLastnameIgnoreCaseIn("bEAuFoRd", "maTTheWs")).hasSize(3); + + repository.save(new Person("the-first", "The First")); + repository.save(new Person("the-first-one", "The First One")); + repository.save(new Person("the-second", "The Second")); + + assertThat(repository.findByLastnameIgnoreCaseIn("tHE fIRsT")).hasSize(1); } @Test // GH-3395 diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java index f81ff21fe..158f69db7 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java @@ -25,6 +25,7 @@ import java.lang.reflect.Method; import java.util.List; import java.util.regex.Pattern; +import org.bson.BsonRegularExpression; import org.bson.Document; import org.bson.types.ObjectId; import org.junit.jupiter.api.BeforeEach; @@ -273,6 +274,17 @@ class MongoQueryCreatorUnitTests { assertThat(query).isEqualTo(query(where("firstName").regex("^dave$", "i"))); } + @Test // GH-4404 + void createsQueryWithFindByInClauseHavingIgnoreCaseCorrectly() { + + PartTree tree = new PartTree("findAllByFirstNameInIgnoreCase", Person.class); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, List.of("da've", "carter")), context); + + Query query = creator.createQuery(); + assertThat(query).isEqualTo(query(where("firstName") + .in(List.of(new BsonRegularExpression("^\\Qda've\\E$", "i"), new BsonRegularExpression("^carter$", "i"))))); + } + @Test // DATAMONGO-770 void createsQueryWithFindByNotIgnoreCaseCorrectly() {