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 4c1aa40c3..e2f304dac 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,8 @@ import java.util.Map.Entry; import org.bson.Document; import org.bson.conversions.Bson; import org.springframework.core.convert.converter.Converter; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; import org.springframework.data.mapping.Association; import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; @@ -69,7 +71,7 @@ public class UpdateMapper extends QueryMapper { for (String s : document.keySet()) { if (s.startsWith("$")) { - if(s.equals("$set")){ + if (s.equals("$set")) { set = document.get(s, Document.class); } hasOperators = true; @@ -99,6 +101,7 @@ public class UpdateMapper extends QueryMapper { /** * Returns {@literal true} if the given {@link Document} is an update object that uses update operators. + * * @param updateObj * @return {@literal true} if the given {@link Document} is an update object. */ @@ -194,11 +197,23 @@ public class UpdateMapper extends QueryMapper { } private Document getMappedValue(Field field, Modifier modifier) { + return new Document(modifier.getKey(), getMappedModifier(field, modifier)); + } + + private Object getMappedModifier(Field field, Modifier modifier) { + + Object value = modifier.getValue(); + + if (value instanceof Sort) { + + Document sortObject = getSortObject((Sort) value); + return field == null || field.getPropertyEntity() == null ? sortObject + : getMappedSort(sortObject, field.getPropertyEntity()); + } TypeInformation typeHint = field == null ? ClassTypeInformation.OBJECT : field.getTypeHint(); - Object value = converter.convertToMongoType(modifier.getValue(), typeHint); - return new Document(modifier.getKey(), value); + return converter.convertToMongoType(value, typeHint); } private TypeInformation getTypeHintForEntity(Object source, MongoPersistentEntity entity) { @@ -229,6 +244,17 @@ public class UpdateMapper extends QueryMapper { : new MetadataBackedUpdateField(entity, key, mappingContext); } + private static Document getSortObject(Sort sort) { + + Document document = new Document(); + + for (Order order : sort) { + document.put(order.getProperty(), order.isAscending() ? 1 : -1); + } + + return document; + } + /** * {@link MetadataBackedField} that handles {@literal $} paths inside a field key. We clean up an update key * containing a {@literal $} before handing it to the super class to make sure property lookups and transformations diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java index 9e0486880..222a25bec 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Update.java @@ -667,33 +667,42 @@ public class Update { * Implementation of {@link Modifier} representing {@code $sort}. * * @author Pavel Vodrazka + * @author Mark Paluch * @since 1.10 */ private static class SortModifier implements Modifier { private final Object sort; + /** + * Creates a new {@link SortModifier} instance given {@link Direction}. + * + * @param direction must not be {@literal null}. + */ public SortModifier(Direction direction) { + + Assert.notNull(direction, "Direction must not be null!"); this.sort = direction.isAscending() ? 1 : -1; } + /** + * Creates a new {@link SortModifier} instance given {@link Sort}. + * + * @param sort must not be {@literal null}. + */ public SortModifier(Sort sort) { - this.sort = createDBObject(sort); - } - - private Document createDBObject(Sort sort) { - Document obj = new Document(); + Assert.notNull(sort, "Sort must not be null!"); for (Order order : sort) { + if (order.isIgnoreCase()) { throw new IllegalArgumentException(String.format("Given sort contained an Order for %s with ignore case! " + "MongoDB does not support sorting ignoring case currently!", order.getProperty())); } - obj.put(order.getProperty(), order.isAscending() ? 1 : -1); } - return obj; + this.sort = sort; } /* @@ -780,14 +789,14 @@ public class Update { * Propagates {@code $sort} to {@code $push}. {@code $sort} requires the {@code $each} operator. Forces document * elements to be sorted in given {@literal order}. * - * @param order must not be {@literal null}. + * @param sort must not be {@literal null}. * @return never {@literal null}. * @since 1.10 */ - public PushOperatorBuilder sort(Sort order) { + public PushOperatorBuilder sort(Sort sort) { - Assert.notNull(order, "Order must not be 'null'."); - this.modifiers.addModifier(new SortModifier(order)); + Assert.notNull(sort, "Sort must not be 'null'."); + this.modifiers.addModifier(new SortModifier(sort)); return this; } 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 2de45a656..ca13e6697 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 @@ -420,7 +420,8 @@ public class UpdateMapperUnitTests { Update update = new Update().push("scores").sort(Direction.DESC).each(42, 23, 68); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); + Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(ParentClass.class)); Document push = getAsDocument(mappedObject, "$push"); Document key = getAsDocument(push, "scores"); @@ -436,17 +437,18 @@ public class UpdateMapperUnitTests { @Test public void updatePushEachWithDocumentSortShouldRenderCorrectly() { - Update update = new Update().push("names") - .sort(new Sort(new Order(Direction.ASC, "last"), new Order(Direction.ASC, "first"))) + Update update = new Update().push("list") + .sort(new Sort(new Order(Direction.ASC, "value"), new Order(Direction.ASC, "field"))) .each(Collections.emptyList()); - Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), context.getPersistentEntity(Object.class)); + Document mappedObject = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(EntityWithList.class)); Document push = getAsDocument(mappedObject, "$push"); - Document key = getAsDocument(push, "names"); + Document key = getAsDocument(push, "list"); assertThat(key.containsKey("$sort"), is(true)); - assertThat((Document) key.get("$sort"), equalTo(new Document("last", 1).append("first", 1))); + assertThat((Document) key.get("$sort"), equalTo(new Document("renamed-value", 1).append("field", 1))); assertThat(key.containsKey("$each"), is(true)); } @@ -1317,9 +1319,14 @@ public class UpdateMapperUnitTests { NestedDocument concreteValue; } + static class EntityWithList { + List list; + } + static class EntityWithAliasedObject { @Field("renamed-value") Object value; + Object field; } static class EntityWithObjectMap {