Browse Source

DATAMONGO-706 - Fixed DBRef conversion for nested keywords.

pull/45/head
Oliver Gierke 13 years ago
parent
commit
aa80d1ad0a
  1. 73
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
  2. 25
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

73
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java

@ -18,6 +18,7 @@ package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Set;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.springframework.core.convert.ConversionException; import org.springframework.core.convert.ConversionException;
@ -84,8 +85,22 @@ public class QueryMapper {
for (String key : query.keySet()) { for (String key : query.keySet()) {
if (Keyword.isKeyword(key)) {
result.putAll(getMappedKeyword(new Keyword(query, key), entity));
continue;
}
Field field = entity == null ? new Field(key) : new MetadataBackedField(key, entity, mappingContext); Field field = entity == null ? new Field(key) : new MetadataBackedField(key, entity, mappingContext);
result.put(field.getMappedKey(), getMappedValue(query.get(key), field));
Object rawValue = query.get(key);
String newKey = field.getMappedKey();
if (Keyword.isKeyword(rawValue) && !field.isIdField()) {
Keyword keyword = new Keyword((DBObject) rawValue);
result.put(newKey, getMappedKeyword(field, keyword));
} else {
result.put(newKey, getMappedValue(field, query.get(key)));
}
} }
return result; return result;
@ -101,13 +116,14 @@ public class QueryMapper {
private DBObject getMappedKeyword(Keyword query, MongoPersistentEntity<?> entity) { private DBObject getMappedKeyword(Keyword query, MongoPersistentEntity<?> entity) {
// $or/$nor // $or/$nor
if (query.key.matches(N_OR_PATTERN)) { if (query.key.matches(N_OR_PATTERN) || query.value instanceof Iterable) {
Iterable<?> conditions = (Iterable<?>) query.value; Iterable<?> conditions = (Iterable<?>) query.value;
BasicDBList newConditions = new BasicDBList(); BasicDBList newConditions = new BasicDBList();
for (Object condition : conditions) { for (Object condition : conditions) {
newConditions.add(getMappedObject((DBObject) condition, entity)); newConditions.add(condition instanceof DBObject ? getMappedObject((DBObject) condition, entity)
: convertSimpleOrDBObject(condition, entity));
} }
return new BasicDBObject(query.key, newConditions); return new BasicDBObject(query.key, newConditions);
@ -119,15 +135,15 @@ public class QueryMapper {
/** /**
* Returns the mapped keyword considered defining a criteria for the given property. * Returns the mapped keyword considered defining a criteria for the given property.
* *
* @param keyword
* @param property * @param property
* @param keyword
* @return * @return
*/ */
private DBObject getMappedKeyword(Keyword keyword, Field property) { private DBObject getMappedKeyword(Field property, Keyword keyword) {
boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists(); boolean needsAssociationConversion = property.isAssociation() && !keyword.isExists();
Object value = needsAssociationConversion ? convertAssociation(keyword.value, property.getProperty()) Object value = needsAssociationConversion ? convertAssociation(keyword.value, property.getProperty())
: getMappedValue(keyword.value, property.with(keyword.key)); : getMappedValue(property.with(keyword.key), keyword.value);
return new BasicDBObject(keyword.key, value); return new BasicDBObject(keyword.key, value);
} }
@ -136,17 +152,17 @@ public class QueryMapper {
* Returns the mapped value for the given source object assuming it's a value for the given * Returns the mapped value for the given source object assuming it's a value for the given
* {@link MongoPersistentProperty}. * {@link MongoPersistentProperty}.
* *
* @param source the source object to be mapped * @param value the source object to be mapped
* @param property the property the value is a value for * @param property the property the value is a value for
* @param newKey the key the value will be bound to eventually * @param newKey the key the value will be bound to eventually
* @return * @return
*/ */
private Object getMappedValue(Object source, Field key) { private Object getMappedValue(Field documentField, Object value) {
if (key.isIdField()) { if (documentField.isIdField()) {
if (source instanceof DBObject) { if (value instanceof DBObject) {
DBObject valueDbo = (DBObject) source; DBObject valueDbo = (DBObject) value;
if (valueDbo.containsField("$in") || valueDbo.containsField("$nin")) { if (valueDbo.containsField("$in") || valueDbo.containsField("$nin")) {
String inKey = valueDbo.containsField("$in") ? "$in" : "$nin"; String inKey = valueDbo.containsField("$in") ? "$in" : "$nin";
List<Object> ids = new ArrayList<Object>(); List<Object> ids = new ArrayList<Object>();
@ -157,22 +173,25 @@ public class QueryMapper {
} else if (valueDbo.containsField("$ne")) { } else if (valueDbo.containsField("$ne")) {
valueDbo.put("$ne", convertId(valueDbo.get("$ne"))); valueDbo.put("$ne", convertId(valueDbo.get("$ne")));
} else { } else {
return getMappedObject((DBObject) source, null); return getMappedObject((DBObject) value, null);
} }
return valueDbo; return valueDbo;
} else { } else {
return convertId(source); return convertId(value);
} }
} }
if (key.isAssociation()) { if (Keyword.isKeyword(value)) {
return Keyword.isKeyword(source) ? getMappedKeyword(new Keyword(source), key) : convertAssociation(source, return getMappedKeyword(new Keyword((DBObject) value), null);
key.getProperty()); }
if (documentField.isAssociation()) {
return convertAssociation(value, documentField.getProperty());
} }
return convertSimpleOrDBObject(source, key.getPropertyEntity()); return convertSimpleOrDBObject(value, documentField.getPropertyEntity());
} }
/** /**
@ -256,16 +275,18 @@ public class QueryMapper {
String key; String key;
Object value; Object value;
public Keyword(Object source) { public Keyword(DBObject source, String key) {
this.key = key;
Assert.isInstanceOf(DBObject.class, source); this.value = source.get(key);
}
DBObject value = (DBObject) source; public Keyword(DBObject dbObject) {
Assert.isTrue(value.keySet().size() == 1, "Keyword must have a single key only!"); Set<String> keys = dbObject.keySet();
Assert.isTrue(keys.size() == 1, "Can only use a single value DBObject!");
this.key = value.keySet().iterator().next(); this.key = keys.iterator().next();
this.value = value.get(key); this.value = dbObject.get(key);
} }
/** /**
@ -286,6 +307,10 @@ public class QueryMapper {
*/ */
public static boolean isKeyword(Object value) { public static boolean isKeyword(Object value) {
if (value instanceof String) {
return ((String) value).startsWith("$");
}
if (!(value instanceof DBObject)) { if (!(value instanceof DBObject)) {
return false; return false;
} }

25
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

@ -414,6 +414,30 @@ public class QueryMapperUnitTests {
assertThat(reference.get("$exists"), is((Object) false)); assertThat(reference.get("$exists"), is((Object) false));
} }
/**
* @see DATAMONGO-706
*/
@Test
public void convertsNestedDBRefsCorrectly() {
Reference reference = new Reference();
reference.id = 5L;
Query query = query(where("someString").is("foo").andOperator(where("reference").in(reference)));
BasicMongoPersistentEntity<?> entity = context.getPersistentEntity(WithDBRef.class);
DBObject mappedObject = mapper.getMappedObject(query.getQueryObject(), entity);
assertThat(mappedObject.get("someString"), is((Object) "foo"));
BasicDBList andClause = getAsDBList(mappedObject, "$and");
assertThat(andClause, hasSize(1));
BasicDBList inClause = getAsDBList(getAsDBObject(getAsDBObject(andClause, 0), "reference"), "$in");
assertThat(inClause, hasSize(1));
assertThat(inClause.get(0), is(instanceOf(com.mongodb.DBRef.class)));
}
class IdWrapper { class IdWrapper {
Object id; Object id;
} }
@ -450,6 +474,7 @@ public class QueryMapperUnitTests {
class WithDBRef { class WithDBRef {
String someString;
@DBRef Reference reference; @DBRef Reference reference;
} }

Loading…
Cancel
Save