DATAMONGO-2149 - Fix $slice in fields projection when pointing to array of DBRefs.

We now no longer try to convert the actual slice parameters into a DBRef.

Original pull request: #623.
This commit is contained in:
Christoph Strobl
2018-11-29 12:58:10 +01:00
committed by Mark Paluch
parent a15d488657
commit 7002cd1456
4 changed files with 105 additions and 1 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core.convert;
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;
@@ -290,7 +291,7 @@ public class QueryMapper {
*/
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)
@@ -610,10 +611,12 @@ public class QueryMapper {
static class Keyword {
private static final String N_OR_PATTERN = "\\$.*or";
private static final Set<String> 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);
@@ -674,6 +677,15 @@ public class QueryMapper {
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.
*

View File

@@ -27,13 +27,16 @@ import java.util.Arrays;
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.geo.Polygon;
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 abstract class AbstractPersonRepositoryIntegrationTests {
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<User> 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<Address> 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<User> 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);
}
}

View File

@@ -356,4 +356,10 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
List<Person> findByAgeGreaterThan(int age, Sort sort);
List<Person> 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);
}

View File

@@ -603,6 +603,18 @@ public class StringBasedMongoQueryUnitTests {
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 @@ public class StringBasedMongoQueryUnitTests {
@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);
}
}