Skip to content

Commit 31e9496

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 5bb7364 commit 31e9496

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
@@ -1077,7 +1077,7 @@ public Class<?> getFieldType() {
10771077
protected static class MetadataBackedField extends Field {
10781078

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

10831083
private final MongoPersistentEntity<?> entity;
@@ -1243,26 +1243,13 @@ protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
12431243
private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression,
12441244
@Nullable MongoPersistentProperty sourceProperty) {
12451245

1246-
String rawPath = removePlaceholders(POSITIONAL_OPERATOR,
1247-
removePlaceholders(DOT_POSITIONAL_PATTERN, pathExpression));
1248-
// fix xx.11.22.33 becomes xx3, it should be xx.33, then path should be null. (test mapNestedLastBigIntegerFieldCorrectly)
1249-
if (pathExpression.contains(".")) {
1250-
String lastDotString = pathExpression.substring(pathExpression.lastIndexOf("."));
1251-
int lastDotLength = lastDotString.length();
1252-
int newLength = 0;
1253-
if (rawPath.contains(".")) {
1254-
newLength = rawPath.substring(rawPath.lastIndexOf(".")).length();
1255-
}
1256-
if (lastDotLength != newLength) {
1257-
rawPath = rawPath.substring(0, rawPath.length() - 1) + lastDotString;
1258-
}
1259-
}
1260-
12611246
if (sourceProperty != null && sourceProperty.getOwner().equals(entity)) {
12621247
return mappingContext.getPersistentPropertyPath(
12631248
PropertyPath.from(Pattern.quote(sourceProperty.getName()), entity.getTypeInformation()));
12641249
}
12651250

1251+
String rawPath = resolvePath(pathExpression);
1252+
12661253
PropertyPath path = forName(rawPath);
12671254
if (path == null || isPathToJavaLangClassProperty(path)) {
12681255
return null;
@@ -1357,6 +1344,38 @@ private boolean isPathToJavaLangClassProperty(PropertyPath path) {
13571344
return false;
13581345
}
13591346

1347+
private static String resolvePath(String source) {
1348+
1349+
String[] segments = source.split("\\.");
1350+
if (segments.length == 1) {
1351+
return source;
1352+
}
1353+
1354+
List<String> path = new ArrayList<>(segments.length);
1355+
1356+
/* always start from a property, so we can skip the first segment.
1357+
from there remove any position placeholder */
1358+
for(int i=1; i < segments.length; i++) {
1359+
String segment = segments[i];
1360+
if (segment.startsWith("[") && segment.endsWith("]")) {
1361+
continue;
1362+
}
1363+
if (NUMERIC_SEGMENT.matcher(segment).matches()) {
1364+
continue;
1365+
}
1366+
path.add(segment);
1367+
}
1368+
1369+
// when property is followed only by placeholders eg. 'values.0.3.90'
1370+
// or when there is no difference in the number of segments
1371+
if (path.isEmpty() || segments.length == path.size() + 1) {
1372+
return source;
1373+
}
1374+
1375+
path.add(0, segments[0]);
1376+
return StringUtils.collectionToDelimitedString(path, ".");
1377+
}
1378+
13601379
/**
13611380
* Return the {@link Converter} to be used to created the mapped key. Default implementation will use
13621381
* {@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

@@ -1212,34 +1214,26 @@ void mapNestedStringFieldCorrectly() {
12121214
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document("levelOne.a.b.d", "e")));
12131215
}
12141216

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

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

1222-
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document("levelOne.0.1.3", "4")));
1225+
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document(path, "4")));
12231226
}
12241227

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

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

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

12451239
@Test // GH-3775
@@ -1729,7 +1723,6 @@ static class UnwrappableType {
17291723

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

0 commit comments

Comments
 (0)