Browse Source

DATAMONGO-2440 - Fix field target type conversion for collection values.

We now preserve the collection nature of the source type when applying custom target type conversions. Prior to this change collection values had been changed to single element causing query errors in MongoDB when using $in queries.

Original pull request: #817.
pull/819/head
Christoph Strobl 6 years ago committed by Mark Paluch
parent
commit
96c4901e7f
No known key found for this signature in database
GPG Key ID: 51A00FA751B91849
  1. 52
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
  2. 32
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

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

@ -15,25 +15,16 @@ @@ -15,25 +15,16 @@
*/
package org.springframework.data.mongodb.core.convert;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.types.ObjectId;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.converter.Converter;
import org.springframework.data.domain.Example;
@ -329,18 +320,14 @@ public class QueryMapper { @@ -329,18 +320,14 @@ public class QueryMapper {
* {@link MongoPersistentProperty}.
*
* @param documentField the key the value will be bound to eventually
* @param value the source object to be mapped
* @param sourceValue the source object to be mapped
* @return
*/
@Nullable
@SuppressWarnings("unchecked")
protected Object getMappedValue(Field documentField, Object value) {
protected Object getMappedValue(Field documentField, Object sourceValue) {
if (documentField.getProperty() != null && documentField.getProperty().hasExplicitWriteTarget()) {
if (conversionService.canConvert(value.getClass(), documentField.getProperty().getFieldType())) {
value = conversionService.convert(value, documentField.getProperty().getFieldType());
}
}
Object value = applyFieldTargetTypeHintToValue(documentField, sourceValue);
if (documentField.isIdField() && !documentField.isAssociation()) {
@ -681,6 +668,35 @@ public class QueryMapper { @@ -681,6 +668,35 @@ public class QueryMapper {
return candidate.startsWith("$");
}
/**
* Convert the given field value into its desired
* {@link org.springframework.data.mongodb.core.mapping.Field#targetType() target type} before applying further
* conversions. In case of a {@link Collection} (used eg. for {@code $in} queries) the individual values will be
* converted one by one.
*
* @param documentField the field and its meta data
* @param value the actual value
* @return the potentially converted target value.
*/
private Object applyFieldTargetTypeHintToValue(Field documentField, Object value) {
if (documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget()) {
return value;
}
if (!conversionService.canConvert(value.getClass(), documentField.getProperty().getFieldType())) {
return value;
}
if (value instanceof Collection) {
return ((Collection<Object>) value).stream()
.map(it -> conversionService.convert(it, documentField.getProperty().getFieldType()))
.collect(Collectors.toList());
}
return conversionService.convert(value, documentField.getProperty().getFieldType());
}
/**
* Value object to capture a query keyword representation.
*

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

@ -901,6 +901,32 @@ public class QueryMapperUnitTests { @@ -901,6 +901,32 @@ public class QueryMapperUnitTests {
assertThat(document).containsEntry("geoJsonPoint.$near.$maxDistance", 1000.0D);
}
@Test // DATAMONGO-2440
void convertsInWithNonIdFieldAndObjectIdTypeHintCorrectly() {
String id = new ObjectId().toHexString();
NonIdFieldWithObjectIdTargetType source = new NonIdFieldWithObjectIdTargetType();
source.stringAsOid = id;
org.bson.Document target = mapper.getMappedObject(query(where("stringAsOid").in(id)).getQueryObject(),
context.getPersistentEntity(NonIdFieldWithObjectIdTargetType.class));
assertThat(target).isEqualTo(org.bson.Document.parse("{\"stringAsOid\": {\"$in\": [{\"$oid\": \"" + id + "\"}]}}"));
}
@Test // DATAMONGO-2440
void convertsInWithIdFieldAndObjectIdTypeHintCorrectly() {
String id = new ObjectId().toHexString();
NonIdFieldWithObjectIdTargetType source = new NonIdFieldWithObjectIdTargetType();
source.id = id;
org.bson.Document target = mapper.getMappedObject(query(where("id").in(id)).getQueryObject(),
context.getPersistentEntity(NonIdFieldWithObjectIdTargetType.class));
assertThat(target).isEqualTo(org.bson.Document.parse("{\"_id\": {\"$in\": [{\"$oid\": \"" + id + "\"}]}}"));
}
@Document
public class Foo {
@Id private ObjectId id;
@ -1037,4 +1063,10 @@ public class QueryMapperUnitTests { @@ -1037,4 +1063,10 @@ public class QueryMapperUnitTests {
static class WithIdPropertyContainingUnderscore {
@Id String with_underscore;
}
static class NonIdFieldWithObjectIdTargetType {
String id;
@Field(targetType = FieldType.OBJECT_ID) String stringAsOid;
}
}

Loading…
Cancel
Save