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);
}
}