Browse Source

hacking - apply value conversion at query creation time for regex.

issue/4346
Christoph Strobl 3 years ago
parent
commit
367cd61e35
No known key found for this signature in database
GPG Key ID: 8CC1AB53391458C8
  1. 33
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java
  2. 40
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoCustomConversions.java
  3. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoWriter.java
  4. 9
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/ConvertingParameterAccessor.java
  5. 13
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/repository/query/MongoQueryCreator.java
  6. 6
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReversingValueConverter.java
  7. 11
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/QueryMapperUnitTests.java
  8. 3
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/UpdateMapperUnitTests.java
  9. 15
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java
  10. 13
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java
  11. 1
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java
  12. 44
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/query/MongoQueryCreatorUnitTests.java
  13. 4
      spring-data-mongodb/src/test/resources/logback.xml

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

@ -1571,6 +1571,39 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App @@ -1571,6 +1571,39 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App
return newDocument;
}
@Nullable
@Override
public Object convertToMongoType(@Nullable Object obj, MongoPersistentProperty property) {
PersistentPropertyAccessor accessor = new MapPersistentPropertyAccessor();
accessor.setProperty(property, obj);
Document newDocument = new Document();
DocumentAccessor dbObjectAccessor = new DocumentAccessor(newDocument);
if (property.isIdProperty() || !property.isWritable()) {
return obj;
}
if (property.isAssociation()) {
writeAssociation(property.getRequiredAssociation(), accessor, dbObjectAccessor);
return dbObjectAccessor.get(property);
}
Object value = obj;
if (value == null) {
if (property.writeNullValues()) {
dbObjectAccessor.put(property, null);
}
} else if (!conversions.isSimpleType(value.getClass())) {
writePropertyInternal(value, dbObjectAccessor, property, accessor);
} else {
writeSimpleInternal(value, newDocument, property, accessor);
}
return dbObjectAccessor.get(property);
}
// TODO: hide in 4.0
public List<Object> maybeConvertList(Iterable<?> source, @Nullable TypeInformation<?> typeInformation) {

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

@ -42,12 +42,15 @@ import org.springframework.data.convert.PropertyValueConverter; @@ -42,12 +42,15 @@ import org.springframework.data.convert.PropertyValueConverter;
import org.springframework.data.convert.PropertyValueConverterFactory;
import org.springframework.data.convert.PropertyValueConverterRegistrar;
import org.springframework.data.convert.SimplePropertyValueConversions;
import org.springframework.data.convert.ValueConversionContext;
import org.springframework.data.convert.WritingConverter;
import org.springframework.data.mapping.PersistentProperty;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.mapping.MongoSimpleTypes;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Value object to capture custom conversion. {@link MongoCustomConversions} also act as factory for
@ -331,9 +334,40 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus @@ -331,9 +334,40 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus
svc.init();
}
// Move to data-commons?
PropertyValueConversions pvc = new PropertyValueConversions() {
@Override
public boolean hasValueConverter(PersistentProperty<?> property) {
return propertyValueConversions.hasValueConverter(property);
}
@Override
public <DV, SV, P extends PersistentProperty<P>, VCC extends ValueConversionContext<P>> PropertyValueConverter<DV, SV, VCC> getValueConverter(
P property) {
return new PropertyValueConverter<DV, SV, VCC>() {
@Nullable
@Override
public DV read(SV value, VCC context) {
return (DV) propertyValueConversions.getValueConverter(property).read(value, context);
}
@Nullable
@Override
public SV write(DV value, VCC context) {
if (ClassUtils.isAssignable(property.getType(), value.getClass())) {
return (SV) propertyValueConversions.getValueConverter(property).write(value, context);
}
return (SV) value;
}
};
}
};
if (!useNativeDriverJavaTimeCodecs) {
return new ConverterConfiguration(STORE_CONVERSIONS, this.customConverters, convertiblePair -> true,
this.propertyValueConversions);
return new ConverterConfiguration(STORE_CONVERSIONS, this.customConverters, convertiblePair -> true, pvc);
}
/*
@ -358,7 +392,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus @@ -358,7 +392,7 @@ public class MongoCustomConversions extends org.springframework.data.convert.Cus
}
return true;
}, this.propertyValueConversions);
}, pvc);
}
private enum DateToUtcLocalDateTimeConverter implements Converter<Date, LocalDateTime> {

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

@ -59,6 +59,11 @@ public interface MongoWriter<T> extends EntityWriter<T, Bson> { @@ -59,6 +59,11 @@ public interface MongoWriter<T> extends EntityWriter<T, Bson> {
@Nullable
Object convertToMongoType(@Nullable Object obj, @Nullable TypeInformation<?> typeInformation);
@Nullable
default Object convertToMongoType(@Nullable Object obj, MongoPersistentProperty property) {
return convertToMongoType(obj, property.getTypeInformation());
}
default Object convertToMongoType(@Nullable Object obj, MongoPersistentEntity<?> entity) {
return convertToMongoType(obj, entity.getTypeInformation());
}

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

@ -36,9 +36,11 @@ import org.springframework.data.repository.query.ParameterAccessor; @@ -36,9 +36,11 @@ import org.springframework.data.repository.query.ParameterAccessor;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import com.mongodb.DBRef;
import org.springframework.util.ObjectUtils;
/**
* Custom {@link ParameterAccessor} that uses a {@link MongoWriter} to serialize parameters into Mongo format.
@ -91,7 +93,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { @@ -91,7 +93,7 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
}
public Object getBindableValue(int index) {
return getConvertedValue(delegate.getBindableValue(index), null);
return getConvertedValue(delegate.getBindableValue(index), (TypeInformation<?>) null);
}
@Override
@ -129,6 +131,11 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor { @@ -129,6 +131,11 @@ public class ConvertingParameterAccessor implements MongoParameterAccessor {
return writer.convertToMongoType(value, typeInformation == null ? null : typeInformation.getActualType());
}
public Object getConvertedValue(Object value, MongoPersistentProperty property) {
return writer.convertToMongoType(value, property);
}
public boolean hasBindableNullValue() {
return delegate.hasBindableNullValue();
}

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

@ -68,7 +68,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> { @@ -68,7 +68,7 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
private static final Log LOG = LogFactory.getLog(MongoQueryCreator.class);
private final MongoParameterAccessor accessor;
private final ConvertingParameterAccessor accessor;
private final MappingContext<?, MongoPersistentProperty> context;
private final boolean isGeoNearQuery;
@ -345,6 +345,17 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> { @@ -345,6 +345,17 @@ class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {
"Argument for creating $regex pattern for property '%s' must not be null", part.getProperty().getSegment()));
}
try {
PersistentPropertyPath<MongoPersistentProperty> persistentPropertyPath = context.getPersistentPropertyPath(part.getProperty());
MongoPersistentProperty leafProperty = persistentPropertyPath.getLeafProperty();/// maybe a call back here
if (leafProperty != null) {
Object convertedValue = accessor.getConvertedValue(value.toString(), leafProperty);
return criteria.regex(toLikeRegex(convertedValue.toString(), part), toRegexOptions(part));
}
} catch (Exception ex) {
System.err.print(ex);
}
return criteria.regex(toLikeRegex(value.toString(), part), toRegexOptions(part));
}

6
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/ReversingValueConverter.java → spring-data-mongodb/src/test/java/org/springframework/data/mongodb/ReversingValueConverter.java

@ -13,14 +13,16 @@ @@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.convert;
package org.springframework.data.mongodb;
import org.springframework.data.mongodb.core.convert.MongoConversionContext;
import org.springframework.data.mongodb.core.convert.MongoValueConverter;
import org.springframework.lang.Nullable;
/**
* @author Christoph Strobl
*/
class ReversingValueConverter implements MongoValueConverter<String, String> {
public class ReversingValueConverter implements MongoValueConverter<String, String> {
@Nullable
@Override

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

@ -29,6 +29,7 @@ import java.util.List; @@ -29,6 +29,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.bson.BsonRegularExpression;
import org.bson.conversions.Bson;
import org.bson.types.Code;
import org.bson.types.ObjectId;
@ -42,6 +43,7 @@ import org.springframework.data.convert.WritingConverter; @@ -42,6 +43,7 @@ import org.springframework.data.convert.WritingConverter;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.ReversingValueConverter;
import org.springframework.data.mongodb.core.DocumentTestUtils;
import org.springframework.data.mongodb.core.Person;
import org.springframework.data.mongodb.core.aggregation.ComparisonOperators;
@ -1455,6 +1457,15 @@ public class QueryMapperUnitTests { @@ -1455,6 +1457,15 @@ public class QueryMapperUnitTests {
assertThat(mappedObject).isEqualTo(new org.bson.Document("text", "eulav"));
}
@Test // GH-4346
void ignoresValueConverterForNonMatchingType() {
org.bson.Document source = new org.bson.Document("text", new BsonRegularExpression("value"));
org.bson.Document mappedObject = mapper.getMappedObject(source, context.getPersistentEntity(WithPropertyValueConverter.class));
assertThat(mappedObject).isEqualTo(source);
}
@Test // GH-2750
void mapsAggregationExpression() {

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

@ -47,9 +47,8 @@ import org.springframework.data.domain.Sort; @@ -47,9 +47,8 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.data.mapping.MappingException;
import org.springframework.data.mongodb.ReversingValueConverter;
import org.springframework.data.mongodb.core.DocumentTestUtils;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.UpdateMapper;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;

15
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/AbstractPersonRepositoryIntegrationTests.java

@ -1637,4 +1637,19 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie @@ -1637,4 +1637,19 @@ public abstract class AbstractPersonRepositoryIntegrationTests implements Dirtie
assertThat(repository.findById(dave.getId()).map(Person::getShippingAddresses))
.contains(Collections.singleton(address));
}
@Test // GH-4346
@DirtiesState
void findCreatingRegexWithValueConverterWorks() {
Person bart = new Person("bart", "simpson");
bart.setNickName("bartman");
operations.save(bart);
List<Person> result = repository.findByNickNameContains("artma");
assertThat(result).hasSize(1);
assertThat(result.get(0).getId().equals(bart.getId()));
}
}

13
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/Person.java

@ -21,7 +21,9 @@ import java.util.List; @@ -21,7 +21,9 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.springframework.data.convert.ValueConverter;
import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.ReversingValueConverter;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexed;
import org.springframework.data.mongodb.core.index.Indexed;
@ -78,6 +80,9 @@ public class Person extends Contact { @@ -78,6 +80,9 @@ public class Person extends Contact {
@DocumentReference User spiritAnimal;
@ValueConverter(ReversingValueConverter.class)
String nickName;
int visits;
public Person() {
@ -325,6 +330,14 @@ public class Person extends Contact { @@ -325,6 +330,14 @@ public class Person extends Contact {
this.spiritAnimal = spiritAnimal;
}
public String getNickName() {
return nickName;
}
public void setNickName(String nickName) {
this.nickName = nickName;
}
@Override
public int hashCode() {

1
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/repository/PersonRepository.java

@ -465,4 +465,5 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query @@ -465,4 +465,5 @@ public interface PersonRepository extends MongoRepository<Person, String>, Query
List<Person> findBySpiritAnimal(User user);
List<Person> findByNickNameContains(String nickName);
}

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

@ -29,6 +29,7 @@ import org.bson.Document; @@ -29,6 +29,7 @@ import org.bson.Document;
import org.bson.types.ObjectId;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.data.convert.ValueConverter;
import org.springframework.data.domain.Range;
import org.springframework.data.domain.Range.Bound;
import org.springframework.data.geo.Distance;
@ -40,7 +41,9 @@ import org.springframework.data.mapping.context.MappingContext; @@ -40,7 +41,9 @@ import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mongodb.core.Person;
import org.springframework.data.mongodb.core.Venue;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.convert.MongoConversionContext;
import org.springframework.data.mongodb.core.convert.MongoConverter;
import org.springframework.data.mongodb.core.convert.MongoValueConverter;
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
import org.springframework.data.mongodb.core.geo.GeoJsonLineString;
import org.springframework.data.mongodb.core.geo.GeoJsonPoint;
@ -57,6 +60,7 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory; @@ -57,6 +60,7 @@ import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.DefaultRepositoryMetadata;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.lang.Nullable;
/**
* Unit test for {@link MongoQueryCreator}.
@ -658,6 +662,16 @@ class MongoQueryCreatorUnitTests { @@ -658,6 +662,16 @@ class MongoQueryCreatorUnitTests {
assertThat(creator.createQuery()).isEqualTo(query(where("location").nearSphere(point).maxDistance(1000.0D)));
}
@Test // GH-4346
void likeQueriesShouldApplyPropertyValueConverterWhenCreatingRegex() {
PartTree tree = new PartTree("findByTextStartingWith", WithValueConverter.class);
MongoQueryCreator creator = new MongoQueryCreator(tree, getAccessor(converter, "spring"), context);
Query query = creator.createQuery();
assertThat(query).isEqualTo(query(where("text").regex("^gnirps")));
}
interface PersonRepository extends Repository<Person, Long> {
List<Person> findByLocationNearAndFirstname(Point location, Distance maxDistance, String firstname);
@ -693,4 +707,34 @@ class MongoQueryCreatorUnitTests { @@ -693,4 +707,34 @@ class MongoQueryCreatorUnitTests {
@GeoSpatialIndexed(type = GeoSpatialIndexType.GEO_2DSPHERE) Point geo;
}
static class WithValueConverter {
@ValueConverter(ReversingValueConverter.class)
String text;
}
static class ReversingValueConverter implements MongoValueConverter<String, String> {
@Nullable
@Override
public String read(@Nullable String value, MongoConversionContext context) {
return reverse(value);
}
@Nullable
@Override
public String write(@Nullable String value, MongoConversionContext context) {
return reverse(value);
}
private String reverse(String source) {
if (source == null) {
return null;
}
return new StringBuilder(source).reverse().toString();
}
}
}

4
spring-data-mongodb/src/test/resources/logback.xml

@ -9,6 +9,10 @@ @@ -9,6 +9,10 @@
<appender name="no-op" class="ch.qos.logback.core.helpers.NOPAppender" />
<!--
<logger name="org.mongodb.driver.protocol" level="DEBUG" />
-->
<!--
<logger name="org.springframework" level="debug" />
-->

Loading…
Cancel
Save