DATAMONGO-2200 - Derive fields for aggregation $project stage from a given type.
We now allow to derive field names for a $project stage from a given type by including all top level fields.
// $project : { title : 1, author : 1 }
Aggregation.project(Book.class)
Original pull request: #748.
This commit is contained in:
committed by
Mark Paluch
parent
ff7a189c98
commit
839aa1b1e6
@@ -244,6 +244,19 @@ public class Aggregation {
|
||||
return new ProjectionOperation(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionOperation} including all top level fields of the given given {@link Class}.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return new instance of {@link ProjectionOperation}.
|
||||
* @since 2.2
|
||||
*/
|
||||
public static ProjectionOperation project(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
return new ProjectionOperation(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a new {@link UnwindOperation} for the field with the given name.
|
||||
*
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Pro
|
||||
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Encapsulates the aggregation framework {@code $project}-operation.
|
||||
@@ -73,6 +74,16 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
this(NONE, ProjectionOperationBuilder.FieldProjection.from(fields));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link ProjectionOperation} including all top level fields of the given {@link Class type}.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @since 2.2
|
||||
*/
|
||||
public ProjectionOperation(Class<?> type) {
|
||||
this(NONE, Collections.singletonList(new TypeProjection(type)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor to allow building up {@link ProjectionOperation} instances from already existing
|
||||
* {@link Projection}s.
|
||||
@@ -1700,4 +1711,35 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
return new Document(field.getName(), expression.toDocument(context));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A {@link Projection} including all top level fields of the given target type mapped to include potentially
|
||||
* deviating field names.
|
||||
*
|
||||
* @since 2.2
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
static class TypeProjection extends Projection {
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
TypeProjection(Class<?> type) {
|
||||
|
||||
super(Fields.field(type.getSimpleName()));
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.Projection#toDocument(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
|
||||
*/
|
||||
@Override
|
||||
public Document toDocument(AggregationOperationContext context) {
|
||||
|
||||
Document projections = new Document();
|
||||
ReflectionUtils.doWithFields(type, it -> projections.append(it.getName(), 1));
|
||||
|
||||
return context.getMappedObject(projections, type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import lombok.Data;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.assertj.core.api.Assertions;
|
||||
import org.bson.Document;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Range;
|
||||
@@ -41,6 +42,12 @@ import org.springframework.data.mongodb.core.aggregation.DateOperators.Timezone;
|
||||
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.StringOperators.Concat;
|
||||
import org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable;
|
||||
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.MongoConverter;
|
||||
import org.springframework.data.mongodb.core.convert.NoOpDbRefResolver;
|
||||
import org.springframework.data.mongodb.core.convert.QueryMapper;
|
||||
import org.springframework.data.mongodb.core.mapping.Field;
|
||||
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link ProjectionOperation}.
|
||||
@@ -61,7 +68,7 @@ public class ProjectionOperationUnitTests {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class) // DATAMONGO-586
|
||||
public void rejectsNullFields() {
|
||||
new ProjectionOperation(null);
|
||||
new ProjectionOperation((Fields) null);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-586
|
||||
@@ -2099,6 +2106,36 @@ public class ProjectionOperationUnitTests {
|
||||
"{ $project : { newDate: { $dateFromString: { dateString : \"2017-02-08T12:10:40.787\", format : \"dd/mm/yyyy\" } } } }"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2200
|
||||
public void typeProjectionShouldIncludeTopLevelFieldsOfType() {
|
||||
|
||||
ProjectionOperation operation = Aggregation.project(Book.class);
|
||||
|
||||
Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT);
|
||||
|
||||
Assertions.assertThat(projectClause) //
|
||||
.hasSize(2) //
|
||||
.containsEntry("title", 1) //
|
||||
.containsEntry("author", 1);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2200
|
||||
public void typeProjectionShouldMapFieldNames() {
|
||||
|
||||
MongoMappingContext mappingContext = new MongoMappingContext();
|
||||
MongoConverter converter = new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext);
|
||||
|
||||
Document document = Aggregation.project(BookRenamed.class)
|
||||
.toDocument(new TypeBasedAggregationOperationContext(Book.class, mappingContext, new QueryMapper(converter)));
|
||||
Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT);
|
||||
|
||||
Assertions.assertThat(projectClause) //
|
||||
.hasSize(2) //
|
||||
.containsEntry("ti_tl_e", 1) //
|
||||
.containsEntry("author", 1);
|
||||
}
|
||||
|
||||
private static Document exctractOperation(String field, Document fromProjectClause) {
|
||||
return (Document) fromProjectClause.get(field);
|
||||
}
|
||||
@@ -2109,6 +2146,12 @@ public class ProjectionOperationUnitTests {
|
||||
Author author;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class BookRenamed {
|
||||
@Field("ti_tl_e") String title;
|
||||
Author author;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Author {
|
||||
String first;
|
||||
|
||||
Reference in New Issue
Block a user