Skip to content

Commit 7002cd1

Browse files
christophstroblmp911de
authored andcommitted
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.
1 parent a15d488 commit 7002cd1

File tree

4 files changed

+105
-1
lines changed

4 files changed

+105
-1
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java

+13-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import java.util.ArrayList;
1919
import java.util.Arrays;
2020
import java.util.Collections;
21+
import java.util.HashSet;
2122
import java.util.Iterator;
2223
import java.util.List;
2324
import java.util.Map.Entry;
@@ -290,7 +291,7 @@ protected Document getMappedKeyword(Keyword keyword, @Nullable MongoPersistentEn
290291
*/
291292
protected Document getMappedKeyword(Field property, Keyword keyword) {
292293

293-
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists();
294+
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists() && keyword.mayHoldDbRef();
294295
Object value = keyword.getValue();
295296

296297
Object convertedValue = needsAssociationConversion ? convertAssociation(value, property)
@@ -610,10 +611,12 @@ protected boolean isKeyword(String candidate) {
610611
static class Keyword {
611612

612613
private static final String N_OR_PATTERN = "\\$.*or";
614+
private static final Set<String> NON_DBREF_CONVERTING_KEYWORDS = new HashSet<>(Arrays.asList("$", "$size", "$slice", "$gt", "$lt"));
613615

614616
private final String key;
615617
private final Object value;
616618

619+
617620
public Keyword(Bson source, String key) {
618621
this.key = key;
619622
this.value = BsonUtils.get(source, key);
@@ -674,6 +677,15 @@ public <T> T getValue() {
674677
return (T) value;
675678
}
676679

680+
/**
681+
*
682+
* @return {@literal true} if key may hold a DbRef.
683+
* @since 2.0.13
684+
*/
685+
public boolean mayHoldDbRef() {
686+
return !NON_DBREF_CONVERTING_KEYWORDS.contains(key);
687+
}
688+
677689
/**
678690
* Returns whether the current keyword indicates a {@literal $jsonSchema} object.
679691
*

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java

+71
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@
2727
import java.util.HashSet;
2828
import java.util.List;
2929
import java.util.Optional;
30+
import java.util.Set;
3031
import java.util.UUID;
3132
import java.util.regex.Pattern;
3233
import java.util.stream.Collectors;
34+
import java.util.stream.IntStream;
3335
import java.util.stream.Stream;
3436

3537
import org.hamcrest.Matchers;
3638
import org.junit.Before;
39+
import org.junit.Ignore;
3740
import org.junit.Rule;
3841
import org.junit.Test;
3942
import org.junit.rules.ExpectedException;
@@ -60,6 +63,7 @@
6063
import org.springframework.data.mongodb.core.MongoOperations;
6164
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
6265
import org.springframework.data.mongodb.core.query.BasicQuery;
66+
import org.springframework.data.mongodb.core.query.Query;
6367
import org.springframework.data.mongodb.repository.Person.Sex;
6468
import org.springframework.data.mongodb.repository.SampleEvaluationContextExtension.SampleSecurityContextHolder;
6569
import org.springframework.data.querydsl.QSort;
@@ -1231,4 +1235,71 @@ public void findByRegexWithPatternAndOptions() {
12311235
assertThat(repository.findByFirstnameRegex(Pattern.compile(fn))).hasSize(0);
12321236
assertThat(repository.findByFirstnameRegex(Pattern.compile(fn, Pattern.CASE_INSENSITIVE))).hasSize(1);
12331237
}
1238+
1239+
@Test // DATAMONGO-2149
1240+
public void annotatedQueryShouldAllowSliceInFieldsProjectionWithDbRef() {
1241+
1242+
operations.remove(new Query(), User.class);
1243+
1244+
List<User> users = IntStream.range(0, 10).mapToObj(it -> {
1245+
1246+
User user = new User();
1247+
user.id = "id" + it;
1248+
user.username = "user" + it;
1249+
1250+
return user;
1251+
}).collect(Collectors.toList());
1252+
1253+
users.forEach(operations::save);
1254+
1255+
alicia.fans = new ArrayList<>(users);
1256+
operations.save(alicia);
1257+
1258+
Person target = repository.findWithSliceInProjection(alicia.getId(), 0, 5);
1259+
assertThat(target.getFans().size()).isEqualTo(5);
1260+
}
1261+
1262+
@Test // DATAMONGO-2149
1263+
public void annotatedQueryShouldAllowPositionalParameterInFieldsProjection() {
1264+
1265+
Set<Address> addressList = IntStream.range(0, 10).mapToObj(it -> new Address("street-" + it, "zip", "lnz"))
1266+
.collect(Collectors.toSet());
1267+
1268+
alicia.setShippingAddresses(addressList);
1269+
operations.save(alicia);
1270+
1271+
Person target = repository.findWithArrayPositionInProjection(1);
1272+
1273+
assertThat(target).isNotNull();
1274+
assertThat(target.getShippingAddresses()).hasSize(1);
1275+
}
1276+
1277+
@Test // DATAMONGO-2149
1278+
@Ignore("This one fails due to Json parse exception within MongoDB")
1279+
public void annotatedQueryShouldAllowPositionalParameterInFieldsProjectionWithDbRef() {
1280+
1281+
// the following needs to be added to PersonRepository.
1282+
1283+
// @Query(value = "{ 'fans' : { '$elemMatch' : { '$ref' : 'user' } } }", fields = "{ 'fans.$': ?0 }")
1284+
// Person findWithArrayPositionInProjectionWithDbRef(int position);
1285+
1286+
List<User> userList = IntStream.range(0, 10).mapToObj(it -> {
1287+
1288+
User user = new User();
1289+
user.id = "" + it;
1290+
user.username = "user" + it;
1291+
1292+
return user;
1293+
}).collect(Collectors.toList());
1294+
1295+
userList.forEach(operations::save);
1296+
1297+
alicia.setFans(userList);
1298+
operations.save(alicia);
1299+
1300+
// Person target = repository.findWithArrayPositionInProjectionWithDbRef(1);
1301+
//
1302+
// assertThat(target).isNotNull();
1303+
// assertThat(target.getShippingAddresses()).hasSize(1);
1304+
}
12341305
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java

+6
Original file line numberDiff line numberDiff line change
@@ -356,4 +356,10 @@ Page<Person> findByCustomQueryLastnameAndAddressStreetInList(String lastname, Li
356356
List<Person> findByAgeGreaterThan(int age, Sort sort);
357357

358358
List<Person> findByFirstnameRegex(Pattern pattern);
359+
360+
@Query(value = "{ 'id' : ?0 }", fields = "{ 'fans': { '$slice': [ ?1, ?2 ] } }")
361+
Person findWithSliceInProjection(String id, int skip, int limit);
362+
363+
@Query(value = "{ 'shippingAddresses' : { '$elemMatch' : { 'city' : { '$eq' : 'lnz' } } } }", fields = "{ 'shippingAddresses.$': ?0 }")
364+
Person findWithArrayPositionInProjection(int position);
359365
}

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java

+15
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,18 @@ public void spelShouldIgnoreJsonParseErrorsForRegex() {
603603
is(new BasicQuery("{lastname: {$regex: 'Chandler'}}").getQueryObject().toJson()));
604604
}
605605

606+
@Test // DATAMONGO-2149
607+
public void shouldParseFieldsProjectionWithSliceCorrectly() {
608+
609+
StringBasedMongoQuery mongoQuery = createQueryForMethod("findWithSliceInProjection", String.class, int.class,
610+
int.class);
611+
ConvertingParameterAccessor accessor = StubParameterAccessor.getAccessor(converter, "Bruce Banner", 0, 5);
612+
613+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(accessor);
614+
615+
assertThat(query.getFieldsObject(), is(equalTo(Document.parse("{ \"fans\" : { \"$slice\" : [0, 5] } }"))));
616+
}
617+
606618
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) {
607619

608620
try {
@@ -718,6 +730,9 @@ private interface SampleRepository extends Repository<Person, Long> {
718730

719731
@Query("{ 'lastname' : { '$regex' : ?#{[0].lastname} } }")
720732
Person findByPersonLastnameRegex(Person key);
733+
734+
@Query(value = "{ 'id' : ?0 }", fields = "{ 'fans': { '$slice': [ ?1, ?2 ] } }")
735+
Person findWithSliceInProjection(String id, int skip, int limit);
721736
}
722737

723738
}

0 commit comments

Comments
 (0)