diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java index a360b8be9..1013628de 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java @@ -1411,6 +1411,14 @@ public class QueryMapper { this.currentIndex = 0; } + String nextToken() { + return pathParts.get(currentIndex+1); + } + + boolean hasNexToken() { + return pathParts.size() > currentIndex+1; + } + /** * Maps the property name while retaining potential positional operator {@literal $}. * @@ -1420,31 +1428,25 @@ public class QueryMapper { protected String mapPropertyName(MongoPersistentProperty property) { StringBuilder mappedName = new StringBuilder(PropertyToFieldNameConverter.INSTANCE.convert(property)); - - boolean inspect = iterator.hasNext(); - - while (inspect) { - - String partial = iterator.next(); - currentIndex++; - - boolean isPositional = isPositionalParameter(partial) && property.isCollectionLike(); - if (property.isMap() && currentPropertyRoot.equals(partial) && iterator.hasNext()) { - partial = iterator.next(); - currentIndex++; - } - - if (isPositional || property.isMap() && !currentPropertyRoot.equals(partial)) { - mappedName.append(".").append(partial); - } - - inspect = isPositional && iterator.hasNext(); + if(!hasNexToken()) { + return mappedName.toString(); } - if (currentIndex + 1 < pathParts.size()) { - currentIndex++; - currentPropertyRoot = pathParts.get(currentIndex); + String nextToken = nextToken(); + if(isPositionalParameter(nextToken)) { + + mappedName.append(".").append(nextToken); + currentIndex+=2; + return mappedName.toString(); } + + if(property.isMap()) { + + mappedName.append(".").append(nextToken); + currentIndex+=2; + return mappedName.toString(); + } + currentIndex++; return mappedName.toString(); } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java index 15704af03..28d522fba 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java @@ -1309,6 +1309,36 @@ class UpdateMapperUnitTests { assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("customers", Arrays.asList("c-name")))); } + @Test // GH-3921 + void mapNumericKeyInPathHavingComplexMapValyeTypes() { + + Update update = new Update().set("testInnerData.testMap.1.intValue", "4"); + Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(TestData.class)); + + assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("testInnerData.testMap.1.intValue","4"))); + } + + @Test // GH-3921 + void mapNumericKeyInPathNotMatchingExistingProperties() { + + Update update = new Update().set("testInnerData.imaginaryMap.1.nonExistingProperty", "4"); + Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(TestData.class)); + + assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("testInnerData.imaginaryMap.1.noExistingProperty","4"))); + } + + @Test // GH-3921 + void mapNumericKeyInPathPartiallyMatchingExistingProperties() { + + Update update = new Update().set("testInnerData.testMap.1.nonExistingProperty.2.someValue", "4"); + Document mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(TestData.class)); + + assertThat(mappedUpdate).isEqualTo(new org.bson.Document("$set",new org.bson.Document("testInnerData.testMap.1.nonExistingProperty.2.someValue","4"))); + } + static class DomainTypeWrappingConcreteyTypeHavingListOfInterfaceTypeAttributes { ListModelWrapper concreteTypeWithListAttributeOfInterfaceType; } @@ -1710,4 +1740,20 @@ class UpdateMapperUnitTests { private List samples; } + @Data + private static class TestData { + @Id + private String id; + private TestInnerData testInnerData; + } + + @Data + private static class TestInnerData { + private Map testMap; + } + + @Data + private static class TestValue { + private int intValue; + } }