Skip to content

Commit 0bf472a

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 2de00cd commit 0bf472a

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
@@ -31,6 +31,8 @@
3131
import org.junit.jupiter.api.BeforeEach;
3232
import org.junit.jupiter.api.Test;
3333
import org.junit.jupiter.api.extension.ExtendWith;
34+
import org.junit.jupiter.params.ParameterizedTest;
35+
import org.junit.jupiter.params.provider.ValueSource;
3436
import org.mockito.Mockito;
3537
import org.mockito.junit.jupiter.MockitoExtension;
3638
import org.springframework.core.convert.converter.Converter;
@@ -1207,34 +1209,26 @@ void mapNestedStringFieldCorrectly() {
12071209
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document("levelOne.a.b.d", "e")));
12081210
}
12091211

1210-
@Test // GH-3775
1211-
void mapNestedIntegerFieldCorrectly() {
1212+
@ParameterizedTest // GH-3775, GH-4426
1213+
@ValueSource(strings = {"levelOne.0.1.3", "levelOne.0.1.32", "levelOne2.0.1.32", "levelOne2.0.1.320"})
1214+
void mapNestedIntegerFieldCorrectly(String path) {
12121215

1213-
Update update = new Update().set("levelOne.0.1.3", "4");
1216+
Update update = new Update().set(path, "4");
12141217
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
12151218
context.getPersistentEntity(EntityWithNestedMap.class));
12161219

1217-
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document("levelOne.0.1.3", "4")));
1220+
assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set", new org.bson.Document(path, "4")));
12181221
}
12191222

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

1233-
Update update = new Update().set("levelOne.0.1.c", "4");
1227+
Update update = new Update().set(path, "4");
12341228
Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(),
12351229
context.getPersistentEntity(EntityWithNestedMap.class));
12361230

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

12401234
@Test // GH-3775
@@ -1742,7 +1736,6 @@ static class UnwrappableType {
17421736

17431737
static class EntityWithNestedMap {
17441738
Map<String, Map<String, Map<String, Object>>> levelOne;
1745-
// for test mapNestedLastBigIntegerFieldCorrectly()
17461739
Map<String, Map<String, Map<String, Object>>> levelOne2;
17471740
}
17481741

0 commit comments

Comments
 (0)