From 078cca83e3d29923159cb323b1941cffa915b2f4 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 28 Oct 2014 08:07:03 +0100 Subject: [PATCH] DATAMONGO-1077 - Fix Update removing $ operator for DBRef. We now retain the positional parameter "$" when mapping field names for associations. Orignal pull request: #235. --- .../mongodb/core/convert/QueryMapper.java | 14 +++- .../mongodb/core/convert/UpdateMapper.java | 82 +++++++++++++++++-- .../core/convert/UpdateMapperUnitTests.java | 17 ++++ 3 files changed, 105 insertions(+), 8 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 0ac69a768..55274b37c 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 @@ -792,8 +792,7 @@ public class QueryMapper { */ @Override public String getMappedKey() { - return path == null ? name : path.toDotPath(isAssociation() ? new AssociationConverter(getAssociation()) - : getPropertyConverter()); + return path == null ? name : path.toDotPath(isAssociation() ? getAssociationConverter() : getPropertyConverter()); } protected PersistentPropertyPath getPath() { @@ -845,6 +844,17 @@ public class QueryMapper { protected Converter getPropertyConverter() { return PropertyToFieldNameConverter.INSTANCE; } + + /** + * Return the {@link Converter} to use for creating the mapped key of an association. Default implementation is + * {@link AssociationConverter}. + * + * @return + * @since 1.7 + */ + protected Converter getAssociationConverter() { + return new AssociationConverter(getAssociation()); + } } /** 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 fcb31852c..c9314b965 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 @@ -20,6 +20,7 @@ import java.util.Iterator; import java.util.Map.Entry; import org.springframework.core.convert.converter.Converter; +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; @@ -193,7 +194,48 @@ public class UpdateMapper extends QueryMapper { */ @Override protected Converter getPropertyConverter() { - return isAssociation() ? new AssociationConverter(getAssociation()) : new UpdatePropertyConverter(key); + return new UpdatePropertyConverter(key); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.convert.QueryMapper.MetadataBackedField#getAssociationConverter() + */ + @Override + 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); + return iterator.hasNext() && iterator.next().equals("$") ? String.format("%s.$", mappedName) : mappedName; + } + } /** @@ -201,10 +243,11 @@ public class UpdateMapper extends QueryMapper { * contained in the source update key. * * @author Oliver Gierke + * @author Christoph Strobl */ private static class UpdatePropertyConverter implements Converter { - private final Iterator iterator; + private final UpdateKeyMapper mapper; /** * Creates a new {@link UpdatePropertyConverter} with the given update key. @@ -215,8 +258,7 @@ public class UpdateMapper extends QueryMapper { Assert.hasText(updateKey, "Update key must not be null or empty!"); - this.iterator = Arrays.asList(updateKey.split("\\.")).iterator(); - this.iterator.next(); + this.mapper = new UpdateKeyMapper(updateKey); } /* @@ -225,9 +267,37 @@ public class UpdateMapper extends QueryMapper { */ @Override public String convert(MongoPersistentProperty property) { + return mapper.mapPropertyName(property); + } + } - String mappedName = PropertyToFieldNameConverter.INSTANCE.convert(property); - return iterator.hasNext() && iterator.next().equals("$") ? String.format("%s.$", mappedName) : mappedName; + /** + * {@link Converter} retaining positional parameter {@literal $} for {@link Association}s. + * + * @author Christoph Strobl + */ + protected static class UpdateAssociationConverter extends AssociationConverter { + + private final UpdateKeyMapper mapper; + + /** + * Creates a new {@link AssociationConverter} for the given {@link Association}. + * + * @param association must not be {@literal null}. + */ + public UpdateAssociationConverter(Association association, String key) { + + super(association); + this.mapper = new UpdateKeyMapper(key); + } + + /* + * (non-Javadoc) + * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) + */ + @Override + public String convert(MongoPersistentProperty source) { + return super.convert(source) == null ? null : mapper.mapPropertyName(source); } } } 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 3ddf2526a..94af26d3d 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 @@ -508,6 +508,23 @@ public class UpdateMapperUnitTests { assertThat(list, equalTo(new BasicDBObjectBuilder().add("_id", "1").get())); } + /** + * @see DATAMONGO-1077 + */ + @Test + public void shouldNotRemovePositionalParameter() { + + Update update = new Update(); + update.unset("dbRefAnnotatedList.$"); + + DBObject mappedUpdate = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(DocumentWithDBRefCollection.class)); + + DBObject $unset = DBObjectTestUtils.getAsDBObject(mappedUpdate, "$unset"); + + assertThat($unset, equalTo(new BasicDBObjectBuilder().add("dbRefAnnotatedList.$", 1).get())); + } + @org.springframework.data.mongodb.core.mapping.Document(collection = "DocumentWithReferenceToInterface") static interface DocumentWithReferenceToInterface {