From 3dfc59bfb89272a3b02073ba66da8f448098cb3e Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Sat, 14 Apr 2012 13:31:46 +0200 Subject: [PATCH] DATAMONGO-429 - Fixed handling of nested arrays in QueryMapper. QueryMapper now correctly transforms arrays not concreting them into BasicDBObjects anymore. --- .../mongodb/core/convert/QueryMapper.java | 57 +++++++++++++++---- .../core/convert/QueryMapperUnitTests.java | 15 ++++- 2 files changed, 60 insertions(+), 12 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 bd09a6f1d..7c129b63a 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 @@ -25,10 +25,12 @@ import org.bson.types.ObjectId; import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConversionService; import org.springframework.data.mapping.PersistentEntity; +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.util.Assert; +import com.mongodb.BasicDBList; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; @@ -40,6 +42,9 @@ import com.mongodb.DBObject; */ public class QueryMapper { + private static final List DEFAULT_ID_NAMES = Arrays.asList("id", "_id"); + private static final String N_OR_PATTERN = "\\$.*or"; + private final ConversionService conversionService; private final MongoConverter converter; @@ -58,8 +63,8 @@ public class QueryMapper { * Replaces the property keys used in the given {@link DBObject} with the appropriate keys by using the * {@link PersistentEntity} metadata. * - * @param query - * @param entity + * @param query must not be {@literal null}. + * @param entity can be {@literal null}. * @return */ public DBObject getMappedObject(DBObject query, MongoPersistentEntity entity) { @@ -67,8 +72,11 @@ public class QueryMapper { DBObject newDbo = new BasicDBObject(); for (String key : query.keySet()) { + + MongoPersistentEntity nestedEntity = getNestedEntity(entity, key); String newKey = key; Object value = query.get(key); + if (isIdKey(key, entity)) { if (value instanceof DBObject) { DBObject valueDbo = (DBObject) value; @@ -80,34 +88,51 @@ public class QueryMapper { } valueDbo.put(inKey, ids.toArray(new Object[ids.size()])); } else { - value = getMappedObject((DBObject) value, entity); + value = getMappedObject((DBObject) value, nestedEntity); } } else { value = convertId(value); } newKey = "_id"; - } else if (key.startsWith("$") && key.endsWith("or")) { + } else if (key.matches(N_OR_PATTERN)) { // $or/$nor Iterable conditions = (Iterable) value; BasicBSONList newConditions = new BasicBSONList(); Iterator iter = conditions.iterator(); while (iter.hasNext()) { - newConditions.add(getMappedObject((DBObject) iter.next(), entity)); + newConditions.add(getMappedObject((DBObject) iter.next(), nestedEntity)); } value = newConditions; } else if (key.equals("$ne")) { value = convertId(value); - } else if (value instanceof DBObject) { - newDbo.put(newKey, getMappedObject((DBObject) value, entity)); - continue; } - newDbo.put(newKey, converter.convertToMongoType(value)); + newDbo.put(newKey, convertSimpleOrDBObject(value, nestedEntity)); } return newDbo; } + /** + * Retriggers mapping if the given source is a {@link DBObject} or simply invokes the + * + * @param source + * @param entity + * @return + */ + private Object convertSimpleOrDBObject(Object source, MongoPersistentEntity entity) { + + if (source instanceof BasicDBList) { + return converter.convertToMongoType(source); + } + + if (source instanceof DBObject) { + return getMappedObject((DBObject) source, entity); + } + + return converter.convertToMongoType(source); + } + /** * Returns whether the given key will be considered an id key. * @@ -122,7 +147,19 @@ public class QueryMapper { return idProperty.getName().equals(key) || idProperty.getFieldName().equals(key); } - return Arrays.asList("id", "_id").contains(key); + return DEFAULT_ID_NAMES.contains(key); + } + + private MongoPersistentEntity getNestedEntity(MongoPersistentEntity entity, String key) { + + MongoPersistentProperty property = entity == null ? null : entity.getPersistentProperty(key); + + if (property == null || !property.isEntity()) { + return null; + } + + MappingContext, MongoPersistentProperty> context = converter.getMappingContext(); + return context.getPersistentEntity(property); } /** 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 1d6090cc5..6ac7326f7 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 @@ -32,10 +32,9 @@ import org.mockito.runners.MockitoJUnitRunner; import org.springframework.data.annotation.Id; import org.springframework.data.mongodb.MongoDbFactory; import org.springframework.data.mongodb.core.Person; -import org.springframework.data.mongodb.core.convert.MappingMongoConverter; -import org.springframework.data.mongodb.core.convert.QueryMapper; import org.springframework.data.mongodb.core.mapping.MongoMappingContext; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; +import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Query; @@ -188,6 +187,18 @@ public class QueryMapperUnitTests { assertThat(result.get("bar"), is(notNullValue())); } + /** + * @see DATAMONGO-429 + */ + @Test + public void transformsArraysCorrectly() { + + Query query = new BasicQuery("{ 'tags' : { '$all' : [ 'green', 'orange']}}"); + + DBObject result = mapper.getMappedObject(query.getQueryObject(), null); + assertThat(result, is(query.getQueryObject())); + } + class Sample { @Id