Skip to content

Commit a4497bc

Browse files
committed
DATAMONGO-1070 - Fixed a few glitches in DBRef binding for repository query methods.
The QueryMapping for derived repository queries pointing to the identifier of the referenced document. We now reduce the query field's key from reference.id to reference so that the generated DBRef is applied correctly and also take care that the id's are potentially converted to ObjectIds. This is mainly achieved by using the AssociationConverter pulled up from UpdateMapper in ObjectMapper.getMappedKey(). MongoQueryCreator now refrains from translating the field keys as that will fail the QueryMapper to correctly detect id properties. Fixed DBRef handling for StringBasedMongoQuery which previously didn't parse the DBRef instance created after JSON parsing for placeholders.
1 parent 6a82c47 commit a4497bc

File tree

7 files changed

+118
-75
lines changed

7 files changed

+118
-75
lines changed

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

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,8 @@ protected boolean isAssociationConversionNecessary(Field documentField, Object v
334334
}
335335

336336
MongoPersistentEntity<?> entity = documentField.getPropertyEntity();
337-
return entity.hasIdProperty() && entity.getIdProperty().getActualType().isAssignableFrom(type);
337+
return entity.hasIdProperty()
338+
&& (type.equals(DBRef.class) || entity.getIdProperty().getActualType().isAssignableFrom(type));
338339
}
339340

340341
/**
@@ -382,10 +383,16 @@ protected Object convertAssociation(Object source, Field field) {
382383
*/
383384
protected Object convertAssociation(Object source, MongoPersistentProperty property) {
384385

385-
if (property == null || source == null || source instanceof DBRef || source instanceof DBObject) {
386+
if (property == null || source == null || source instanceof DBObject) {
386387
return source;
387388
}
388389

390+
if (source instanceof DBRef) {
391+
392+
DBRef ref = (DBRef) source;
393+
return new DBRef(ref.getDB(), ref.getRef(), convertId(ref.getId()));
394+
}
395+
389396
if (source instanceof Iterable) {
390397
BasicDBList result = new BasicDBList();
391398
for (Object element : (Iterable<?>) source) {
@@ -785,7 +792,8 @@ private final Association<MongoPersistentProperty> findAssociation() {
785792
*/
786793
@Override
787794
public String getMappedKey() {
788-
return path == null ? name : path.toDotPath(getPropertyConverter());
795+
return path == null ? name : path.toDotPath(isAssociation() ? new AssociationConverter(getAssociation())
796+
: getPropertyConverter());
789797
}
790798

791799
protected PersistentPropertyPath<MongoPersistentProperty> getPath() {
@@ -838,4 +846,44 @@ protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
838846
return PropertyToFieldNameConverter.INSTANCE;
839847
}
840848
}
849+
850+
/**
851+
* Converter to skip all properties after an association property was rendered.
852+
*
853+
* @author Oliver Gierke
854+
*/
855+
protected static class AssociationConverter implements Converter<MongoPersistentProperty, String> {
856+
857+
private final MongoPersistentProperty property;
858+
private boolean associationFound;
859+
860+
/**
861+
* Creates a new {@link AssociationConverter} for the given {@link Association}.
862+
*
863+
* @param association must not be {@literal null}.
864+
*/
865+
public AssociationConverter(Association<MongoPersistentProperty> association) {
866+
867+
Assert.notNull(association, "Association must not be null!");
868+
this.property = association.getInverse();
869+
}
870+
871+
/*
872+
* (non-Javadoc)
873+
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
874+
*/
875+
@Override
876+
public String convert(MongoPersistentProperty source) {
877+
878+
if (associationFound) {
879+
return null;
880+
}
881+
882+
if (property.equals(source)) {
883+
associationFound = true;
884+
}
885+
886+
return source.getFieldName();
887+
}
888+
}
841889
}

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

Lines changed: 0 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.Map.Entry;
2121

2222
import org.springframework.core.convert.converter.Converter;
23-
import org.springframework.data.mapping.Association;
2423
import org.springframework.data.mapping.context.MappingContext;
2524
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
2625
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
@@ -197,46 +196,6 @@ protected Converter<MongoPersistentProperty, String> getPropertyConverter() {
197196
return isAssociation() ? new AssociationConverter(getAssociation()) : new UpdatePropertyConverter(key);
198197
}
199198

200-
/**
201-
* Converter to skip all properties after an association property was rendered.
202-
*
203-
* @author Oliver Gierke
204-
*/
205-
private static class AssociationConverter implements Converter<MongoPersistentProperty, String> {
206-
207-
private final MongoPersistentProperty property;
208-
private boolean associationFound;
209-
210-
/**
211-
* Creates a new {@link AssociationConverter} for the given {@link Association}.
212-
*
213-
* @param association must not be {@literal null}.
214-
*/
215-
public AssociationConverter(Association<MongoPersistentProperty> association) {
216-
217-
Assert.notNull(association, "Association must not be null!");
218-
this.property = association.getInverse();
219-
}
220-
221-
/*
222-
* (non-Javadoc)
223-
* @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object)
224-
*/
225-
@Override
226-
public String convert(MongoPersistentProperty source) {
227-
228-
if (associationFound) {
229-
return null;
230-
}
231-
232-
if (property.equals(source)) {
233-
associationFound = true;
234-
}
235-
236-
return source.getFieldName();
237-
}
238-
}
239-
240199
/**
241200
* Special {@link Converter} for {@link MongoPersistentProperty} instances that will concatenate the {@literal $}
242201
* contained in the source update key.

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -102,9 +102,7 @@ protected Criteria create(Part part, Iterator<Object> iterator) {
102102

103103
PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
104104
MongoPersistentProperty property = path.getLeafProperty();
105-
Criteria criteria = from(part, property,
106-
where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
107-
(PotentiallyConvertingIterator) iterator);
105+
Criteria criteria = from(part, property, where(path.toDotPath()), (PotentiallyConvertingIterator) iterator);
108106

109107
return criteria;
110108
}
@@ -123,9 +121,7 @@ protected Criteria and(Part part, Criteria base, Iterator<Object> iterator) {
123121
PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
124122
MongoPersistentProperty property = path.getLeafProperty();
125123

126-
return from(part, property,
127-
base.and(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
128-
(PotentiallyConvertingIterator) iterator);
124+
return from(part, property, base.and(path.toDotPath()), (PotentiallyConvertingIterator) iterator);
129125
}
130126

131127
/*

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQuery.java

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.util.StringUtils;
3030

3131
import com.mongodb.DBObject;
32+
import com.mongodb.DBRef;
3233
import com.mongodb.util.JSON;
3334

3435
/**
@@ -199,6 +200,7 @@ private static enum ParameterBindingParser {
199200
* {@link Collections#emptyList()}.
200201
*
201202
* @param input
203+
* @param conversionService must not be {@literal null}.
202204
* @return
203205
*/
204206
public List<ParameterBinding> parseParameterBindingsFrom(String input) {
@@ -229,14 +231,7 @@ private void collectParameterReferencesIntoBindings(List<ParameterBinding> bindi
229231
if (value instanceof String) {
230232

231233
String string = ((String) value).trim();
232-
233-
Matcher valueMatcher = PARSEABLE_BINDING_PATTERN.matcher(string);
234-
while (valueMatcher.find()) {
235-
int paramIndex = Integer.parseInt(valueMatcher.group(PARAMETER_INDEX_GROUP));
236-
boolean quoted = (string.startsWith("'") && string.endsWith("'"))
237-
|| (string.startsWith("\"") && string.endsWith("\""));
238-
bindings.add(new ParameterBinding(paramIndex, quoted));
239-
}
234+
potentiallyAddBinding(string, bindings);
240235

241236
} else if (value instanceof Pattern) {
242237

@@ -255,6 +250,13 @@ private void collectParameterReferencesIntoBindings(List<ParameterBinding> bindi
255250
bindings.add(new ParameterBinding(paramIndex, quoted));
256251
}
257252

253+
} else if (value instanceof DBRef) {
254+
255+
DBRef dbref = (DBRef) value;
256+
257+
potentiallyAddBinding(dbref.getRef(), bindings);
258+
potentiallyAddBinding(dbref.getId().toString(), bindings);
259+
258260
} else if (value instanceof DBObject) {
259261

260262
DBObject dbo = (DBObject) value;
@@ -264,6 +266,20 @@ private void collectParameterReferencesIntoBindings(List<ParameterBinding> bindi
264266
}
265267
}
266268
}
269+
270+
private void potentiallyAddBinding(String source, List<ParameterBinding> bindings) {
271+
272+
Matcher valueMatcher = PARSEABLE_BINDING_PATTERN.matcher(source);
273+
274+
while (valueMatcher.find()) {
275+
276+
int paramIndex = Integer.parseInt(valueMatcher.group(PARAMETER_INDEX_GROUP));
277+
boolean quoted = (source.startsWith("'") && source.endsWith("'"))
278+
|| (source.startsWith("\"") && source.endsWith("\""));
279+
280+
bindings.add(new ParameterBinding(paramIndex, quoted));
281+
}
282+
}
267283
}
268284

269285
/**

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -658,6 +658,22 @@ public void getMappedSortIgnoresTextScoreWhenNotSortedByScore() {
658658
assertThat(dbo, equalTo(new BasicDBObjectBuilder().add("_id", 1).get()));
659659
}
660660

661+
/**
662+
* @see DATAMONGO-1070
663+
*/
664+
@Test
665+
public void mapsIdReferenceToDBRefCorrectly() {
666+
667+
ObjectId id = new ObjectId();
668+
669+
DBObject query = new BasicDBObject("reference.id", new com.mongodb.DBRef(null, "reference", id.toString()));
670+
DBObject result = mapper.getMappedObject(query, context.getPersistentEntity(WithDBRef.class));
671+
672+
assertThat(result.containsField("reference"), is(true));
673+
com.mongodb.DBRef reference = getTypedValue(result, "reference", com.mongodb.DBRef.class);
674+
assertThat(reference.getId(), is(instanceOf(ObjectId.class)));
675+
}
676+
661677
@Document
662678
public class Foo {
663679
@Id private ObjectId id;

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java

Lines changed: 3 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -167,19 +167,6 @@ public void createsGreaterThanEqualQueryCorrectly() throws Exception {
167167
assertThat(creator.createQuery(), is(reference));
168168
}
169169

170-
/**
171-
* @see DATAMONGO-291
172-
*/
173-
@Test
174-
public void honoursMappingInformationForPropertyPaths() {
175-
176-
PartTree partTree = new PartTree("findByUsername", User.class);
177-
178-
MongoQueryCreator creator = new MongoQueryCreator(partTree, getAccessor(converter, "Oliver"), context);
179-
Query reference = query(where("foo").is("Oliver"));
180-
assertThat(creator.createQuery(), is(reference));
181-
}
182-
183170
/**
184171
* @see DATAMONGO-338
185172
*/
@@ -268,7 +255,7 @@ public void createsQueryWithStartingWithPredicateCorrectly() {
268255
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "Matt"), context);
269256
Query query = creator.createQuery();
270257

271-
assertThat(query, is(query(where("foo").regex("^Matt"))));
258+
assertThat(query, is(query(where("username").regex("^Matt"))));
272259
}
273260

274261
/**
@@ -281,7 +268,7 @@ public void createsQueryWithEndingWithPredicateCorrectly() {
281268
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "ews"), context);
282269
Query query = creator.createQuery();
283270

284-
assertThat(query, is(query(where("foo").regex("ews$"))));
271+
assertThat(query, is(query(where("username").regex("ews$"))));
285272
}
286273

287274
/**
@@ -294,7 +281,7 @@ public void createsQueryWithContainingPredicateCorrectly() {
294281
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "thew"), context);
295282
Query query = creator.createQuery();
296283

297-
assertThat(query, is(query(where("foo").regex(".*thew.*"))));
284+
assertThat(query, is(query(where("username").regex(".*thew.*"))));
298285
}
299286

300287
private void assertBindsDistanceToQuery(Point point, Distance distance, Query reference) throws Exception {

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/StringBasedMongoQueryUnitTests.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.junit.runner.RunWith;
3030
import org.mockito.Mock;
3131
import org.mockito.runners.MockitoJUnitRunner;
32+
import org.springframework.data.mongodb.core.DBObjectTestUtils;
3233
import org.springframework.data.mongodb.core.MongoOperations;
3334
import org.springframework.data.mongodb.core.convert.DbRefResolver;
3435
import org.springframework.data.mongodb.core.convert.DefaultMongoTypeMapper;
@@ -43,6 +44,7 @@
4344

4445
import com.mongodb.BasicDBObject;
4546
import com.mongodb.DBObject;
47+
import com.mongodb.DBRef;
4648

4749
/**
4850
* Unit tests for {@link StringBasedMongoQuery}.
@@ -255,6 +257,22 @@ public void bindsSimplePropertyWithRegexCorrectly() throws Exception {
255257
assertThat(query.getQueryObject(), is(reference.getQueryObject()));
256258
}
257259

260+
/**
261+
* @see DATAMONGO-1070
262+
*/
263+
@Test
264+
public void parsesDbRefDeclarationsCorrectly() throws Exception {
265+
266+
StringBasedMongoQuery mongoQuery = createQueryForMethod("methodWithManuallyDefinedDbRef", String.class);
267+
ConvertingParameterAccessor parameterAccessor = StubParameterAccessor.getAccessor(converter, "myid");
268+
269+
org.springframework.data.mongodb.core.query.Query query = mongoQuery.createQuery(parameterAccessor);
270+
271+
DBRef dbRef = DBObjectTestUtils.getTypedValue(query.getQueryObject(), "reference", DBRef.class);
272+
assertThat(dbRef.getId(), is((Object) "myid"));
273+
assertThat(dbRef.getRef(), is("reference"));
274+
}
275+
258276
private StringBasedMongoQuery createQueryForMethod(String name, Class<?>... parameters) throws Exception {
259277

260278
Method method = SampleRepository.class.getMethod(name, parameters);
@@ -291,7 +309,10 @@ private interface SampleRepository {
291309
@Query("{'title': { $regex : '^?0', $options : 'i'}}")
292310
List<DBObject> findByTitleBeginsWithExplicitQuoting(String title);
293311

294-
@Query(value = "{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}")
312+
@Query("{$where: 'return this.date.getUTCMonth() == ?2 && this.date.getUTCDay() == ?3;'}")
295313
List<DBObject> findByQueryWithParametersInExpression(int param1, int param2, int param3, int param4);
314+
315+
@Query("{ 'reference' : { $ref : 'reference', $id : ?0 }}")
316+
Object methodWithManuallyDefinedDbRef(String id);
296317
}
297318
}

0 commit comments

Comments
 (0)