Polishing.

Add tests for projections on dbref properties update java- and reference documentation.

Original Pull Request: #3894
This commit is contained in:
Christoph Strobl
2021-12-10 09:11:47 +01:00
parent 0070b12f95
commit fdff74f7b5
6 changed files with 117 additions and 12 deletions

View File

@@ -245,8 +245,17 @@ class EntityOperations {
return UntypedOperations.instance();
}
public <M, D> EntityProjection<M, D> introspectProjection(Class<M> resultType,
Class<D> entityType) {
/**
* Introspect the given {@link Class result type} in the context of the {@link Class entity type} whether the returned
* type is a projection and what property paths are participating in the projection.
*
* @param resultType the type to project on. Must not be {@literal null}.
* @param entityType the source domain type. Must not be {@literal null}.
* @return the introspection result.
* @since 3.4
* @see EntityProjectionIntrospector#introspect(Class, Class)
*/
public <M, D> EntityProjection<M, D> introspectProjection(Class<M> resultType, Class<D> entityType) {
return introspector.introspect(resultType, entityType);
}

View File

@@ -2777,8 +2777,7 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
@Nullable
private <T> T doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, Class<?> entityType,
Document replacement, FindAndReplaceOptions options,
EntityProjection<T, ?> projection) {
Document replacement, FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug(String.format(
@@ -3240,14 +3239,14 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
*/
private class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
private final MongoConverter reader;
private final MongoConverter mongoConverter;
private final EntityProjection<T, S> projection;
private final String collectionName;
ProjectingReadCallback(MongoConverter reader, EntityProjection<T, S> projection,
ProjectingReadCallback(MongoConverter mongoConverter, EntityProjection<T, S> projection,
String collectionName) {
this.reader = reader;
this.mongoConverter = mongoConverter;
this.projection = projection;
this.collectionName = collectionName;
}
@@ -3265,10 +3264,10 @@ public class MongoTemplate implements MongoOperations, ApplicationContextAware,
maybeEmitEvent(new AfterLoadEvent<>(document, projection.getMappedType().getType(), collectionName));
Object entity = reader.project(projection, document);
Object entity = mongoConverter.project(projection, document);
if (entity == null) {
throw new MappingException(String.format("EntityReader %s returned null", reader));
throw new MappingException(String.format("EntityReader %s returned null", mongoConverter));
}
maybeEmitEvent(new AfterConvertEvent<>(document, entity, collectionName));

View File

@@ -51,7 +51,7 @@ class PropertyOperations {
Document computeMappedFieldsForProjection(EntityProjection<?, ?> projection,
Document fields) {
if (!projection.isProjection() || !projection.isClosedProjection()) {
if (!projection.isClosedProjection()) {
return fields;
}

View File

@@ -2621,8 +2621,7 @@ public class ReactiveMongoTemplate implements ReactiveMongoOperations, Applicati
*/
private <T> Mono<T> doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
FindAndReplaceOptions options,
EntityProjection<T, ?> projection) {
FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
return Mono.defer(() -> {

View File

@@ -32,6 +32,7 @@ import org.bson.BsonString;
import org.bson.BsonValue;
import org.bson.Document;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -44,6 +45,8 @@ import org.springframework.data.geo.Point;
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind;
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
import org.springframework.data.mongodb.core.index.GeospatialIndex;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.DocumentReference;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.query.BasicQuery;
import org.springframework.data.mongodb.core.query.NearQuery;
@@ -549,6 +552,71 @@ class ExecutableFindOperationSupportTests {
).containsExactlyInAnyOrder("luke");
}
@Test // GH-2860
void projectionOnDbRef() {
WithRefs source = new WithRefs();
source.id = "id-1";
source.noRef = "value";
source.planetDbRef = alderan;
template.save(source);
WithDbRefProjection target = template.query(WithRefs.class).as(WithDbRefProjection.class)
.matching(where("id").is(source.id)).oneValue();
assertThat(target.getPlanetDbRef()).isEqualTo(alderan);
}
@Test // GH-2860
@Disabled("GH-3913")
void propertyProjectionOnDbRef() {
WithRefs source = new WithRefs();
source.id = "id-1";
source.noRef = "value";
source.planetDbRef = alderan;
template.save(source);
WithDbRefPropertyProjection target = template.query(WithRefs.class).as(WithDbRefPropertyProjection.class)
.matching(where("id").is(source.id)).oneValue();
assertThat(target.getPlanetDbRef().getName()).isEqualTo(alderan.getName());
}
@Test // GH-2860
void projectionOnDocRef() {
WithRefs source = new WithRefs();
source.id = "id-1";
source.noRef = "value";
source.planetDocRef = alderan;
template.save(source);
WithDocumentRefProjection target = template.query(WithRefs.class).as(WithDocumentRefProjection.class)
.matching(where("id").is(source.id)).oneValue();
assertThat(target.getPlanetDocRef()).isEqualTo(alderan);
}
@Test // GH-2860
void propertyProjectionOnDocRef() {
WithRefs source = new WithRefs();
source.id = "id-1";
source.noRef = "value";
source.planetDocRef = alderan;
template.save(source);
WithDocRefPropertyProjection target = template.query(WithRefs.class).as(WithDocRefPropertyProjection.class)
.matching(where("id").is(source.id)).oneValue();
assertThat(target.getPlanetDocRef().getName()).isEqualTo(alderan.getName());
}
interface Contact {}
@Data
@@ -618,6 +686,34 @@ class ExecutableFindOperationSupportTests {
String getId();
}
@Data
static class WithRefs {
@Id String id;
String noRef;
@DBRef Planet planetDbRef;
@DocumentReference Planet planetDocRef;
}
interface WithDbRefProjection {
Planet getPlanetDbRef();
}
interface WithDocumentRefProjection {
Planet getPlanetDocRef();
}
interface WithDbRefPropertyProjection {
PlanetProjection getPlanetDbRef();
}
interface WithDocRefPropertyProjection {
PlanetProjection getPlanetDocRef();
}
private void initPersons() {
han = new Person();

View File

@@ -2050,6 +2050,8 @@ NOTE: Using projections allows `MongoTemplate` to optimize result mapping by lim
by the projection target type. This applies as long as the `Query` itself does not contain any field restriction and the
target type is a closed interface or DTO projection.
WARNING: Projections must not be applied to <<mapping-usage-references,DBRefs>>.
You can switch between retrieving a single entity and retrieving multiple entities as a `List` or a `Stream` through the terminating methods: `first()`, `one()`, `all()`, or `stream()`.
When writing a geo-spatial query with `near(NearQuery)`, the number of terminating methods is altered to include only the methods that are valid for running a `geoNear` command in MongoDB (fetching entities as a `GeoResult` within `GeoResults`), as the following example shows: