Skip to content

Commit a8dbfb9

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 82917db commit a8dbfb9

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

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

10801080
private final MongoPersistentEntity<?> entity;
@@ -1216,26 +1216,13 @@ protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
12161216
private PersistentPropertyPath<MongoPersistentProperty> getPath(String pathExpression,
12171217
@Nullable MongoPersistentProperty sourceProperty) {
12181218

1219-
String rawPath = removePlaceholders(POSITIONAL_OPERATOR,
1220-
removePlaceholders(DOT_POSITIONAL_PATTERN, pathExpression));
1221-
// fix xx.11.22.33 becomes xx3, it should be xx.33, then path should be null. (test mapNestedLastBigIntegerFieldCorrectly)
1222-
if (pathExpression.contains(".")) {
1223-
String lastDotString = pathExpression.substring(pathExpression.lastIndexOf("."));
1224-
int lastDotLength = lastDotString.length();
1225-
int newLength = 0;
1226-
if (rawPath.contains(".")) {
1227-
newLength = rawPath.substring(rawPath.lastIndexOf(".")).length();
1228-
}
1229-
if (lastDotLength != newLength) {
1230-
rawPath = rawPath.substring(0, rawPath.length() - 1) + lastDotString;
1231-
}
1232-
}
1233-
12341219
if (sourceProperty != null && sourceProperty.getOwner().equals(entity)) {
12351220
return mappingContext.getPersistentPropertyPath(
12361221
PropertyPath.from(Pattern.quote(sourceProperty.getName()), entity.getTypeInformation()));
12371222
}
12381223

1224+
String rawPath = resolvePath(pathExpression);
1225+
12391226
PropertyPath path = forName(rawPath);
12401227
if (path == null || isPathToJavaLangClassProperty(path)) {
12411228
return null;
@@ -1330,6 +1317,38 @@ private boolean isPathToJavaLangClassProperty(PropertyPath path) {
13301317
return false;
13311318
}
13321319

1320+
private static String resolvePath(String source) {
1321+
1322+
String[] segments = source.split("\\.");
1323+
if (segments.length == 1) {
1324+
return source;
1325+
}
1326+
1327+
List<String> path = new ArrayList<>(segments.length);
1328+
1329+
/* always start from a property, so we can skip the first segment.
1330+
from there remove any position placeholder */
1331+
for(int i=1; i < segments.length; i++) {
1332+
String segment = segments[i];
1333+
if (segment.startsWith("[") && segment.endsWith("]")) {
1334+
continue;
1335+
}
1336+
if (NUMERIC_SEGMENT.matcher(segment).matches()) {
1337+
continue;
1338+
}
1339+
path.add(segment);
1340+
}
1341+
1342+
// when property is followed only by placeholders eg. 'values.0.3.90'
1343+
// or when there is no difference in the number of segments
1344+
if (path.isEmpty() || segments.length == path.size() + 1) {
1345+
return source;
1346+
}
1347+
1348+
path.add(0, segments[0]);
1349+
return StringUtils.collectionToDelimitedString(path, ".");
1350+
}
1351+
13331352
/**
13341353
* Return the {@link Converter} to be used to created the mapped key. Default implementation will use
13351354
* {@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)