From 5cca849ecbe1885e84f666e31486d1c3d05289bb Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Mon, 6 Nov 2017 20:57:27 +0100 Subject: [PATCH] DATAMONGO-1761 - Polishing. Refactor MongoConverter.mapValueToTargetType to imperative method instead of returning a function for easier consumption. Adapt return types in Javadoc to the actual return type. Remove undocumented type parameters. Add overload using reified entity and return type generics. Slight documentation tweaks. Original pull request: #494. Related pull request: #514. --- .../data/mongodb/CodecRegistryProvider.java | 8 +- .../data/mongodb/MongoDbFactory.java | 16 ++- .../mongodb/ReactiveMongoDatabaseFactory.java | 7 +- .../mongodb/core/ExecutableFindOperation.java | 4 +- .../core/ExecutableFindOperationSupport.java | 14 ++- .../data/mongodb/core/MongoOperations.java | 1 - .../data/mongodb/core/MongoTemplate.java | 32 +++-- .../mongodb/core/ReactiveFindOperation.java | 2 +- .../core/ReactiveFindOperationSupport.java | 8 +- .../mongodb/core/ReactiveMongoOperations.java | 11 +- .../mongodb/core/ReactiveMongoTemplate.java | 34 +++--- .../mongodb/core/convert/MongoConverter.java | 65 +++++------ .../mongodb/core/MongoOperationsExtensions.kt | 12 +- .../core/ReactiveMongoOperationsExtensions.kt | 21 ++-- .../ExecutableFindOperationSupportTests.java | 10 +- .../ReactiveFindOperationSupportTests.java | 110 ++++++++++++------ .../core/MongoOperationsExtensionsTests.kt | 23 +++- .../ReactiveMongoOperationsExtensionsTests.kt | 25 +++- src/main/asciidoc/new-features.adoc | 1 + src/main/asciidoc/reference/mongodb.adoc | 13 ++- 20 files changed, 252 insertions(+), 165 deletions(-) diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/CodecRegistryProvider.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/CodecRegistryProvider.java index f470dd8a1..6c1992fc5 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/CodecRegistryProvider.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/CodecRegistryProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * Copyright 2017-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,9 +23,13 @@ import org.bson.codecs.configuration.CodecRegistry; import org.springframework.util.Assert; /** + * Provider interface to obtain {@link CodecRegistry} from the underlying MongoDB Java driver. + * * @author Christoph Strobl + * @author Mark Paluch * @since 2.1 */ +@FunctionalInterface public interface CodecRegistryProvider { /** @@ -50,7 +54,7 @@ public interface CodecRegistryProvider { /** * Get the {@link Codec} registered for the given {@literal type} or an {@link Optional#empty() empty Optional} * instead. - * + * * @param type must not be {@literal null}. * @param * @return never {@literal null}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDbFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDbFactory.java index 3604fe6ac..b46e624ce 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDbFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/MongoDbFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,15 +25,16 @@ import com.mongodb.client.MongoDatabase; /** * Interface for factories creating {@link DB} instances. - * + * * @author Mark Pollack * @author Thomas Darimont + * @author Christoph Strobl */ public interface MongoDbFactory extends CodecRegistryProvider { /** * Creates a default {@link DB} instance. - * + * * @return * @throws DataAccessException */ @@ -41,7 +42,7 @@ public interface MongoDbFactory extends CodecRegistryProvider { /** * Creates a {@link DB} instance to access the database with the given name. - * + * * @param dbName must not be {@literal null} or empty. * @return * @throws DataAccessException @@ -50,13 +51,18 @@ public interface MongoDbFactory extends CodecRegistryProvider { /** * Exposes a shared {@link MongoExceptionTranslator}. - * + * * @return will never be {@literal null}. */ PersistenceExceptionTranslator getExceptionTranslator(); DB getLegacyDb(); + /** + * Get the underlying {@link CodecRegistry} used by the MongoDB Java driver. + * + * @return never {@literal null}. + */ @Override default CodecRegistry getCodecRegistry() { return getDb().getCodecRegistry(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseFactory.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseFactory.java index 46a945056..4ce6a714b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseFactory.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/ReactiveMongoDatabaseFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,6 +56,11 @@ public interface ReactiveMongoDatabaseFactory extends CodecRegistryProvider { */ PersistenceExceptionTranslator getExceptionTranslator(); + /** + * Get the underlying {@link CodecRegistry} used by the reactive MongoDB Java driver. + * + * @return never {@literal null}. + */ @Override default CodecRegistry getCodecRegistry() { return getMongoDatabase().getCodecRegistry(); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java index 92bdb5e6e..d7becb8cb 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * Copyright 2017-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -258,7 +258,7 @@ public interface ExecutableFindOperation { * to obtain the domain type.
* Using {@link Object} also works for non strictly typed fields. Eg. a mixture different types like fields using * {@link String} in one {@link org.bson.Document} while {@link Long} in another. - *
Any Simple type like {@link String}, {@link Long}, ...
+ *
Any Simple type like {@link String} or {@link Long}.
*
The result is mapped directly by the MongoDB Java driver and the {@link org.bson.codecs.CodeCodec Codecs} in * place. This works only for results where all documents considered for the operation use the very same type for * the field.
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java index 8f15265d3..1987a9cce 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * Copyright 2017-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -209,12 +209,13 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { * (non-Javadoc) * @see org.springframework.data.mongodb.core.ExecutableFindOperation.FindDistinct#distinct(java.lang.String) */ + @SuppressWarnings("unchecked") @Override public TerminatingDistinct distinct(String field) { Assert.notNull(field, "Field must not be null!"); - return new DistinctOperationSupport<>(this, field); + return new DistinctOperationSupport(this, field); } private List doFind(@Nullable CursorPreparer preparer) { @@ -287,9 +288,9 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { static class DistinctOperationSupport implements TerminatingDistinct { private final String field; - private final ExecutableFindSupport delegate; + private final ExecutableFindSupport delegate; - public DistinctOperationSupport(ExecutableFindSupport delegate, String field) { + public DistinctOperationSupport(ExecutableFindSupport delegate, String field) { this.delegate = delegate; this.field = field; @@ -300,11 +301,12 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { * @see org.springframework.data.mongodb.core.ExecutableFindOperation.DistinctWithProjection#as(java.lang.Class) */ @Override + @SuppressWarnings("unchecked") public TerminatingDistinct as(Class resultType) { Assert.notNull(resultType, "ResultType must not be null!"); - return new DistinctOperationSupport((ExecutableFindSupport) delegate.as(resultType), field); + return new DistinctOperationSupport<>((ExecutableFindSupport) delegate.as(resultType), field); } /* @@ -316,7 +318,7 @@ class ExecutableFindOperationSupport implements ExecutableFindOperation { Assert.notNull(query, "Query must not be null!"); - return new DistinctOperationSupport((ExecutableFindSupport) delegate.matching(query), field); + return new DistinctOperationSupport<>((ExecutableFindSupport) delegate.matching(query), field); } /* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java index d3f457dff..3d5268f8d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoOperations.java @@ -761,7 +761,6 @@ public interface MongoOperations extends FluentMongoOperations { * @param field the name of the field to inspect for distinct values. Must not be {@literal null}. * @param collection the explicit name of the actual {@link MongoCollection}. Must not be {@literal null}. * @param resultClass the result type. Must not be {@literal null}. - * @param * @return never {@literal null}. * @since 2.1 */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java index 91bbf1533..338389d43 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2017 the original author or authors. + * Copyright 2010-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -121,7 +121,6 @@ import org.springframework.util.ObjectUtils; import org.springframework.util.ResourceUtils; import org.springframework.util.StringUtils; -import com.mongodb.Function; import com.mongodb.MongoClient; import com.mongodb.MongoException; import com.mongodb.ReadPreference; @@ -803,7 +802,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, } /* - * (non-Javadoc) + * (non-Javadoc) * @see org.springframework.data.mongodb.core.MongoOperations#findDistinct(org.springframework.data.mongodb.core.query.Query, java.lang.String, java.lang.Class, java.lang.Class) */ @Override @@ -816,6 +815,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @see org.springframework.data.mongodb.core.MongoOperations#findDistinct(org.springframework.data.mongodb.core.query.Query, java.lang.String, java.lang.String, java.lang.Class, java.lang.Class) */ @Override + @SuppressWarnings("unchecked") public List findDistinct(Query query, String field, String collectionName, Class entityClass, Class resultClass) { @@ -830,20 +830,24 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), entity); String mappedFieldName = queryMapper.getMappedFields(new Document(field, 1), entity).keySet().iterator().next(); - Class mongoDriverCompatibleType = getMongoDbFactory().getCodecFor(resultClass).map(Codec::getEncoderClass) + Class mongoDriverCompatibleType = getMongoDbFactory().getCodecFor(resultClass).map(Codec::getEncoderClass) .orElse((Class) BsonValue.class); MongoIterable result = execute((db) -> { - DistinctIterable iterable = db.getCollection(collectionName).distinct(mappedFieldName, mappedQuery, + DistinctIterable iterable = db.getCollection(collectionName).distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType); - return query.getCollation().isPresent() - ? iterable.collation(query.getCollation().map(Collation::toMongoCollation).get()) : iterable; + return query.getCollation().map(Collation::toMongoCollation).map(iterable::collation).orElse(iterable); }); if (resultClass == Object.class || mongoDriverCompatibleType != resultClass) { - result = result.map(mapDistinctResult(getMostSpecificConversionTargetType(resultClass, entityClass, field))); + + MongoConverter converter = getConverter(); + DefaultDbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory); + + result = result.map((source) -> converter.mapValueToTargetType(source, + getMostSpecificConversionTargetType(resultClass, entityClass, field), dbRefResolver)); } try { @@ -860,7 +864,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, * @return the most specific conversion target type depending on user preference and domain type property. * @since 2.1 */ - private Class getMostSpecificConversionTargetType(Class userType, Class domainType, String field) { + private static Class getMostSpecificConversionTargetType(Class userType, Class domainType, String field) { Class conversionTargetType = userType; try { @@ -879,16 +883,6 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware, return conversionTargetType; } - /** - * @param targetType the desired conversion target type. - * @return new {@link Function} converting {@link BsonValue} into desired target type. - * @since 2.1 - */ - private Function mapDistinctResult(Class targetType) { - return (source) -> getConverter().mapValueToTargetType(targetType, new DefaultDbRefResolver(mongoDbFactory)) - .apply(source); - } - @Override public GeoResults geoNear(NearQuery near, Class entityClass) { return geoNear(near, entityClass, determineCollectionName(entityClass)); diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperation.java index 4e28f850f..8dbc357d3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperation.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * Copyright 2017-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java index d72f30505..90aa9f2de 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupport.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * Copyright 2017-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -217,6 +217,7 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation { preparer != null ? preparer : getCursorPreparer(query)); } + @SuppressWarnings("unchecked") private Flux doFindDistinct(String field) { return template.findDistinct(query, field, getCollectionName(), domainType, @@ -259,7 +260,7 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation { Assert.notNull(resultType, "ResultType must not be null!"); - return new DistinctOperationSupport((ReactiveFindSupport) delegate.as(resultType), field); + return new DistinctOperationSupport<>((ReactiveFindSupport) delegate.as(resultType), field); } /* @@ -267,11 +268,12 @@ class ReactiveFindOperationSupport implements ReactiveFindOperation { * @see org.springframework.data.mongodb.core.ReactiveFindOperation.DistinctWithQuery#matching(org.springframework.data.mongodb.core.query.Query) */ @Override + @SuppressWarnings("unchecked") public TerminatingDistinct matching(Query query) { Assert.notNull(query, "Query must not be null!"); - return new DistinctOperationSupport((ReactiveFindSupport) delegate.matching(query), field); + return new DistinctOperationSupport<>((ReactiveFindSupport) delegate.matching(query), field); } /* diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java index 20161a02c..d9ca552a3 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoOperations.java @@ -379,7 +379,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations { /** * Finds the distinct values for a specified {@literal field} across a single {@link MongoCollection} or view and - * returns the results in a {@link List}. + * returns the results in a {@link Flux}. * * @param field the name of the field to inspect for distinct values. Must not be {@literal null}. * @param entityClass the domain type used for determining the actual {@link MongoCollection}. Must not be @@ -394,7 +394,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations { /** * Finds the distinct values for a specified {@literal field} across a single {@link MongoCollection} or view and - * returns the results in a {@link List}. + * returns the results in a {@link Flux}. * * @param query filter {@link Query} to restrict search. Must not be {@literal null}. * @param field the name of the field to inspect for distinct values. Must not be {@literal null}. @@ -408,7 +408,7 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations { /** * Finds the distinct values for a specified {@literal field} across a single {@link MongoCollection} or view and - * returns the results in a {@link List}. + * returns the results in a {@link Flux}. * * @param query filter {@link Query} to restrict search. Must not be {@literal null}. * @param field the name of the field to inspect for distinct values. Must not be {@literal null}. @@ -423,14 +423,13 @@ public interface ReactiveMongoOperations extends ReactiveFluentMongoOperations { /** * Finds the distinct values for a specified {@literal field} across a single {@link MongoCollection} or view and - * returns the results in a {@link List}. + * returns the results in a {@link Flux}. * * @param query filter {@link Query} to restrict search. Must not be {@literal null}. * @param field the name of the field to inspect for distinct values. Must not be {@literal null}. * @param collection the explicit name of the actual {@link MongoCollection}. Must not be {@literal null}. * @param resultClass the result type. Must not be {@literal null}. - * @param - * @return + * @return never {@literal null}. * @since 2.1 */ default Flux findDistinct(Query query, String field, String collection, Class resultClass) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java index ba8af473c..46e6ea434 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2017 the original author or authors. + * Copyright 2016-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,17 +24,8 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.util.function.Tuple2; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -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.function.Function; import java.util.stream.Collectors; @@ -692,6 +683,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * (non-Javadoc) * @see org.springframework.data.mongodb.core.ReactiveMongoOperations#findDistinct(org.springframework.data.mongodb.core.query.Query, java.lang.String, java.lang.String, java.lang.Class, java.lang.Class) */ + @SuppressWarnings("unchecked") public Flux findDistinct(Query query, String field, String collectionName, Class entityClass, Class resultClass) { @@ -706,23 +698,25 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati Document mappedQuery = queryMapper.getMappedObject(query.getQueryObject(), entity); String mappedFieldName = queryMapper.getMappedFields(new Document(field, 1), entity).keySet().iterator().next(); - Class mongoDriverCompatibleType = mongoDatabaseFactory.getCodecFor(resultClass).map(Codec::getEncoderClass) + Class mongoDriverCompatibleType = mongoDatabaseFactory.getCodecFor(resultClass).map(Codec::getEncoderClass) .orElse((Class) BsonValue.class); - Flux result = execute(collectionName, collection -> { + Flux result = execute(collectionName, collection -> { - DistinctPublisher publisher = collection.distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType); + DistinctPublisher publisher = collection.distinct(mappedFieldName, mappedQuery, mongoDriverCompatibleType); - return query.getCollation().isPresent() - ? publisher.collation(query.getCollation().map(Collation::toMongoCollation).get()) : publisher; + return query.getCollation().map(Collation::toMongoCollation).map(publisher::collation).orElse(publisher); }); if (resultClass == Object.class || mongoDriverCompatibleType != resultClass) { - result = result.map( - getConverter().mapValueToTargetType(getMostSpecificConversionTargetType(resultClass, entityClass, field), NO_OP_REF_RESOLVER)); + + Class targetType = getMostSpecificConversionTargetType(resultClass, entityClass, field); + MongoConverter converter = getConverter(); + + result = result.map(it -> converter.mapValueToTargetType(it, targetType, NO_OP_REF_RESOLVER)); } - return result; + return (Flux) result; } /** @@ -732,7 +726,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati * @return the most specific conversion target type depending on user preference and domain type property. * @since 2.1 */ - private Class getMostSpecificConversionTargetType(Class userType, Class domainType, String field) { + private static Class getMostSpecificConversionTargetType(Class userType, Class domainType, String field) { Class conversionTargetType = userType; try { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java index 442f4cc6a..41d5aa62e 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MongoConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2016 the original author or authors. + * Copyright 2010-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,6 @@ */ package org.springframework.data.mongodb.core.convert; -import java.util.function.Function; - import org.bson.BsonValue; import org.bson.Document; import org.bson.conversions.Bson; @@ -26,17 +24,19 @@ import org.springframework.data.convert.TypeMapper; import org.springframework.data.mongodb.core.mapping.MongoPersistentEntity; import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty; import org.springframework.data.mongodb.util.BsonUtils; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import com.mongodb.DBRef; /** - * Central Mongo specific converter interface which combines {@link MongoWriter} and {@link MongoReader}. - * + * Central Mongo specific converter interface which combines {@link MongoWriter} and {@link EntityReader}. + * * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl + * @author Mark Paluch */ public interface MongoConverter extends EntityConverter, MongoPersistentProperty, Object, Bson>, MongoWriter, @@ -45,7 +45,7 @@ public interface MongoConverter /** * Returns thw {@link TypeMapper} being used to write type information into {@link Document}s created with that * converter. - * + * * @return will never be {@literal null}. */ MongoTypeMapper getTypeMapper(); @@ -58,50 +58,49 @@ public interface MongoConverter * @param dbRefResolver must not be {@literal null}. * @param * @param - * @return new typed {@link com.mongodb.Function}. + * @return new typed {@link java.util.function.Function}. * @throws IllegalArgumentException if {@literal targetType} is {@literal null}. * @since 2.1 */ - default Function mapValueToTargetType(Class targetType, DbRefResolver dbRefResolver) { + @SuppressWarnings("unchecked") + @Nullable + default T mapValueToTargetType(S source, Class targetType, DbRefResolver dbRefResolver) { Assert.notNull(targetType, "TargetType must not be null!"); Assert.notNull(dbRefResolver, "DbRefResolver must not be null!"); - return (source) -> { - if (targetType != Object.class && ClassUtils.isAssignable(targetType, source.getClass())) { - return (T) source; - } + if (targetType != Object.class && ClassUtils.isAssignable(targetType, source.getClass())) { + return (T) source; + } - if (source instanceof BsonValue) { + if (source instanceof BsonValue) { - Object value = BsonUtils.toJavaType((BsonValue) source); + Object value = BsonUtils.toJavaType((BsonValue) source); - if (value instanceof Document) { + if (value instanceof Document) { - Document sourceDocument = (Document) value; + Document sourceDocument = (Document) value; - if (sourceDocument.containsKey("$ref") && sourceDocument.containsKey("$id")) { + if (sourceDocument.containsKey("$ref") && sourceDocument.containsKey("$id")) { - sourceDocument = dbRefResolver - .fetch(new DBRef(sourceDocument.getString("$ref"), sourceDocument.get("$id"))); - if (sourceDocument == null) { - return null; - } - } - - return read(targetType, sourceDocument); - } else { - if (!ClassUtils.isAssignable(targetType, value.getClass())) { - if (getConversionService().canConvert(value.getClass(), targetType)) { - return getConversionService().convert(value, targetType); - } + sourceDocument = dbRefResolver.fetch(new DBRef(sourceDocument.getString("$ref"), sourceDocument.get("$id"))); + if (sourceDocument == null) { + return null; } } - return (T) value; + return read(targetType, sourceDocument); + } else { + if (!ClassUtils.isAssignable(targetType, value.getClass())) { + if (getConversionService().canConvert(value.getClass(), targetType)) { + return getConversionService().convert(value, targetType); + } + } } - return (T) getConversionService().convert(source, targetType); - }; + + return (T) value; + } + return getConversionService().convert(source, targetType); } } diff --git a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensions.kt b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensions.kt index e0bb3ec85..03b1ca734 100644 --- a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensions.kt +++ b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensions.kt @@ -338,7 +338,7 @@ inline fun MongoOperations.findDistinct(field: String, entityC * @since 2.1 */ inline fun MongoOperations.findDistinct(query: Query, field: String, entityClass: KClass<*>): List = - findDistinct(query, field, entityClass.java, T::class.java); + findDistinct(query, field, entityClass.java, T::class.java) /** * Extension for [MongoOperations.findDistinct] leveraging reified type parameters. @@ -346,17 +346,19 @@ inline fun MongoOperations.findDistinct(query: Query, field: S * @author Christoph Strobl * @since 2.1 */ -inline fun MongoOperations.findDistinct(query: Query, field: String, collectionName: String?, entityClass: KClass<*>): List = - findDistinct(query, field, collectionName, entityClass.java, T::class.java); +inline fun MongoOperations.findDistinct(query: Query, field: String, collectionName: String, entityClass: KClass<*>): List = + findDistinct(query, field, collectionName, entityClass.java, T::class.java) /** * Extension for [MongoOperations.findDistinct] leveraging reified type parameters. * * @author Christoph Strobl + * @author Mark Paluch * @since 2.1 */ -inline fun MongoOperations.findDistinct(query: Query, field: String, collectionName: String?): List = - findDistinct(query, field, collectionName, T::class.java); +inline fun MongoOperations.findDistinct(query: Query, field: String, collectionName: String? = null): List = + if (collectionName != null) findDistinct(query, field, collectionName, E::class.java, T::class.java) + else findDistinct(query, field, E::class.java, T::class.java) /** * Extension for [MongoOperations.findAndModify] leveraging reified type parameters. diff --git a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensions.kt b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensions.kt index e06d0f40e..832f87349 100644 --- a/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensions.kt +++ b/spring-data-mongodb/src/main/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensions.kt @@ -109,7 +109,6 @@ fun ReactiveMongoOperations.dropCollection(entityClass: KClass): Mo inline fun ReactiveMongoOperations.dropCollection(): Mono = dropCollection(T::class.java) - /** * Extension for [ReactiveMongoOperations.findAll] leveraging reified type parameters. * @@ -166,7 +165,7 @@ inline fun ReactiveMongoOperations.findById(id: Any, collectio if (collectionName != null) findById(id, T::class.java, collectionName) else findById(id, T::class.java) /** - * Extension for [MongoOperations.findDistinct] leveraging reified type parameters. + * Extension for [ReactiveMongoOperations.findDistinct] leveraging reified type parameters. * * @author Christoph Strobl * @since 2.1 @@ -175,31 +174,33 @@ inline fun ReactiveMongoOperations.findDistinct(field: String, findDistinct(field, entityClass.java, T::class.java); /** - * Extension for [MongoOperations.findDistinct] leveraging reified type parameters. + * Extension for [ReactiveMongoOperations.findDistinct] leveraging reified type parameters. * * @author Christoph Strobl * @since 2.1 */ inline fun ReactiveMongoOperations.findDistinct(query: Query, field: String, entityClass: KClass<*>): Flux = - findDistinct(query, field, entityClass.java, T::class.java); + findDistinct(query, field, entityClass.java, T::class.java) /** - * Extension for [MongoOperations.findDistinct] leveraging reified type parameters. + * Extension for [ReactiveMongoOperations.findDistinct] leveraging reified type parameters. * * @author Christoph Strobl * @since 2.1 */ -inline fun ReactiveMongoOperations.findDistinct(query: Query, field: String, collectionName: String?, entityClass: KClass<*>): Flux = - findDistinct(query, field, collectionName, entityClass.java, T::class.java); +inline fun ReactiveMongoOperations.findDistinct(query: Query, field: String, collectionName: String, entityClass: KClass<*>): Flux = + findDistinct(query, field, collectionName, entityClass.java, T::class.java) /** - * Extension for [MongoOperations.findDistinct] leveraging reified type parameters. + * Extension for [ReactiveMongoOperations.findDistinct] leveraging reified type parameters. * * @author Christoph Strobl + * @author Mark Paluch * @since 2.1 */ -inline fun ReactiveMongoOperations.findDistinct(query: Query, field: String, collectionName: String?): Flux = - findDistinct(query, field, collectionName, T::class.java); +inline fun ReactiveMongoOperations.findDistinct(query: Query, field: String, collectionName: String? = null): Flux = + if (collectionName != null) findDistinct(query, field, collectionName, E::class.java, T::class.java) + else findDistinct(query, field, E::class.java, T::class.java) /** * Extension for [ReactiveMongoOperations.geoNear] leveraging reified type parameters. diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java index fd7d3407c..93b5c7a0c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * Copyright 2017-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -372,7 +372,7 @@ public class ExecutableFindOperationSupportTests { } @Test // DATAMONGO-1761 - public void distinctReturnsSimpleFieldValuesCorrectlyForCollectionHavingNoReturnTypeSpecified() { + public void distinctReturnsSimpleFieldValuesCorrectly() { Person anakin = new Person(); anakin.firstname = "anakin"; @@ -395,7 +395,7 @@ public class ExecutableFindOperationSupportTests { } @Test // DATAMONGO-1761 - public void distinctReturnsComplexValuesCorrectlyForCollectionHavingNoReturnTypeSpecified() { + public void distinctReturnsComplexValuesCorrectly() { Sith sith = new Sith(); sith.rank = "lord"; @@ -410,7 +410,7 @@ public class ExecutableFindOperationSupportTests { } @Test // DATAMONGO-1761 - public void distinctReturnsComplexValuesCorrectlyForCollectionHavingReturnTypeSpecified() { + public void distinctReturnsComplexValuesCorrectlyHavingReturnTypeSpecified() { Sith sith = new Sith(); sith.rank = "lord"; @@ -426,7 +426,7 @@ public class ExecutableFindOperationSupportTests { } @Test // DATAMONGO-1761 - public void distinctReturnsComplexValuesCorrectlyForCollectionHavingReturnTypeDocumentSpecified() { + public void distinctReturnsComplexValuesCorrectlyHavingReturnTypeDocumentSpecified() { Sith sith = new Sith(); sith.rank = "lord"; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupportTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupportTests.java index fdea09cda..a3218392b 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupportTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ReactiveFindOperationSupportTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * Copyright 2017-2018 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -114,15 +114,17 @@ public class ReactiveFindOperationSupportTests { @Test // DATAMONGO-1719 public void findAllWithProjection() { - StepVerifier.create(template.query(Person.class).as(Jedi.class).all().map(it -> it.getClass().getName())) - .expectNext(Jedi.class.getName(), Jedi.class.getName()).verifyComplete(); + StepVerifier.create(template.query(Person.class).as(Jedi.class).all().map(it -> it.getClass().getName())) // + .expectNext(Jedi.class.getName(), Jedi.class.getName()) // + .verifyComplete(); } @Test // DATAMONGO-1719 public void findAllBy() { - StepVerifier.create(template.query(Person.class).matching(query(where("firstname").is("luke"))).all()) - .expectNext(luke).verifyComplete(); + StepVerifier.create(template.query(Person.class).matching(query(where("firstname").is("luke"))).all()) // + .expectNext(luke) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -130,7 +132,8 @@ public class ReactiveFindOperationSupportTests { StepVerifier .create(template.query(Jedi.class).inCollection(STAR_WARS).matching(query(where("name").is("luke"))).all()) - .consumeNextWith(it -> assertThat(it).isInstanceOf(Jedi.class)).verifyComplete(); + .consumeNextWith(it -> assertThat(it).isInstanceOf(Jedi.class)) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -139,7 +142,8 @@ public class ReactiveFindOperationSupportTests { StepVerifier .create( template.query(Human.class).inCollection(STAR_WARS).matching(query(where("firstname").is("luke"))).all()) - .expectNextCount(1).verifyComplete(); + .expectNextCount(1) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -147,7 +151,8 @@ public class ReactiveFindOperationSupportTests { StepVerifier .create(template.query(Person.class).as(Jedi.class).matching(query(where("firstname").is("luke"))).all()) - .consumeNextWith(it -> assertThat(it).isInstanceOf(Jedi.class)).verifyComplete(); + .consumeNextWith(it -> assertThat(it).isInstanceOf(Jedi.class)) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -159,7 +164,8 @@ public class ReactiveFindOperationSupportTests { assertThat(it).isInstanceOf(PersonProjection.class); assertThat(it.getFirstname()).isEqualTo("luke"); - }).verifyComplete(); + }) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -170,14 +176,16 @@ public class ReactiveFindOperationSupportTests { assertThat(it).isInstanceOf(PersonSpELProjection.class); assertThat(it.getName()).isEqualTo("luke"); - }).verifyComplete(); + }) // + .verifyComplete(); } @Test // DATAMONGO-1719 public void findBy() { StepVerifier.create(template.query(Person.class).matching(query(where("firstname").is("luke"))).one()) - .expectNext(luke).verifyComplete(); + .expectNext(luke) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -191,7 +199,8 @@ public class ReactiveFindOperationSupportTests { public void findByTooManyResults() { StepVerifier.create(template.query(Person.class).matching(query(where("firstname").in("han", "luke"))).one()) - .expectError(IncorrectResultSizeDataAccessException.class).verify(); + .expectError(IncorrectResultSizeDataAccessException.class) // + .verify(); } @Test // DATAMONGO-1719 @@ -209,7 +218,9 @@ public class ReactiveFindOperationSupportTests { StepVerifier.create(template.query(Planet.class).near(NearQuery.near(-73.9667, 40.78).spherical(true)).all()) .consumeNextWith(actual -> { assertThat(actual.getDistance()).isNotNull(); - }).expectNextCount(1).verifyComplete(); + }) // + .expectNextCount(1) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -229,7 +240,9 @@ public class ReactiveFindOperationSupportTests { assertThat(actual.getDistance()).isNotNull(); assertThat(actual.getContent()).isInstanceOf(Human.class); assertThat(actual.getContent().getId()).isEqualTo("alderan"); - }).expectNextCount(1).verifyComplete(); + }) // + .expectNextCount(1) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -250,7 +263,9 @@ public class ReactiveFindOperationSupportTests { assertThat(it.getDistance()).isNotNull(); assertThat(it.getContent()).isInstanceOf(PlanetProjection.class); assertThat(it.getContent().getName()).isEqualTo("alderan"); - }).expectNextCount(1).verifyComplete(); + }) // + .expectNextCount(1) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -271,7 +286,9 @@ public class ReactiveFindOperationSupportTests { assertThat(it.getDistance()).isNotNull(); assertThat(it.getContent()).isInstanceOf(PlanetSpELProjection.class); assertThat(it.getContent().getId()).isEqualTo("alderan"); - }).expectNextCount(1).verifyComplete(); + }) // + .expectNextCount(1) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -289,7 +306,8 @@ public class ReactiveFindOperationSupportTests { StepVerifier .create(template.query(Person.class).matching(query(where("firstname").is(luke.getFirstname()))).count()) - .expectNext(1L).verifyComplete(); + .expectNext(1L) // + .verifyComplete(); } @Test // DATAMONGO-1719 @@ -310,14 +328,16 @@ public class ReactiveFindOperationSupportTests { StepVerifier .create(template.query(Person.class).matching(query(where("firstname").is(luke.getFirstname()))).exists()) - .expectNext(true).verifyComplete(); + .expectNext(true) // + .verifyComplete(); } @Test // DATAMONGO-1719 public void existsShouldReturnFalseWhenNoElementMatchesQuery() { StepVerifier.create(template.query(Person.class).matching(query(where("firstname").is("spock"))).exists()) - .expectNext(false).verifyComplete(); + .expectNext(false) // + .verifyComplete(); } @Test // DATAMONGO-1761 @@ -337,11 +357,12 @@ public class ReactiveFindOperationSupportTests { blocking.save(anakin); StepVerifier.create(template.query(Person.class).distinct("lastname").as(String.class).all()) - .assertNext(in("solo", "skywalker")).assertNext(in("solo", "skywalker")).verifyComplete(); + .assertNext(in("solo", "skywalker")).assertNext(in("solo", "skywalker")) // + .verifyComplete(); } @Test // DATAMONGO-1761 - public void distinctReturnsSimpleFieldValuesCorrectlyForCollectionHavingNoReturnTypeSpecified() { + public void distinctReturnsSimpleFieldValuesCorrectly() { Person anakin = new Person(); anakin.firstname = "anakin"; @@ -361,12 +382,15 @@ public class ReactiveFindOperationSupportTests { Consumer containedInAbilities = in(anakin.ability, padme.ability, jaja.ability); - StepVerifier.create(template.query(Person.class).distinct("ability").all()).assertNext(containedInAbilities) - .assertNext(containedInAbilities).assertNext(containedInAbilities).verifyComplete(); + StepVerifier.create(template.query(Person.class).distinct("ability").all()) // + .assertNext(containedInAbilities) // + .assertNext(containedInAbilities) // + .assertNext(containedInAbilities) // + .verifyComplete(); } @Test // DATAMONGO-1761 - public void distinctReturnsComplexValuesCorrectlyForCollectionHavingNoReturnTypeSpecified() { + public void distinctReturnsComplexValuesCorrectly() { Sith sith = new Sith(); sith.rank = "lord"; @@ -377,12 +401,13 @@ public class ReactiveFindOperationSupportTests { blocking.save(anakin); - StepVerifier.create(template.query(Person.class).distinct("ability").all()).expectNext(anakin.ability) + StepVerifier.create(template.query(Person.class).distinct("ability").all()) // + .expectNext(anakin.ability) // .verifyComplete(); } @Test // DATAMONGO-1761 - public void distinctReturnsComplexValuesCorrectlyForCollectionHavingReturnTypeSpecified() { + public void distinctReturnsComplexValuesCorrectlyHavingReturnTypeSpecified() { Sith sith = new Sith(); sith.rank = "lord"; @@ -393,12 +418,13 @@ public class ReactiveFindOperationSupportTests { blocking.save(anakin); - StepVerifier.create(template.query(Person.class).distinct("ability").as(Sith.class).all()).expectNext(sith) + StepVerifier.create(template.query(Person.class).distinct("ability").as(Sith.class).all()) // + .expectNext(sith) // .verifyComplete(); } @Test // DATAMONGO-1761 - public void distinctReturnsComplexValuesCorrectlyForCollectionHavingReturnTypeDocumentSpecified() { + public void distinctReturnsComplexValuesCorrectlyReturnTypeDocumentSpecified() { Sith sith = new Sith(); sith.rank = "lord"; @@ -410,14 +436,16 @@ public class ReactiveFindOperationSupportTests { blocking.save(anakin); StepVerifier.create(template.query(Person.class).distinct("ability").as(Document.class).all()) - .expectNext(new Document("rank", "lord").append("_class", Sith.class.getName())).verifyComplete(); + .expectNext(new Document("rank", "lord").append("_class", Sith.class.getName())) // + .verifyComplete(); } @Test // DATAMONGO-1761 public void distinctMapsFieldNameCorrectly() { StepVerifier.create(template.query(Jedi.class).inCollection(STAR_WARS).distinct("name").as(String.class).all()) - .assertNext(in("han", "luke")).assertNext(in("han", "luke")).verifyComplete(); + .assertNext(in("han", "luke")).assertNext(in("han", "luke")) // + .verifyComplete(); } @Test // DATAMONGO-1761 @@ -425,7 +453,9 @@ public class ReactiveFindOperationSupportTests { Consumer inValues = in(new BsonString("solo"), new BsonString("skywalker")); StepVerifier.create(template.query(Person.class).distinct("lastname").as(BsonValue.class).all()) - .assertNext(inValues).assertNext(inValues).verifyComplete(); + .assertNext(inValues) // + .assertNext(inValues) // + .verifyComplete(); } @Test // DATAMONGO-1761 @@ -433,7 +463,9 @@ public class ReactiveFindOperationSupportTests { blocking.save(new Document("darth", "vader"), STAR_WARS); - StepVerifier.create(template.query(Person.class).distinct("darth").all()).expectNext("vader").verifyComplete(); + StepVerifier.create(template.query(Person.class).distinct("darth").all()) // + .expectNext("vader") // + .verifyComplete(); } @Test // DATAMONGO-1761 @@ -445,7 +477,8 @@ public class ReactiveFindOperationSupportTests { blocking.save(luke); StepVerifier.create(template.query(Person.class).distinct("father").as(Jedi.class).all()) - .expectNext(new Jedi("anakin")).verifyComplete(); + .expectNext(new Jedi("anakin")) // + .verifyComplete(); } @Test // DATAMONGO-1761 @@ -457,7 +490,8 @@ public class ReactiveFindOperationSupportTests { blocking.save(luke); StepVerifier.create(template.query(Object.class).inCollection(STAR_WARS).distinct("father").as(Jedi.class).all()) - .expectNext(new Jedi("anakin")).verifyComplete(); + .expectNext(new Jedi("anakin")) // + .verifyComplete(); } @Test // DATAMONGO-1761 @@ -471,13 +505,17 @@ public class ReactiveFindOperationSupportTests { Person expected = new Person(); expected.firstname = luke.father.firstname; - StepVerifier.create(template.query(Person.class).distinct("father").all()).expectNext(expected).verifyComplete(); + StepVerifier.create(template.query(Person.class).distinct("father").all()) // + .expectNext(expected) // + .verifyComplete(); } @Test // DATAMONGO-1761 public void distinctThrowsExceptionWhenExplicitMappingTypeCannotBeApplied() { + StepVerifier.create(template.query(Person.class).distinct("firstname").as(Long.class).all()) - .expectError(InvalidDataAccessApiUsageException.class).verify(); + .expectError(InvalidDataAccessApiUsageException.class) // + .verify(); } interface Contact {} diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensionsTests.kt index 76008cac5..13125c6a4 100644 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensionsTests.kt +++ b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/MongoOperationsExtensionsTests.kt @@ -36,6 +36,7 @@ import org.springframework.data.mongodb.core.query.Update /** * @author Sebastien Deleuze * @author Mark Paluch + * @author Christoph Strobl */ @RunWith(MockitoJUnitRunner::class) class MongoOperationsExtensionsTests { @@ -704,12 +705,30 @@ class MongoOperationsExtensionsTests { verify(operations).findDistinct(query, "field", "collection", First::class.java, String::class.java) } + @Test // DATAMONGO-1761 + fun `findDistinctImplicit(Query, String) should call java counterpart`() { + + val query = mock() + + operations.findDistinct(query, "field") + verify(operations).findDistinct(query, "field", First::class.java, String::class.java) + } + @Test // DATAMONGO-1761 fun `findDistinct(Query, String, String) should call java counterpart`() { val query = mock() - operations.findDistinct(query, "field", "collection") - verify(operations).findDistinct(query, "field", "collection", String::class.java) + operations.findDistinct(query, "field", "collection") + verify(operations).findDistinct(query, "field", "collection", First::class.java, String::class.java) + } + + @Test // DATAMONGO-1761 + fun `findDistinct(Query, String, KClass) should call java counterpart`() { + + val query = mock() + + operations.findDistinct(query, "field", First::class) + verify(operations).findDistinct(query, "field", First::class.java, String::class.java) } } diff --git a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensionsTests.kt b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensionsTests.kt index b2f0ecaaf..2a3bd7d7f 100644 --- a/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensionsTests.kt +++ b/spring-data-mongodb/src/test/kotlin/org/springframework/data/mongodb/core/ReactiveMongoOperationsExtensionsTests.kt @@ -30,6 +30,8 @@ import reactor.core.publisher.Mono /** * @author Sebastien Deleuze + * @author Christoph Strobl + * @author Mark Paluch */ @RunWith(MockitoJUnitRunner::class) class ReactiveMongoOperationsExtensionsTests { @@ -552,12 +554,31 @@ class ReactiveMongoOperationsExtensionsTests { verify(operations).findDistinct(query, "field", "collection", First::class.java, String::class.java) } + @Test // DATAMONGO-1761 + fun `findDistinctImplicit(Query, String) should call java counterpart`() { + + val query = mock() + + operations.findDistinct(query, "field") + verify(operations).findDistinct(query, "field", First::class.java, String::class.java) + } + @Test // DATAMONGO-1761 fun `findDistinct(Query, String, String) should call java counterpart`() { val query = mock() - operations.findDistinct(query, "field", "collection") - verify(operations).findDistinct(query, "field", "collection", String::class.java) + operations.findDistinct(query, "field", "collection") + verify(operations).findDistinct(query, "field", "collection", First::class.java, String::class.java) + } + + + @Test // DATAMONGO-1761 + fun `findDistinct(Query, String, KClass) should call java counterpart`() { + + val query = mock() + + operations.findDistinct(query, "field", First::class) + verify(operations).findDistinct(query, "field", First::class.java, String::class.java) } } diff --git a/src/main/asciidoc/new-features.adoc b/src/main/asciidoc/new-features.adoc index 57e6addb3..00c8abd3c 100644 --- a/src/main/asciidoc/new-features.adoc +++ b/src/main/asciidoc/new-features.adoc @@ -4,6 +4,7 @@ [[new-features.2-1-0]] == What's new in Spring Data MongoDB 2.1 * Cursor-based aggregation execution. +* <> for imperative and reactive Template API. [[new-features.2-0-0]] == What's new in Spring Data MongoDB 2.0 diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index 70ea97a32..f3afc8336 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -1090,8 +1090,9 @@ The query methods need to specify the target type T that will be returned and th [[mongo-template.query.distinct]] === Query distinct values -MongoDB allows obtaining distinct field values for a single field. The stored values do not have to have the same data type to be considered, nor is the feature limited to simple types. -However when retrieving distinct values the actual result type does matter for the sake of conversion. +MongoDB provides an operation to obtain distinct values for a single field using a query from the resulting documents. +Resulting values are not required to have the same data type, nor is the feature limited to simple types. +For retriaval the actual result type does matter for the sake of conversion and typing. .Retrieving distinct values ==== @@ -1100,13 +1101,13 @@ However when retrieving distinct values the actual result type does matter for t template.query(Person.class) <1> .distinct("lastname") <2> .all(); <3> ---- +---- <1> Query the collection of `Person`. <2> Select _distinct_ values of the `lastname` field. The fieldname will be mapped according to the domain types property declaration, taking potential `@Field` annotations into account. <3> Retrieve all distinct values as `List` of `Object` due to no explicit result type specification. ==== -Retrieving distinct values into a `Collection` of `Object.class` is the most flexible way as it will try to determine the property value of the domain type converting results to the desired type or mapping `Document` structures. +Retrieving distinct values into a `Collection` of `Object` is the most flexible way as it will try to determine the property value of the domain type converting results to the desired type or mapping `Document` structures. Sometimes, when all values of the desired field are fixed to a certain type, it is more convenient to directly obtain a correctly typed `Collection` @@ -1118,12 +1119,12 @@ template.query(Person.class) <1> .distinct("lastname") <2> .as(String.class) <3> .all(); <4> ---- +---- <1> Query the collection of `Person`. <2> Select _distinct_ values of the `lastname` field. The fieldname will be mapped according to the domain types property declaration, taking potential `@Field` annotations into account. <3> Retrieved values will be converted into the desired target type. In this case `String`. It would also be possible to map the values to a more complex type if the stored field contains a document. <4> Retrieve all distinct values as a `List` of `String`. Throws a `DataAccessException` if the type cannot be converted into the desired target type. -=== +==== [[mongo.geospatial]] === GeoSpatial Queries