Browse Source

Skip value conversion of `Pattern` and `BsonRegularExpression` in `QueryMapper`.

QueryMapper no longer attempts to convert regex objects when a field has an explicit write target.

Closes #4649
pull/4666/head
Mark Paluch 2 years ago
parent
commit
ac5c724ede
No known key found for this signature in database
GPG Key ID: 55BC6374BAA9D973
  1. 6
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/QueryMapper.java
  2. 99
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java

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

@ -32,6 +32,7 @@ import java.util.stream.Collectors;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.bson.BsonRegularExpression;
import org.bson.BsonValue; import org.bson.BsonValue;
import org.bson.Document; import org.bson.Document;
import org.bson.conversions.Bson; import org.bson.conversions.Bson;
@ -868,7 +869,7 @@ public class QueryMapper {
* conversions. In case of a {@link Collection} (used eg. for {@code $in} queries) the individual values will be * conversions. In case of a {@link Collection} (used eg. for {@code $in} queries) the individual values will be
* converted one by one. * converted one by one.
* *
* @param documentField the field and its meta data * @param documentField the field and its metadata
* @param value the actual value. Can be {@literal null}. * @param value the actual value. Can be {@literal null}.
* @return the potentially converted target value. * @return the potentially converted target value.
*/ */
@ -876,7 +877,8 @@ public class QueryMapper {
private Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Object value) { private Object applyFieldTargetTypeHintToValue(Field documentField, @Nullable Object value) {
if (value == null || documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget() if (value == null || documentField.getProperty() == null || !documentField.getProperty().hasExplicitWriteTarget()
|| value instanceof Document || value instanceof DBObject) { || value instanceof Document || value instanceof DBObject || value instanceof Pattern
|| value instanceof BsonRegularExpression) {
return value; return value;
} }

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

@ -29,12 +29,15 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.regex.Pattern;
import org.bson.BsonRegularExpression;
import org.bson.conversions.Bson; import org.bson.conversions.Bson;
import org.bson.types.Code; import org.bson.types.Code;
import org.bson.types.ObjectId; import org.bson.types.ObjectId;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.converter.Converter;
import org.springframework.data.annotation.Id; import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Transient; import org.springframework.data.annotation.Transient;
@ -52,8 +55,17 @@ import org.springframework.data.mongodb.core.aggregation.EvaluationOperators.Exp
import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext; import org.springframework.data.mongodb.core.aggregation.TypeBasedAggregationOperationContext;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint; import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
import org.springframework.data.mongodb.core.geo.GeoJsonPolygon; import org.springframework.data.mongodb.core.geo.GeoJsonPolygon;
import org.springframework.data.mongodb.core.mapping.*; import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.FieldName.Type; import org.springframework.data.mongodb.core.mapping.FieldName.Type;
import org.springframework.data.mongodb.core.mapping.FieldType;
import org.springframework.data.mongodb.core.mapping.MongoId;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity;
import org.springframework.data.mongodb.core.mapping.TextScore;
import org.springframework.data.mongodb.core.mapping.Unwrapped;
import org.springframework.data.mongodb.core.query.BasicQuery; import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.Criteria; import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query; import org.springframework.data.mongodb.core.query.Query;
@ -629,7 +641,7 @@ public class QueryMapperUnitTests {
org.bson.Document queryObject = query( org.bson.Document queryObject = query(
where("referenceList").is(new org.bson.Document("$nested", new org.bson.Document("$keys", 0L)))) where("referenceList").is(new org.bson.Document("$nested", new org.bson.Document("$keys", 0L))))
.getQueryObject(); .getQueryObject();
org.bson.Document mappedObject = mapper.getMappedObject(queryObject, org.bson.Document mappedObject = mapper.getMappedObject(queryObject,
context.getPersistentEntity(WithDBRefList.class)); context.getPersistentEntity(WithDBRefList.class));
@ -901,7 +913,7 @@ public class QueryMapperUnitTests {
@Test // GH-3688 @Test // GH-3688
void mappingShouldAllowSettingEntireNestedNumericKeyedMapValue() { void mappingShouldAllowSettingEntireNestedNumericKeyedMapValue() {
Query query = query(where("outerMap.1.map").is(null)); //newEntityWithComplexValueTypeMap() Query query = query(where("outerMap.1.map").is(null)); // newEntityWithComplexValueTypeMap()
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(EntityWithIntKeyedMapOfMap.class)); context.getPersistentEntity(EntityWithIntKeyedMapOfMap.class));
@ -1073,6 +1085,22 @@ public class QueryMapperUnitTests {
assertThat(document).isEqualTo(new org.bson.Document("scripts", new Code(script))); assertThat(document).isEqualTo(new org.bson.Document("scripts", new Code(script)));
} }
@Test // GH-4649
void shouldRetainRegexPattern() {
Query query = new Query(where("text").regex("foo"));
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WithExplicitTargetTypes.class));
assertThat(document.get("text")).isInstanceOf(Pattern.class);
query = new Query(where("text").regex(new BsonRegularExpression("foo")));
document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WithExplicitTargetTypes.class));
assertThat(document.get("text")).isInstanceOf(BsonRegularExpression.class);
}
@Test // DATAMONGO-2339 @Test // DATAMONGO-2339
void findByIdUsesMappedIdFieldNameWithUnderscoreCorrectly() { void findByIdUsesMappedIdFieldNameWithUnderscoreCorrectly() {
@ -1376,7 +1404,8 @@ public class QueryMapperUnitTests {
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WithPropertyUsingUnderscoreInName.class)); context.getPersistentEntity(WithPropertyUsingUnderscoreInName.class));
assertThat(document).isEqualTo(new org.bson.Document("fieldname_with_underscores", new org.bson.Document("$exists", true))); assertThat(document)
.isEqualTo(new org.bson.Document("fieldname_with_underscores", new org.bson.Document("$exists", true)));
} }
@Test // GH-3601 @Test // GH-3601
@ -1398,7 +1427,8 @@ public class QueryMapperUnitTests {
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class)); context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
assertThat(document).isEqualTo(new org.bson.Document("simple.fieldname_with_underscores", new org.bson.Document("$exists", true))); assertThat(document)
.isEqualTo(new org.bson.Document("simple.fieldname_with_underscores", new org.bson.Document("$exists", true)));
} }
@Test // GH-3601 @Test // GH-3601
@ -1420,7 +1450,8 @@ public class QueryMapperUnitTests {
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class)); context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
assertThat(document).isEqualTo(new org.bson.Document("double_underscore.fieldname_with_underscores", new org.bson.Document("$exists", true))); assertThat(document).isEqualTo(
new org.bson.Document("double_underscore.fieldname_with_underscores", new org.bson.Document("$exists", true)));
} }
@Test // GH-3601 @Test // GH-3601
@ -1431,7 +1462,8 @@ public class QueryMapperUnitTests {
org.bson.Document document = mapper.getMappedObject(query.getQueryObject(), org.bson.Document document = mapper.getMappedObject(query.getQueryObject(),
context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class)); context.getPersistentEntity(WrapperAroundWithPropertyUsingUnderscoreInName.class));
assertThat(document).isEqualTo(new org.bson.Document("double_underscore.renamed", new org.bson.Document("$exists", true))); assertThat(document)
.isEqualTo(new org.bson.Document("double_underscore.renamed", new org.bson.Document("$exists", true)));
} }
@Test // GH-3633 @Test // GH-3633
@ -1469,7 +1501,8 @@ public class QueryMapperUnitTests {
Query query = query(where("address.street").is("1007 Mountain Drive")); Query query = query(where("address.street").is("1007 Mountain Drive"));
MongoCustomConversions mongoCustomConversions = new MongoCustomConversions(Collections.singletonList(new MyAddressToDocumentConverter())); MongoCustomConversions mongoCustomConversions = new MongoCustomConversions(
Collections.singletonList(new MyAddressToDocumentConverter()));
this.context = new MongoMappingContext(); this.context = new MongoMappingContext();
this.context.setSimpleTypeHolder(mongoCustomConversions.getSimpleTypeHolder()); this.context.setSimpleTypeHolder(mongoCustomConversions.getSimpleTypeHolder());
@ -1481,7 +1514,8 @@ public class QueryMapperUnitTests {
this.mapper = new QueryMapper(converter); this.mapper = new QueryMapper(converter);
assertThat(mapper.getMappedSort(query.getQueryObject(), context.getPersistentEntity(Customer.class))).isEqualTo(new org.bson.Document("address.street", "1007 Mountain Drive")); assertThat(mapper.getMappedSort(query.getQueryObject(), context.getPersistentEntity(Customer.class)))
.isEqualTo(new org.bson.Document("address.street", "1007 Mountain Drive"));
} }
@Test // GH-3790 @Test // GH-3790
@ -1502,7 +1536,8 @@ public class QueryMapperUnitTests {
@Test // GH-3668 @Test // GH-3668
void mapStringIdFieldProjection() { void mapStringIdFieldProjection() {
org.bson.Document mappedFields = mapper.getMappedFields(new org.bson.Document("id", 1), context.getPersistentEntity(WithStringId.class)); org.bson.Document mappedFields = mapper.getMappedFields(new org.bson.Document("id", 1),
context.getPersistentEntity(WithStringId.class));
assertThat(mappedFields).containsEntry("_id", 1); assertThat(mappedFields).containsEntry("_id", 1);
} }
@ -1528,7 +1563,8 @@ public class QueryMapperUnitTests {
@Test // GH-3596 @Test // GH-3596
void considersValueConverterWhenPresent() { void considersValueConverterWhenPresent() {
org.bson.Document mappedObject = mapper.getMappedObject(new org.bson.Document("text", "value"), context.getPersistentEntity(WithPropertyValueConverter.class)); org.bson.Document mappedObject = mapper.getMappedObject(new org.bson.Document("text", "value"),
context.getPersistentEntity(WithPropertyValueConverter.class));
assertThat(mappedObject).isEqualTo(new org.bson.Document("text", "eulav")); assertThat(mappedObject).isEqualTo(new org.bson.Document("text", "eulav"));
} }
@ -1589,7 +1625,8 @@ public class QueryMapperUnitTests {
@Test // GH-4464 @Test // GH-4464
void usesKeyNameWithDotsIfFieldNameTypeIsKey() { void usesKeyNameWithDotsIfFieldNameTypeIsKey() {
org.bson.Document mappedObject = mapper.getMappedObject(query(where("value").is("A")).getQueryObject(), context.getPersistentEntity(WithPropertyHavingDotsInFieldName.class)); org.bson.Document mappedObject = mapper.getMappedObject(query(where("value").is("A")).getQueryObject(),
context.getPersistentEntity(WithPropertyHavingDotsInFieldName.class));
assertThat(mappedObject).isEqualTo("{ 'field.name.with.dots' : 'A' }"); assertThat(mappedObject).isEqualTo("{ 'field.name.with.dots' : 'A' }");
} }
@ -1606,7 +1643,8 @@ public class QueryMapperUnitTests {
@Test // GH-4510 @Test // GH-4510
void convertsNestedOperatorValueForPropertyThatHasValueConverter() { void convertsNestedOperatorValueForPropertyThatHasValueConverter() {
org.bson.Document mappedObject = mapper.getMappedObject(query(where("text").gt("spring").lt( "data")).getQueryObject(), org.bson.Document mappedObject = mapper.getMappedObject(
query(where("text").gt("spring").lt("data")).getQueryObject(),
context.getPersistentEntity(WithPropertyValueConverter.class)); context.getPersistentEntity(WithPropertyValueConverter.class));
assertThat(mappedObject).isEqualTo("{ 'text' : { $gt : 'gnirps', $lt : 'atad' } }"); assertThat(mappedObject).isEqualTo("{ 'text' : { $gt : 'gnirps', $lt : 'atad' } }");
@ -1615,7 +1653,8 @@ public class QueryMapperUnitTests {
@Test // GH-4510 @Test // GH-4510
void convertsNestedOperatorValueForPropertyContainingListThatHasValueConverter() { void convertsNestedOperatorValueForPropertyContainingListThatHasValueConverter() {
org.bson.Document mappedObject = mapper.getMappedObject(query(where("text").gt("spring").in( "data")).getQueryObject(), org.bson.Document mappedObject = mapper.getMappedObject(
query(where("text").gt("spring").in("data")).getQueryObject(),
context.getPersistentEntity(WithPropertyValueConverter.class)); context.getPersistentEntity(WithPropertyValueConverter.class));
assertThat(mappedObject).isEqualTo("{ 'text' : { $gt : 'gnirps', $in : [ 'atad' ] } }"); assertThat(mappedObject).isEqualTo("{ 'text' : { $gt : 'gnirps', $in : [ 'atad' ] } }");
@ -1752,23 +1791,20 @@ public class QueryMapperUnitTests {
private String name; private String name;
@DocumentReference(lookup = "{ 'name' : ?#{#target} }") @DocumentReference(lookup = "{ 'name' : ?#{#target} }") private Customer customer;
private Customer customer;
@DocumentReference(lookup = "{ 'name' : ?#{#target} }") @DocumentReference(lookup = "{ 'name' : ?#{#target} }") private List<Customer> customers;
private List<Customer> customers;
@DocumentReference @DocumentReference private Sample sample;
private Sample sample;
@DocumentReference @DocumentReference private List<Sample> samples;
private List<Sample> samples;
} }
class WithTextScoreProperty { class WithTextScoreProperty {
@Id String id; @Id String id;
@TextScore @Field("score") Float textScore; @TextScore
@Field("score") Float textScore;
} }
static class RootForClassWithExplicitlyRenamedIdField { static class RootForClassWithExplicitlyRenamedIdField {
@ -1805,7 +1841,7 @@ public class QueryMapperUnitTests {
Map<Integer, SimpleEntityWithoutId> map; Map<Integer, SimpleEntityWithoutId> map;
} }
static class EntityWithIntKeyedMapOfMap{ static class EntityWithIntKeyedMapOfMap {
Map<Integer, EntityWithComplexValueTypeMap> outerMap; Map<Integer, EntityWithComplexValueTypeMap> outerMap;
} }
@ -1818,6 +1854,9 @@ public class QueryMapperUnitTests {
@Field(targetType = FieldType.SCRIPT) // @Field(targetType = FieldType.SCRIPT) //
String script; String script;
@Field(targetType = FieldType.STRING) //
String text;
@Field(targetType = FieldType.SCRIPT) // @Field(targetType = FieldType.SCRIPT) //
List<String> scripts; List<String> scripts;
} }
@ -1887,15 +1926,13 @@ public class QueryMapperUnitTests {
String fieldname_with_underscores; String fieldname_with_underscores;
@Field("renamed") @Field("renamed") String renamed_fieldname_with_underscores;
String renamed_fieldname_with_underscores;
} }
@Document @Document
static class Customer { static class Customer {
@Id @Id private ObjectId id;
private ObjectId id;
private String name; private String name;
private MyAddress address; private MyAddress address;
} }
@ -1906,8 +1943,7 @@ public class QueryMapperUnitTests {
static class WithPropertyValueConverter { static class WithPropertyValueConverter {
@ValueConverter(ReversingValueConverter.class) @ValueConverter(ReversingValueConverter.class) String text;
String text;
} }
@WritingConverter @WritingConverter
@ -1923,8 +1959,7 @@ public class QueryMapperUnitTests {
static class WithPropertyHavingDotsInFieldName { static class WithPropertyHavingDotsInFieldName {
@Field(name = "field.name.with.dots", nameType = Type.KEY) @Field(name = "field.name.with.dots", nameType = Type.KEY) String value;
String value;
} }
} }

Loading…
Cancel
Save