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 bd5a5ccfe..5ba6ca37c 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 @@ -867,7 +867,7 @@ public class QueryMapper { * @return */ protected Converter getPropertyConverter() { - return PropertyToFieldNameConverter.INSTANCE; + return new PositionParameterRetainingPropertyKeyConverter(name); } /** @@ -881,6 +881,24 @@ public class QueryMapper { return new AssociationConverter(getAssociation()); } + /** + * @author Christoph Strobl + * @since 1.8 + */ + static class PositionParameterRetainingPropertyKeyConverter implements Converter { + + private final KeyMapper keyMapper; + + PositionParameterRetainingPropertyKeyConverter(String rawKey) { + this.keyMapper = new KeyMapper(rawKey); + } + + @Override + public String convert(MongoPersistentProperty source) { + return keyMapper.mapPropertyName(source); + } + } + /* * (non-Javadoc) * @see org.springframework.data.mongodb.core.convert.QueryMapper.Field#getTypeHint() @@ -901,6 +919,63 @@ public class QueryMapper { return NESTED_DOCUMENT; } + + /** + * @author Christoph Strobl + * @since 1.8 + */ + static class KeyMapper { + + Iterator iterator; + + public KeyMapper(String key) { + + this.iterator = Arrays.asList(key.split("\\.")).iterator(); + this.iterator.next(); + } + + /** + * Maps the property name while retaining potential positional operator {@literal $}. + * + * @param property + * @return + */ + protected String mapPropertyName(MongoPersistentProperty property) { + + String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property); + + boolean inspect = iterator.hasNext(); + while (inspect) { + + String partial = iterator.next(); + + boolean isPositional = (isPositionalParameter(partial) && (property.isMap() || property.isCollectionLike() || property + .isArray())); + if (isPositional) { + mappedName += "." + partial; + } + + inspect = isPositional && iterator.hasNext(); + } + + return mappedName; + } + + boolean isPositionalParameter(String partial) { + + if (partial.equals("$")) { + return true; + } + + try { + Long.valueOf(partial); + return true; + } catch (NumberFormatException e) { + return false; + } + } + } + } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java index 091a600d7..ea60fbb7d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/UpdateMapper.java @@ -15,8 +15,6 @@ */ package org.springframework.data.mongodb.core.convert; -import java.util.Arrays; -import java.util.Iterator; import java.util.Map.Entry; import org.springframework.core.convert.converter.Converter; @@ -24,13 +22,11 @@ import org.springframework.data.mapping.Association; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; -import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty.PropertyToFieldNameConverter; import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Update.Modifier; import org.springframework.data.mongodb.core.query.Update.Modifiers; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; -import org.springframework.util.Assert; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; @@ -213,7 +209,7 @@ public class UpdateMapper extends QueryMapper { */ @Override protected Converter getPropertyConverter() { - return new UpdatePropertyConverter(key); + return new PositionParameterRetainingPropertyKeyConverter(key); } /* @@ -225,99 +221,6 @@ public class UpdateMapper extends QueryMapper { return new UpdateAssociationConverter(getAssociation(), key); } - /** - * Special mapper handling positional parameter {@literal $} within property names. - * - * @author Christoph Strobl - * @since 1.7 - */ - private static class UpdateKeyMapper { - - private final Iterator iterator; - - protected UpdateKeyMapper(String rawKey) { - - Assert.hasText(rawKey, "Key must not be null or empty!"); - - this.iterator = Arrays.asList(rawKey.split("\\.")).iterator(); - this.iterator.next(); - } - - /** - * Maps the property name while retaining potential positional operator {@literal $}. - * - * @param property - * @return - */ - protected String mapPropertyName(MongoPersistentProperty property) { - - String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property); - - boolean inspect = iterator.hasNext(); - while (inspect) { - - String partial = iterator.next(); - - boolean isPositional = isPositionalParameter(partial); - if (isPositional) { - mappedName += "." + partial; - } - - inspect = isPositional && iterator.hasNext(); - } - - return mappedName; - } - - boolean isPositionalParameter(String partial) { - - if (partial.equals("$")) { - return true; - } - - try { - Long.valueOf(partial); - return true; - } catch (NumberFormatException e) { - return false; - } - } - - } - - /** - * Special {@link Converter} for {@link MongoPersistentProperty} instances that will concatenate the {@literal $} - * contained in the source update key. - * - * @author Oliver Gierke - * @author Christoph Strobl - */ - private static class UpdatePropertyConverter implements Converter { - - private final UpdateKeyMapper mapper; - - /** - * Creates a new {@link UpdatePropertyConverter} with the given update key. - * - * @param updateKey must not be {@literal null} or empty. - */ - public UpdatePropertyConverter(String updateKey) { - - Assert.hasText(updateKey, "Update key must not be null or empty!"); - - this.mapper = new UpdateKeyMapper(updateKey); - } - - /* - * (non-Javadoc) - * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) - */ - @Override - public String convert(MongoPersistentProperty property) { - return mapper.mapPropertyName(property); - } - } - /** * {@link Converter} retaining positional parameter {@literal $} for {@link Association}s. * @@ -325,7 +228,7 @@ public class UpdateMapper extends QueryMapper { */ protected static class UpdateAssociationConverter extends AssociationConverter { - private final UpdateKeyMapper mapper; + private final KeyMapper mapper; /** * Creates a new {@link AssociationConverter} for the given {@link Association}. @@ -335,7 +238,7 @@ public class UpdateMapper extends QueryMapper { public UpdateAssociationConverter(Association association, String key) { super(association); - this.mapper = new UpdateKeyMapper(key); + this.mapper = new KeyMapper(key); } /* diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java index b510d7145..8cf5852b7 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java @@ -790,6 +790,34 @@ public class QueryMapperUnitTests { assertThat(dbo, isBsonObject().containing("geoJsonPoint.$geoIntersects.$geometry.coordinates")); } + /** + * @see DATAMONGO-1269 + */ + @Test + public void mappingShouldRetainNumericMapKey() { + + Query query = query(where("map.1.stringProperty").is("ba'alzamon")); + + DBObject dbo = mapper.getMappedObject(query.getQueryObject(), + context.getPersistentEntity(EntityWithComplexValueTypeMap.class)); + + assertThat(dbo.containsField("map.1.stringProperty"), is(true)); + } + + /** + * @see DATAMONGO-1269 + */ + @Test + public void mappingShouldRetainNumericPositionInList() { + + Query query = query(where("list.1.stringProperty").is("ba'alzamon")); + + DBObject dbo = mapper.getMappedObject(query.getQueryObject(), + context.getPersistentEntity(EntityWithComplexValueTypeList.class)); + + assertThat(dbo.containsField("list.1.stringProperty"), is(true)); + } + @Document public class Foo { @Id private ObjectId id; @@ -890,4 +918,18 @@ public class QueryMapperUnitTests { GeoJsonPoint geoJsonPoint; @Field("geoJsonPointWithNameViaFieldAnnotation") GeoJsonPoint namedGeoJsonPoint; } + + static class SimpeEntityWithoutId { + + String stringProperty; + Integer integerProperty; + } + + static class EntityWithComplexValueTypeMap { + Map map; + } + + static class EntityWithComplexValueTypeList { + List list; + } }