From 443cde623623fe08888cd86da39f6bf4198dd1a0 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Mon, 3 Mar 2014 21:29:10 +0100 Subject: [PATCH] DATAMONGO-863 - UpdateMapper doesn't convert raw DBObjects anymore. UpdateMapper now only performs simple conversion if it encounters a DBObject, instead of deep inspection on keywords used. This allows to use custom clauses nested in Update for operations not directly supported. Original Pull Request: #138. --- .../mongodb/core/convert/QueryMapper.java | 44 ++++++++++++++++--- .../mongodb/core/convert/UpdateMapper.java | 7 ++- .../core/convert/UpdateMapperUnitTests.java | 22 ++++++++++ 3 files changed, 66 insertions(+), 7 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 b463a108b..83abddb31 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 @@ -138,7 +138,7 @@ public class QueryMapper { value = getMappedValue(field, rawValue); } - return Collections.singletonMap(key, value).entrySet().iterator().next(); + return createMapEntry(key, value); } /** @@ -168,7 +168,7 @@ public class QueryMapper { BasicDBList newConditions = new BasicDBList(); for (Object condition : conditions) { - newConditions.add(condition instanceof DBObject ? getMappedObject((DBObject) condition, entity) + newConditions.add(isDBObject(condition) ? getMappedObject((DBObject) condition, entity) : convertSimpleOrDBObject(condition, entity)); } @@ -209,7 +209,7 @@ public class QueryMapper { if (documentField.isIdField()) { - if (value instanceof DBObject) { + if (isDBObject(value)) { DBObject valueDbo = (DBObject) value; DBObject resultDbo = new BasicDBObject(valueDbo.toMap()); @@ -267,13 +267,13 @@ public class QueryMapper { * @param entity * @return */ - private Object convertSimpleOrDBObject(Object source, MongoPersistentEntity entity) { + protected Object convertSimpleOrDBObject(Object source, MongoPersistentEntity entity) { if (source instanceof BasicDBList) { return delegateConvertToMongoType(source, entity); } - if (source instanceof DBObject) { + if (isDBObject(source)) { return getMappedObject((DBObject) source, entity); } @@ -329,6 +329,40 @@ public class QueryMapper { return createDbRefFor(source, property); } + /** + * Checks whether the given value is a {@link DBObject}. + * + * @param value can be {@literal null}. + * @return + */ + protected final boolean isDBObject(Object value) { + return value instanceof DBObject; + } + + /** + * Creates a new {@link Entry} for the given {@link Field} with the given value. + * + * @param field must not be {@literal null}. + * @param value can be {@literal null}. + * @return + */ + protected final Entry createMapEntry(Field field, Object value) { + return createMapEntry(field.getMappedKey(), value); + } + + /** + * Creates a new {@link Entry} with the given key and value. + * + * @param key must not be {@literal null} or empty. + * @param value can be {@literal null} + * @return + */ + private Entry createMapEntry(String key, Object value) { + + Assert.hasText(key, "Key must not be null or empty!"); + return Collections.singletonMap(key, value).entrySet().iterator().next(); + } + private DBRef createDbRefFor(Object source, MongoPersistentProperty property) { if (source instanceof DBRef) { 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 0b839b102..21154de2a 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 @@ -16,7 +16,6 @@ package org.springframework.data.mongodb.core.convert; import java.util.Arrays; -import java.util.Collections; import java.util.Iterator; import java.util.Map.Entry; @@ -76,6 +75,10 @@ public class UpdateMapper extends QueryMapper { @Override protected Entry getMappedObjectForField(Field field, Object rawValue) { + if (isDBObject(rawValue)) { + return createMapEntry(field, convertSimpleOrDBObject(rawValue, field.getPropertyEntity())); + } + if (!isUpdateModifier(rawValue)) { return super.getMappedObjectForField(field, getMappedValue(field, rawValue)); } @@ -100,7 +103,7 @@ public class UpdateMapper extends QueryMapper { throw new IllegalArgumentException(String.format("Unable to map value of type '%s'!", rawValue.getClass())); } - return Collections.singletonMap(field.getMappedKey(), value).entrySet().iterator().next(); + return createMapEntry(field, value); } /* 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 3af0fc27a..fbc6dd719 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 @@ -25,6 +25,7 @@ import java.util.Arrays; import java.util.List; import org.hamcrest.Matcher; +import org.hamcrest.collection.IsIterableContainingInOrder; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -390,6 +391,27 @@ public class UpdateMapperUnitTests { assertThat(setClause.containsField("listOfInterface.$.value"), is(true)); } + /** + * @see DATAMONGO-863 + */ + @Test + public void doesNotConvertRawDbObjects() { + + Update update = new Update(); + update.pull("options", + new BasicDBObject("_id", new BasicDBObject("$in", converter.convertToMongoType(Arrays.asList(1L, 2L))))); + + DBObject mappedObject = mapper.getMappedObject(update.getUpdateObject(), + context.getPersistentEntity(ParentClass.class)); + + DBObject setClause = getAsDBObject(mappedObject, "$pull"); + DBObject options = getAsDBObject(setClause, "options"); + DBObject idClause = getAsDBObject(options, "_id"); + BasicDBList inClause = getAsDBList(idClause, "$in"); + + assertThat(inClause, IsIterableContainingInOrder. contains(1L, 2L)); + } + static interface Model {} static class ModelImpl implements Model {