diff --git a/pom.xml b/pom.xml index 28dc5c96c1..f3622b413f 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2149-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-benchmarks/pom.xml b/spring-data-mongodb-benchmarks/pom.xml index c2ff37b35c..b1b8224d23 100644 --- a/spring-data-mongodb-benchmarks/pom.xml +++ b/spring-data-mongodb-benchmarks/pom.xml @@ -7,7 +7,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2149-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index fd36f227c0..8e62f01c14 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2149-SNAPSHOT ../pom.xml @@ -50,7 +50,7 @@ org.springframework.data spring-data-mongodb - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2149-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index cb441dd8ef..0b3175f500 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2149-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index f3c85a046a..af0de50160 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 2.2.0.BUILD-SNAPSHOT + 2.2.0.DATAMONGO-2149-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java index fd6a18155c..f305d41127 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java @@ -18,6 +18,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; @@ -299,7 +300,7 @@ protected Document getMappedKeyword(Keyword keyword, @Nullable MongoPersistentEn */ protected Document getMappedKeyword(Field property, Keyword keyword) { - boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists(); + boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists() && keyword.mayHoldDbRef(); Object value = keyword.getValue(); Object convertedValue = needsAssociationConversion ? convertAssociation(value, property) @@ -634,10 +635,12 @@ protected boolean isKeyword(String candidate) { static class Keyword { private static final String N_OR_PATTERN = "\\$.*or"; + private static final Set NON_DBREF_CONVERTING_KEYWORDS = new HashSet<>(Arrays.asList("$", "$size", "$slice", "$gt", "$lt")); private final String key; private final Object value; + public Keyword(Bson source, String key) { this.key = key; this.value = BsonUtils.get(source, key); @@ -698,6 +701,15 @@ public T getValue() { return (T) value; } + /** + * + * @return {@literal true} if key may hold a DbRef. + * @since 2.0.13 + */ + public boolean mayHoldDbRef() { + return !NON_DBREF_CONVERTING_KEYWORDS.contains(key); + } + /** * Returns whether the current keyword indicates a {@literal $jsonSchema} object. * 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 23264fc238..6fb33ba392 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 @@ -27,13 +27,16 @@ import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import java.util.regex.Pattern; import java.util.stream.Collectors; +import java.util.stream.IntStream; import java.util.stream.Stream; import org.hamcrest.Matchers; import org.junit.Before; +import org.junit.Ignore; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; @@ -60,6 +63,7 @@ import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.data.mongodb.core.query.BasicQuery; +import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.repository.Person.Sex; import org.springframework.data.mongodb.repository.SampleEvaluationContextExtension.SampleSecurityContextHolder; import org.springframework.data.querydsl.QSort; @@ -1231,4 +1235,71 @@ public void findByRegexWithPatternAndOptions() { assertThat(repository.findByFirstnameRegex(Pattern.compile(fn))).hasSize(0); assertThat(repository.findByFirstnameRegex(Pattern.compile(fn, Pattern.CASE_INSENSITIVE))).hasSize(1); } + + @Test // DATAMONGO-2149 + public void annotatedQueryShouldAllowSliceInFieldsProjectionWithDbRef() { + + operations.remove(new Query(), User.class); + + List users = IntStream.range(0, 10).mapToObj(it -> { + + User user = new User(); + user.id = "id" + it; + user.username = "user" + it; + + return user; + }).collect(Collectors.toList()); + + users.forEach(operations::save); + + alicia.fans = new ArrayList<>(users); + operations.save(alicia); + + Person target = repository.findWithSliceInProjection(alicia.getId(), 0, 5); + assertThat(target.getFans().size()).isEqualTo(5); + } + + @Test // DATAMONGO-2149 + public void annotatedQueryShouldAllowPositionalParameterInFieldsProjection() { + + Set
addressList = IntStream.range(0, 10).mapToObj(it -> new Address("street-" + it, "zip", "lnz")) + .collect(Collectors.toSet()); + + alicia.setShippingAddresses(addressList); + operations.save(alicia); + + Person target = repository.findWithArrayPositionInProjection(1); + + assertThat(target).isNotNull(); + assertThat(target.getShippingAddresses()).hasSize(1); + } + + @Test // DATAMONGO-2149 + @Ignore("This one fails due to Json parse exception within MongoDB") + public void annotatedQueryShouldAllowPositionalParameterInFieldsProjectionWithDbRef() { + + // the following needs to be added to PersonRepository. + + // @Query(value = "{ 'fans' : { '$elemMatch' : { '$ref' : 'user' } } }", fields = "{ 'fans.$': ?0 }") + // Person findWithArrayPositionInProjectionWithDbRef(int position); + + List userList = IntStream.range(0, 10).mapToObj(it -> { + + User user = new User(); + user.id = "" + it; + user.username = "user" + it; + + return user; + }).collect(Collectors.toList()); + + userList.forEach(operations::save); + + alicia.setFans(userList); + operations.save(alicia); + + // Person target = repository.findWithArrayPositionInProjectionWithDbRef(1); + // + // assertThat(target).isNotNull(); + // assertThat(target.getShippingAddresses()).hasSize(1); + } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java index 4d1590ea34..e9e70ae623 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java @@ -356,4 +356,10 @@ Page findByCustomQueryLastnameAndAddressStreetInList(String lastname, Li List findByAgeGreaterThan(int age, Sort sort); List findByFirstnameRegex(Pattern pattern); + + @Query(value = "{ 'id' : ?0 }", fields = "{ 'fans': { '$slice': [ ?1, ?2 ] } }") + Person findWithSliceInProjection(String id, int skip, int limit); + + @Query(value = "{ 'shippingAddresses' : { '$elemMatch' : { 'city' : { '$eq' : 'lnz' } } } }", fields = "{ 'shippingAddresses.$': ?0 }") + Person findWithArrayPositionInProjection(int position); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java index 9f9db1ead6..fe6fe56022 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java @@ -603,6 +603,18 @@ public void spelShouldIgnoreJsonParseErrorsForRegex() { is(new BasicQuery("{lastname: {$regex: 'Chandler'}}").getQueryObject().toJson())); } + @Test // DATAMONGO-2149 + public void shouldParseFieldsProjectionWithSliceCorrectly() { + + StringBasedMongoQuery mongoQuery = createQueryForMethod("findWithSliceInProjection", String.class, int.class, + int.class); + ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Bruce Banner", 0, 5); + + org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor); + + assertThat(query.getFieldsObject(), is(equalTo(Document.parse("{ \"fans\" : { \"$slice\" : [0, 5] } }")))); + } + private StringBasedMongoQuery createQueryForMethod(String name, Class... parameters) { try { @@ -718,6 +730,9 @@ private interface SampleRepository extends Repository { @Query("{ 'lastname' : { '$regex' : ?#{[0].lastname} } }") Person findByPersonLastnameRegex(Person key); + + @Query(value = "{ 'id' : ?0 }", fields = "{ 'fans': { '$slice': [ ?1, ?2 ] } }") + Person findWithSliceInProjection(String id, int skip, int limit); } }