From 746199fb28bf41deac4d0ef8013ff7e40a954e5a 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 9a97f1df3..898861cef 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 3.4.13 */ + @Deprecated public Object toCaseInsensitiveMatch(Object source) { return source instanceof String ? new BsonRegularExpression(Pattern.quote((String) source), "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 3838fdf68..d5747b801 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; @@ -406,7 +406,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) { + + return new BsonRegularExpression(MongoRegexCreator.INSTANCE.toRegularExpression((String) it, matchMode), + regexOptions); + } + return it; + }); } return streamable.toList(); @@ -498,6 +509,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 97dec780d..f1d5d09f4 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 @@ -1451,9 +1451,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 efb20a86e..85484e01a 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; @@ -277,6 +278,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() {