From 99e90cb6a37c2b7ed755ef9742fe4d053c6fd1a0 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 5 Aug 2015 08:17:08 +0200 Subject: [PATCH 1/2] DATAMONGO-1269 - QueryMapper drops numeric keys in Maps. Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-cross-store/pom.xml | 4 ++-- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb-log4j/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index 74b7a38a74..50e5a08be3 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1269-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index 7d18f752fc..a7491f916d 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1269-SNAPSHOT ../pom.xml @@ -48,7 +48,7 @@ org.springframework.data spring-data-mongodb - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1269-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 13c0985a6a..ec3d3361fa 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1269-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml index 64d6f7864a..570680c088 100644 --- a/spring-data-mongodb-log4j/pom.xml +++ b/spring-data-mongodb-log4j/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1269-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 819112059b..b24bb3e1dc 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 1.8.0.BUILD-SNAPSHOT + 1.8.0.DATAMONGO-1269-SNAPSHOT ../pom.xml From 01274893adf1a042dc93b07950b45ea5e81d81c3 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 5 Aug 2015 12:53:34 +0200 Subject: [PATCH 2/2] DATAMONGO-1269 - Retain position parameter in property path. We now retain position parameters in paths used in queries when mapping the fieldname. This allows to map "list.1.name" to the name property of the first element in the list. The change also fixes a glitch in mapping java.util.Map like structures having numeric keys. --- .../mongodb/core/convert/QueryMapper.java | 77 ++++++++++++- .../mongodb/core/convert/UpdateMapper.java | 103 +----------------- .../core/convert/QueryMapperUnitTests.java | 42 +++++++ 3 files changed, 121 insertions(+), 101 deletions(-) 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 bd5a5ccfec..5ba6ca37cd 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 @@ private PersistentPropertyPath getPath(String pathExpre * @return */ protected Converter getPropertyConverter() { - return PropertyToFieldNameConverter.INSTANCE; + return new PositionParameterRetainingPropertyKeyConverter(name); } /** @@ -881,6 +881,24 @@ protected Converter getAssociationConverter() { 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 TypeInformation getTypeHint() { 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 091a600d7a..ea60fbb7df 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.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 String getMappedKey() { */ @Override protected Converter getPropertyConverter() { - return new UpdatePropertyConverter(key); + return new PositionParameterRetainingPropertyKeyConverter(key); } /* @@ -225,99 +221,6 @@ protected Converter getAssociationConverter() { 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 String convert(MongoPersistentProperty property) { */ 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 @@ protected static class UpdateAssociationConverter extends AssociationConverter { 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 b510d7145e..8cf5852b76 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 void intersectsShouldUseGeoJsonRepresentationCorrectly() { 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 @@ static class ClassWithGeoTypes { GeoJsonPoint geoJsonPoint; @Field("geoJsonPointWithNameViaFieldAnnotation") GeoJsonPoint namedGeoJsonPoint; } + + static class SimpeEntityWithoutId { + + String stringProperty; + Integer integerProperty; + } + + static class EntityWithComplexValueTypeMap { + Map map; + } + + static class EntityWithComplexValueTypeList { + List list; + } }