From 81f2c910f739e334efb0d0b168bb33f5866f423a Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 11 Nov 2014 12:49:56 +0100 Subject: [PATCH] DATAMONGO-1075 - Containing keyword is now correctly translated for collection properties. We now inspect the properties type when creating criteria for CONTAINS keyword so that, if the target property is of type String, we use an expression, and if the property is collection like we try to finds an exact match within the collection using $in. Added support for NotContaining along the way. Original pull request: #241. --- .../repository/query/MongoQueryCreator.java | 29 ++++++++++++- .../query/MongoQueryCreatorUnitTests.java | 43 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) 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 16b2af801..3085f5dc8 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 @@ -1,5 +1,5 @@ /* - * Copyright 2010-2013 the original author or authors. + * Copyright 2010-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,6 +46,7 @@ import org.springframework.util.Assert; * * @author Oliver Gierke * @author Thomas Darimont + * @author Christoph Strobl */ class MongoQueryCreator extends AbstractQueryCreator { @@ -190,7 +191,9 @@ class MongoQueryCreator extends AbstractQueryCreator { case STARTING_WITH: case ENDING_WITH: case CONTAINING: - return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString()); + return createContainingCriteria(part, property, criteria, parameters); + case NOT_CONTAINING: + return createContainingCriteria(part, property, criteria, parameters).not(); case REGEX: return criteria.regex(parameters.next().toString()); case EXISTS: @@ -284,6 +287,27 @@ class MongoQueryCreator extends AbstractQueryCreator { Arrays.asList(IgnoreCaseType.ALWAYS, IgnoreCaseType.WHEN_POSSIBLE), part.shouldIgnoreCase())); } + /** + * If the target property of the comparison is of type String, then the operator checks for match using regular + * expression. If the target property of the comparison is a {@link Collection} then the operator evaluates to true if + * it finds an exact match within any member of the {@link Collection}. + * + * @param part + * @param property + * @param criteria + * @param parameters + * @return + */ + private Criteria createContainingCriteria(Part part, MongoPersistentProperty property, Criteria criteria, + PotentiallyConvertingIterator parameters) { + + if (property.isCollectionLike()) { + return criteria.in(nextAsArray(parameters, property)); + } + + return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString()); + } + /** * Creates an appropriate like-regex and appends it to the given criteria. * @@ -357,6 +381,7 @@ class MongoQueryCreator extends AbstractQueryCreator { source = source + "$"; break; case CONTAINING: + case NOT_CONTAINING: source = "*" + source + "*"; break; case SIMPLE_PROPERTY: 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 a6e90fd05..b7181341e 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 @@ -425,6 +425,47 @@ public class MongoQueryCreatorUnitTests { assertThat(query, is(query(where("firstName").regex("^dave$", "i").and("age").is(42)))); } + /** + * @see DATAMONGO-1075 + */ + @Test + public void shouldCreateInClauseWhenUsingContainsOnCollectionLikeProperty() { + + PartTree tree = new PartTree("findByEmailAddressesContaining", User.class); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); + + Query query = creator.createQuery(); + + assertThat(query, is(query(where("emailAddresses").in("dave")))); + } + + /** + * @see DATAMONGO-1075 + */ + @Test + public void shouldCreateInClauseWhenUsingNotContainsOnCollectionLikeProperty() { + + PartTree tree = new PartTree("findByEmailAddressesNotContaining", User.class); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "dave"), context); + + Query query = creator.createQuery(); + + assertThat(query, is(query(where("emailAddresses").not().in("dave")))); + } + + /** + * @see DATAMONGO-1075 + */ + @Test + public void shouldCreateRegexWhenUsingNotContainsOnStringProperty() { + + PartTree tree = new PartTree("findByUsernameNotContaining", User.class); + MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context); + Query query = creator.createQuery(); + + assertThat(query, is(query(where("username").regex(".*thew.*").not()))); + } + interface PersonRepository extends Repository { List findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname); @@ -435,5 +476,7 @@ public class MongoQueryCreatorUnitTests { @Field("foo") String username; @DBRef User creator; + + List emailAddresses; } }