Skip to content

Commit 7cb27c7

Browse files
Polishing.
Update tests to make use of ValueSource. Replace regex based path inspection with segment by segment analysis. Original Pull Request: #4427
1 parent 0cd082e commit 7cb27c7

File tree

2 files changed

+47
-35
lines changed

2 files changed

+47
-35
lines changed

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

+35-16
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,7 @@ public Class<?> getFieldType() {
10891089
protected static class MetadataBackedField extends Field {
10901090

10911091
private static final Pattern POSITIONAL_PARAMETER_PATTERN = Pattern.compile("\\.\\$(\\[.*?\\])?");
1092-
private static final Pattern DOT_POSITIONAL_PATTERN = Pattern.compile("\\.\\d+(?!$)");
1092+
private static final Pattern NUMERIC_SEGMENT = Pattern.compile("\\d+");
10931093
private static final String INVALID_ASSOCIATION_REFERENCE = "Invalid path reference %s; Associations can only be pointed to directly or via their id property";
10941094

10951095
private final MongoPersistentEntity<?> entity;
@@ -1231,26 +1231,13 @@ protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
12311231
private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression,
12321232
@Nullable MongoPersistentProperty sourceProperty) {
12331233

1234-
String rawPath = removePlaceholders(POSITIONAL_OPERATOR,
1235-
removePlaceholders(DOT_POSITIONAL_PATTERN, pathExpression));
1236-
// fix xx.11.22.33 becomes xx3, it should be xx.33, then path should be null. (test mapNestedLastBigIntegerFieldCorrectly)
1237-
if (pathExpression.contains(".")) {
1238-
String lastDotString = pathExpression.substring(pathExpression.lastIndexOf("."));
1239-
int lastDotLength = lastDotString.length();
1240-
int newLength = 0;
1241-
if (rawPath.contains(".")) {
1242-
newLength = rawPath.substring(rawPath.lastIndexOf(".")).length();
1243-
}
1244-
if (lastDotLength != newLength) {
1245-
rawPath = rawPath.substring(0, rawPath.length() - 1) + lastDotString;
1246-
}
1247-
}
1248-
12491234
if (sourceProperty != null && sourceProperty.getOwner().equals(entity)) {
12501235
return mappingContext.getPersistentPropertyPath(
12511236
PropertyPath.from(Pattern.quote(sourceProperty.getName()), entity.getTypeInformation()));
12521237
}
12531238

1239+
String rawPath = resolvePath(pathExpression);
1240+
12541241
PropertyPath path = forName(rawPath);
12551242
if (path == null || isPathToJavaLangClassProperty(path)) {
12561243
return null;
@@ -1345,6 +1332,38 @@ private boolean isPathToJavaLangClassProperty(PropertyPath path) {
13451332
return false;
13461333
}
13471334

1335+
private static String resolvePath(String source) {
1336+
1337+
String[] segments = source.split("\\.");
1338+
if (segments.length == 1) {
1339+
return source;
1340+
}
1341+
1342+
List<String> path = new ArrayList<>(segments.length);
1343+
1344+
/* always start from a property, so we can skip the first segment.
1345+
from there remove any position placeholder */
1346+
for(int i=1; i < segments.length; i++) {
1347+
String segment = segments[i];
1348+
if (segment.startsWith("[") && segment.endsWith("]")) {
1349+
continue;
1350+
}
1351+
if (NUMERIC_SEGMENT.matcher(segment).matches()) {
1352+
continue;
1353+
}
1354+
path.add(segment);
1355+
}
1356+
1357+
// when property is followed only by placeholders eg. 'values.0.3.90'
1358+
// or when there is no difference in the number of segments
1359+
if (path.isEmpty() || segments.length == path.size() + 1) {
1360+
return source;
1361+
}
1362+
1363+
path.add(0, segments[0]);
1364+
return StringUtils.collectionToDelimitedString(path, ".");
1365+
}
1366+
13481367
/**
13491368
* Return the {@link Converter} to be used to created the mapped key. Default implementation will use
13501369
* {@link PropertyToFieldNameConverter}.

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java

+12-19
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@
3535
import org.junit.jupiter.api.BeforeEach;
3636
import org.junit.jupiter.api.Test;
3737
import org.junit.jupiter.api.extension.ExtendWith;
38+
import org.junit.jupiter.params.ParameterizedTest;
39+
import org.junit.jupiter.params.provider.ValueSource;
3840
import org.mockito.Mockito;
3941
import org.mockito.junit.jupiter.MockitoExtension;
4042
import org.springframework.core.convert.converter.Converter;
@@ -1213,34 +1215,26 @@ void mapNestedStringFieldCorrectly() {
12131215
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document("levelOne.a.b.d", "e")));
12141216
}
12151217

1216-
@Test // GH-3775
1217-
void mapNestedIntegerFieldCorrectly() {
1218+
@ParameterizedTest // GH-3775, GH-4426
1219+
@ValueSource(strings = {"levelOne.0.1.3", "levelOne.0.1.32", "levelOne2.0.1.32", "levelOne2.0.1.320"})
1220+
void mapNestedIntegerFieldCorrectly(String path) {
12181221

1219-
Update update = new Update().set("levelOne.0.1.3", "4");
1222+
Update update = new Update().set(path, "4");
12201223
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
12211224
context.getPersistentEntity(EntityWithNestedMap.class));
12221225

1223-
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document("levelOne.0.1.3", "4")));
1226+
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document(path, "4")));
12241227
}
12251228

1226-
@Test
1227-
void mapNestedLastBigIntegerFieldCorrectly() {
1228-
1229-
Update update = new Update().set("levelOne.0.1.32", "4");
1230-
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
1231-
context.getPersistentEntity(EntityWithNestedMap.class));
1232-
1233-
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document("levelOne.0.1.32", "4")));
1234-
}
1235-
1236-
@Test // GH-3775
1237-
void mapNestedMixedStringIntegerFieldCorrectly() {
1229+
@ParameterizedTest // GH-3775, GH-4426
1230+
@ValueSource(strings = {"levelOne.0.1.c", "levelOne.0.1.c.32", "levelOne2.0.1.32.c", "levelOne2.0.1.c.320"})
1231+
void mapNestedMixedStringIntegerFieldCorrectly(String path) {
12381232

1239-
Update update = new Update().set("levelOne.0.1.c", "4");
1233+
Update update = new Update().set(path, "4");
12401234
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
12411235
context.getPersistentEntity(EntityWithNestedMap.class));
12421236

1243-
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document("levelOne.0.1.c", "4")));
1237+
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document(path, "4")));
12441238
}
12451239

12461240
@Test // GH-3775
@@ -1730,7 +1724,6 @@ static class UnwrappableType {
17301724

17311725
static class EntityWithNestedMap {
17321726
Map<String, Map<String, Map<String, Object>>> levelOne;
1733-
// for test mapNestedLastBigIntegerFieldCorrectly()
17341727
Map<String, Map<String, Map<String, Object>>> levelOne2;
17351728
}
17361729

0 commit comments

Comments
 (0)