2020import static org .mockito .Mockito .*;
2121import static org .springframework .data .mongodb .core .DocumentTestUtils .*;
2222
23+ import lombok .Data ;
2324import lombok .EqualsAndHashCode ;
2425import lombok .Getter ;
2526import lombok .RequiredArgsConstructor ;
@@ -2689,8 +2690,8 @@ void projectShouldReadNestedProjection() {
26892690 .and ((target , underlyingType ) -> !converter .conversions .isSimpleType (target )),
26902691 mappingContext );
26912692
2692- EntityProjection <WithNestedProjection , Person > projection = introspector
2693- . introspect ( WithNestedProjection . class , Person .class );
2693+ EntityProjection <WithNestedProjection , Person > projection = introspector . introspect ( WithNestedProjection . class ,
2694+ Person .class );
26942695 WithNestedProjection person = converter .project (projection , source );
26952696
26962697 assertThat (person .getAddresses ()).extracting (AddressProjection ::getStreet ).hasSize (1 ).containsOnly ("hwy" );
@@ -2714,6 +2715,22 @@ void projectShouldReadProjectionWithNestedEntity() {
27142715 assertThat (person .getAddresses ()).extracting (Address ::getStreet ).hasSize (1 ).containsOnly ("hwy" );
27152716 }
27162717
2718+ @ Test // GH-3998
2719+ void shouldReadOpenProjection () {
2720+
2721+ org .bson .Document author = new org .bson .Document ("firstName" , "Walter" ).append ("lastName" , "White" );
2722+ org .bson .Document book = new org .bson .Document ("_id" , "foo" ).append ("name" , "my-book" ).append ("author" , author );
2723+
2724+ EntityProjectionIntrospector introspector = EntityProjectionIntrospector .create (converter .getProjectionFactory (),
2725+ EntityProjectionIntrospector .ProjectionPredicate .typeHierarchy ()
2726+ .and ((target , underlyingType ) -> !converter .conversions .isSimpleType (target )),
2727+ mappingContext );
2728+
2729+ BookProjection projection = converter .project (introspector .introspect (BookProjection .class , Book .class ), book );
2730+
2731+ assertThat (projection .getName ()).isEqualTo ("my-book by Walter White" );
2732+ }
2733+
27172734 static class GenericType <T > {
27182735 T content ;
27192736 }
@@ -3438,11 +3455,56 @@ static class WithFieldWrite {
34383455 @ org .springframework .data .mongodb .core .mapping .Field (
34393456 write = org .springframework .data .mongodb .core .mapping .Field .Write .ALWAYS ) Integer writeAlways ;
34403457
3441- @ org .springframework .data .mongodb .core .mapping .DBRef @ org .springframework .data .mongodb .core .mapping .Field (
3458+ @ org .springframework .data .mongodb .core .mapping .DBRef
3459+ @ org .springframework .data .mongodb .core .mapping .Field (
34423460 write = org .springframework .data .mongodb .core .mapping .Field .Write .NON_NULL ) Person writeNonNullPerson ;
34433461
3444- @ org .springframework .data .mongodb .core .mapping .DBRef @ org .springframework .data .mongodb .core .mapping .Field (
3462+ @ org .springframework .data .mongodb .core .mapping .DBRef
3463+ @ org .springframework .data .mongodb .core .mapping .Field (
34453464 write = org .springframework .data .mongodb .core .mapping .Field .Write .ALWAYS ) Person writeAlwaysPerson ;
34463465
34473466 }
3467+
3468+ interface BookProjection {
3469+
3470+ @ Value ("#{target.name + ' by ' + target.author.firstName + ' ' + target.author.lastName}" )
3471+ String getName ();
3472+ }
3473+
3474+ @ Data
3475+ static class Book {
3476+
3477+ @ Id String id ;
3478+
3479+ String name ;
3480+
3481+ Author author = new Author ();
3482+
3483+ public Book () {}
3484+
3485+ public Book (String id , String name , Author author ) {
3486+ this .id = id ;
3487+ this .name = name ;
3488+ this .author = author ;
3489+ }
3490+ }
3491+
3492+ static class Author {
3493+
3494+ @ Id String id ;
3495+
3496+ String firstName ;
3497+
3498+ String lastName ;
3499+
3500+ public Author () {}
3501+
3502+ public Author (String id , String firstName , String lastName ) {
3503+ this .id = id ;
3504+ this .firstName = firstName ;
3505+ this .lastName = lastName ;
3506+ }
3507+
3508+ }
3509+
34483510}
0 commit comments