Skip to content

Commit fdff74f

Browse files
Polishing.
Add tests for projections on dbref properties update java- and reference documentation. Original Pull Request: #3894
1 parent 0070b12 commit fdff74f

File tree

6 files changed

+117
-12
lines changed

6 files changed

+117
-12
lines changed

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/EntityOperations.java

+11-2
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,17 @@ public <T> TypedOperations<T> forType(@Nullable Class<T> entityClass) {
245245
return UntypedOperations.instance();
246246
}
247247

248-
public <M, D> EntityProjection<M, D> introspectProjection(Class<M> resultType,
249-
Class<D> entityType) {
248+
/**
249+
* Introspect the given {@link Class result type} in the context of the {@link Class entity type} whether the returned
250+
* type is a projection and what property paths are participating in the projection.
251+
*
252+
* @param resultType the type to project on. Must not be {@literal null}.
253+
* @param entityType the source domain type. Must not be {@literal null}.
254+
* @return the introspection result.
255+
* @since 3.4
256+
* @see EntityProjectionIntrospector#introspect(Class, Class)
257+
*/
258+
public <M, D> EntityProjection<M, D> introspectProjection(Class<M> resultType, Class<D> entityType) {
250259
return introspector.introspect(resultType, entityType);
251260
}
252261

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/MongoTemplate.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -2777,8 +2777,7 @@ protected <T> T doFindAndReplace(String collectionName, Document mappedQuery, Do
27772777
@Nullable
27782778
private <T> T doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
27792779
Document mappedSort, @Nullable com.mongodb.client.model.Collation collation, Class<?> entityType,
2780-
Document replacement, FindAndReplaceOptions options,
2781-
EntityProjection<T, ?> projection) {
2780+
Document replacement, FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
27822781

27832782
if (LOGGER.isDebugEnabled()) {
27842783
LOGGER.debug(String.format(
@@ -3240,14 +3239,14 @@ public T doWith(Document document) {
32403239
*/
32413240
private class ProjectingReadCallback<S, T> implements DocumentCallback<T> {
32423241

3243-
private final MongoConverter reader;
3242+
private final MongoConverter mongoConverter;
32443243
private final EntityProjection<T, S> projection;
32453244
private final String collectionName;
32463245

3247-
ProjectingReadCallback(MongoConverter reader, EntityProjection<T, S> projection,
3246+
ProjectingReadCallback(MongoConverter mongoConverter, EntityProjection<T, S> projection,
32483247
String collectionName) {
32493248

3250-
this.reader = reader;
3249+
this.mongoConverter = mongoConverter;
32513250
this.projection = projection;
32523251
this.collectionName = collectionName;
32533252
}
@@ -3265,10 +3264,10 @@ public T doWith(Document document) {
32653264

32663265
maybeEmitEvent(new AfterLoadEvent<>(document, projection.getMappedType().getType(), collectionName));
32673266

3268-
Object entity = reader.project(projection, document);
3267+
Object entity = mongoConverter.project(projection, document);
32693268

32703269
if (entity == null) {
3271-
throw new MappingException(String.format("EntityReader %s returned null", reader));
3270+
throw new MappingException(String.format("EntityReader %s returned null", mongoConverter));
32723271
}
32733272

32743273
maybeEmitEvent(new AfterConvertEvent<>(document, entity, collectionName));

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/PropertyOperations.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class PropertyOperations {
5151
Document computeMappedFieldsForProjection(EntityProjection<?, ?> projection,
5252
Document fields) {
5353

54-
if (!projection.isProjection() || !projection.isClosedProjection()) {
54+
if (!projection.isClosedProjection()) {
5555
return fields;
5656
}
5757

spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/ReactiveMongoTemplate.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -2621,8 +2621,7 @@ protected <T> Mono<T> doFindAndReplace(String collectionName, Document mappedQue
26212621
*/
26222622
private <T> Mono<T> doFindAndReplace(String collectionName, Document mappedQuery, Document mappedFields,
26232623
Document mappedSort, com.mongodb.client.model.Collation collation, Class<?> entityType, Document replacement,
2624-
FindAndReplaceOptions options,
2625-
EntityProjection<T, ?> projection) {
2624+
FindAndReplaceOptions options, EntityProjection<T, ?> projection) {
26262625

26272626
return Mono.defer(() -> {
26282627

spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/ExecutableFindOperationSupportTests.java

+96
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.bson.BsonValue;
3333
import org.bson.Document;
3434
import org.junit.jupiter.api.BeforeEach;
35+
import org.junit.jupiter.api.Disabled;
3536
import org.junit.jupiter.api.Test;
3637
import org.junit.jupiter.api.extension.ExtendWith;
3738

@@ -44,6 +45,8 @@
4445
import org.springframework.data.mongodb.core.ExecutableFindOperation.TerminatingFind;
4546
import org.springframework.data.mongodb.core.index.GeoSpatialIndexType;
4647
import org.springframework.data.mongodb.core.index.GeospatialIndex;
48+
import org.springframework.data.mongodb.core.mapping.DBRef;
49+
import org.springframework.data.mongodb.core.mapping.DocumentReference;
4750
import org.springframework.data.mongodb.core.mapping.Field;
4851
import org.springframework.data.mongodb.core.query.BasicQuery;
4952
import org.springframework.data.mongodb.core.query.NearQuery;
@@ -549,6 +552,71 @@ void distinctAppliesFilterQuery() {
549552
).containsExactlyInAnyOrder("luke");
550553
}
551554

555+
@Test // GH-2860
556+
void projectionOnDbRef() {
557+
558+
WithRefs source = new WithRefs();
559+
source.id = "id-1";
560+
source.noRef = "value";
561+
source.planetDbRef = alderan;
562+
563+
template.save(source);
564+
565+
WithDbRefProjection target = template.query(WithRefs.class).as(WithDbRefProjection.class)
566+
.matching(where("id").is(source.id)).oneValue();
567+
568+
assertThat(target.getPlanetDbRef()).isEqualTo(alderan);
569+
}
570+
571+
@Test // GH-2860
572+
@Disabled("GH-3913")
573+
void propertyProjectionOnDbRef() {
574+
575+
WithRefs source = new WithRefs();
576+
source.id = "id-1";
577+
source.noRef = "value";
578+
source.planetDbRef = alderan;
579+
580+
template.save(source);
581+
582+
WithDbRefPropertyProjection target = template.query(WithRefs.class).as(WithDbRefPropertyProjection.class)
583+
.matching(where("id").is(source.id)).oneValue();
584+
585+
assertThat(target.getPlanetDbRef().getName()).isEqualTo(alderan.getName());
586+
}
587+
588+
@Test // GH-2860
589+
void projectionOnDocRef() {
590+
591+
WithRefs source = new WithRefs();
592+
source.id = "id-1";
593+
source.noRef = "value";
594+
source.planetDocRef = alderan;
595+
596+
template.save(source);
597+
598+
WithDocumentRefProjection target = template.query(WithRefs.class).as(WithDocumentRefProjection.class)
599+
.matching(where("id").is(source.id)).oneValue();
600+
601+
assertThat(target.getPlanetDocRef()).isEqualTo(alderan);
602+
}
603+
604+
@Test // GH-2860
605+
void propertyProjectionOnDocRef() {
606+
607+
WithRefs source = new WithRefs();
608+
source.id = "id-1";
609+
source.noRef = "value";
610+
source.planetDocRef = alderan;
611+
612+
template.save(source);
613+
614+
WithDocRefPropertyProjection target = template.query(WithRefs.class).as(WithDocRefPropertyProjection.class)
615+
.matching(where("id").is(source.id)).oneValue();
616+
617+
assertThat(target.getPlanetDocRef().getName()).isEqualTo(alderan.getName());
618+
}
619+
552620
interface Contact {}
553621

554622
@Data
@@ -618,6 +686,34 @@ interface PlanetSpELProjection {
618686
String getId();
619687
}
620688

689+
@Data
690+
static class WithRefs {
691+
692+
@Id String id;
693+
694+
String noRef;
695+
696+
@DBRef Planet planetDbRef;
697+
698+
@DocumentReference Planet planetDocRef;
699+
}
700+
701+
interface WithDbRefProjection {
702+
Planet getPlanetDbRef();
703+
}
704+
705+
interface WithDocumentRefProjection {
706+
Planet getPlanetDocRef();
707+
}
708+
709+
interface WithDbRefPropertyProjection {
710+
PlanetProjection getPlanetDbRef();
711+
}
712+
713+
interface WithDocRefPropertyProjection {
714+
PlanetProjection getPlanetDocRef();
715+
}
716+
621717
private void initPersons() {
622718

623719
han = new Person();

src/main/asciidoc/reference/mongodb.adoc

+2
Original file line numberDiff line numberDiff line change
@@ -2050,6 +2050,8 @@ NOTE: Using projections allows `MongoTemplate` to optimize result mapping by lim
20502050
by the projection target type. This applies as long as the `Query` itself does not contain any field restriction and the
20512051
target type is a closed interface or DTO projection.
20522052

2053+
WARNING: Projections must not be applied to <<mapping-usage-references,DBRefs>>.
2054+
20532055
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()`.
20542056

20552057
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:

0 commit comments

Comments
 (0)