Skip to content

Commit 3dfc59b

Browse files
committed
DATAMONGO-429 - Fixed handling of nested arrays in QueryMapper.
QueryMapper now correctly transforms arrays not concreting them into BasicDBObjects anymore.
1 parent 9e50621 commit 3dfc59b

File tree

2 files changed

+60
-12
lines changed

2 files changed

+60
-12
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java

+47-10
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@
2525
import org.springframework.core.convert.ConversionException;
2626
import org.springframework.core.convert.ConversionService;
2727
import org.springframework.data.mapping.PersistentEntity;
28+
import org.springframework.data.mapping.context.MappingContext;
2829
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2930
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
3031
import org.springframework.util.Assert;
3132

33+
import com.mongodb.BasicDBList;
3234
import com.mongodb.BasicDBObject;
3335
import com.mongodb.DBObject;
3436

@@ -40,6 +42,9 @@
4042
*/
4143
public class QueryMapper {
4244

45+
private static final List<String> DEFAULT_ID_NAMES = Arrays.asList("id", "_id");
46+
private static final String N_OR_PATTERN = "\\$.*or";
47+
4348
private final ConversionService conversionService;
4449
private final MongoConverter converter;
4550

@@ -58,17 +63,20 @@ public QueryMapper(MongoConverter converter) {
5863
* Replaces the property keys used in the given {@link DBObject} with the appropriate keys by using the
5964
* {@link PersistentEntity} metadata.
6065
*
61-
* @param query
62-
* @param entity
66+
* @param query must not be {@literal null}.
67+
* @param entity can be {@literal null}.
6368
* @return
6469
*/
6570
public DBObject getMappedObject(DBObject query, MongoPersistentEntity<?> entity) {
6671

6772
DBObject newDbo = new BasicDBObject();
6873

6974
for (String key : query.keySet()) {
75+
76+
MongoPersistentEntity<?> nestedEntity = getNestedEntity(entity, key);
7077
String newKey = key;
7178
Object value = query.get(key);
79+
7280
if (isIdKey(key, entity)) {
7381
if (value instanceof DBObject) {
7482
DBObject valueDbo = (DBObject) value;
@@ -80,34 +88,51 @@ public DBObject getMappedObject(DBObject query, MongoPersistentEntity<?> entity)
8088
}
8189
valueDbo.put(inKey, ids.toArray(new Object[ids.size()]));
8290
} else {
83-
value = getMappedObject((DBObject) value, entity);
91+
value = getMappedObject((DBObject) value, nestedEntity);
8492
}
8593
} else {
8694
value = convertId(value);
8795
}
8896
newKey = "_id";
89-
} else if (key.startsWith("$") && key.endsWith("or")) {
97+
} else if (key.matches(N_OR_PATTERN)) {
9098
// $or/$nor
9199
Iterable<?> conditions = (Iterable<?>) value;
92100
BasicBSONList newConditions = new BasicBSONList();
93101
Iterator<?> iter = conditions.iterator();
94102
while (iter.hasNext()) {
95-
newConditions.add(getMappedObject((DBObject) iter.next(), entity));
103+
newConditions.add(getMappedObject((DBObject) iter.next(), nestedEntity));
96104
}
97105
value = newConditions;
98106
} else if (key.equals("$ne")) {
99107
value = convertId(value);
100-
} else if (value instanceof DBObject) {
101-
newDbo.put(newKey, getMappedObject((DBObject) value, entity));
102-
continue;
103108
}
104109

105-
newDbo.put(newKey, converter.convertToMongoType(value));
110+
newDbo.put(newKey, convertSimpleOrDBObject(value, nestedEntity));
106111
}
107112

108113
return newDbo;
109114
}
110115

116+
/**
117+
* Retriggers mapping if the given source is a {@link DBObject} or simply invokes the
118+
*
119+
* @param source
120+
* @param entity
121+
* @return
122+
*/
123+
private Object convertSimpleOrDBObject(Object source, MongoPersistentEntity<?> entity) {
124+
125+
if (source instanceof BasicDBList) {
126+
return converter.convertToMongoType(source);
127+
}
128+
129+
if (source instanceof DBObject) {
130+
return getMappedObject((DBObject) source, entity);
131+
}
132+
133+
return converter.convertToMongoType(source);
134+
}
135+
111136
/**
112137
* Returns whether the given key will be considered an id key.
113138
*
@@ -122,7 +147,19 @@ private boolean isIdKey(String key, MongoPersistentEntity<?> entity) {
122147
return idProperty.getName().equals(key) || idProperty.getFieldName().equals(key);
123148
}
124149

125-
return Arrays.asList("id", "_id").contains(key);
150+
return DEFAULT_ID_NAMES.contains(key);
151+
}
152+
153+
private MongoPersistentEntity<?> getNestedEntity(MongoPersistentEntity<?> entity, String key) {
154+
155+
MongoPersistentProperty property = entity == null ? null : entity.getPersistentProperty(key);
156+
157+
if (property == null || !property.isEntity()) {
158+
return null;
159+
}
160+
161+
MappingContext<? extends MongoPersistentEntity<?>, MongoPersistentProperty> context = converter.getMappingContext();
162+
return context.getPersistentEntity(property);
126163
}
127164

128165
/**

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@
3232
import org.springframework.data.annotation.Id;
3333
import org.springframework.data.mongodb.MongoDbFactory;
3434
import org.springframework.data.mongodb.core.Person;
35-
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
36-
import org.springframework.data.mongodb.core.convert.QueryMapper;
3735
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
3836
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
37+
import org.springframework.data.mongodb.core.query.BasicQuery;
3938
import org.springframework.data.mongodb.core.query.Criteria;
4039
import org.springframework.data.mongodb.core.query.Query;
4140

@@ -188,6 +187,18 @@ public void handlesAllPropertiesIfDBObject() {
188187
assertThat(result.get("bar"), is(notNullValue()));
189188
}
190189

190+
/**
191+
* @see DATAMONGO-429
192+
*/
193+
@Test
194+
public void transformsArraysCorrectly() {
195+
196+
Query query = new BasicQuery("{ 'tags' : { '$all' : [ 'green', 'orange']}}");
197+
198+
DBObject result = mapper.getMappedObject(query.getQueryObject(), null);
199+
assertThat(result, is(query.getQueryObject()));
200+
}
201+
191202
class Sample {
192203

193204
@Id

0 commit comments

Comments
 (0)