DATAMONGO-2200 - Use mapping context where available.
Use the mapping context to get the required fields or fall back to property descriptors if no mapping context is available. Original pull request: #748.
This commit is contained in:
committed by
Mark Paluch
parent
839aa1b1e6
commit
e24c5e0846
@@ -15,9 +15,18 @@
|
||||
*/
|
||||
package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* The context for an {@link AggregationOperation}.
|
||||
@@ -66,4 +75,32 @@ public interface AggregationOperationContext {
|
||||
* @return
|
||||
*/
|
||||
FieldReference getReference(String name);
|
||||
|
||||
/**
|
||||
* Returns the {@link Fields} exposed by the type. May be a {@literal class} or an {@literal interface}.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @return never {@literal null}.
|
||||
* @since 2.2
|
||||
*/
|
||||
default Fields getFields(Class<?> type) {
|
||||
|
||||
Assert.notNull(type, "Type must not be null!");
|
||||
|
||||
List<String> fields = Arrays.stream(BeanUtils.getPropertyDescriptors(type)) //
|
||||
.filter(it -> { // object and default methods
|
||||
Method method = it.getReadMethod();
|
||||
if (method == null) {
|
||||
return false;
|
||||
}
|
||||
if (ReflectionUtils.isObjectMethod(method)) {
|
||||
return false;
|
||||
}
|
||||
return !method.isDefault();
|
||||
}) //
|
||||
.map(PropertyDescriptor::getName) //
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Fields.fields(fields.toArray(new String[0]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,15 @@ class ExposedFieldsAggregationOperationContext implements AggregationOperationCo
|
||||
return getReference(null, name);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getFields(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Fields getFields(Class<?> type) {
|
||||
return rootContext.getFields(type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a {@link FieldReference} to the given {@link Field} with the given {@code name}.
|
||||
*
|
||||
|
||||
@@ -78,4 +78,13 @@ class NestedDelegatingExpressionAggregationOperationContext implements Aggregati
|
||||
public FieldReference getReference(String name) {
|
||||
return new ExpressionFieldReference(delegate.getReference(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getFields(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Fields getFields(Class<?> type) {
|
||||
return delegate.getFields(type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,15 @@ public class PrefixingDelegatingAggregationOperationContext implements Aggregati
|
||||
return delegate.getReference(name);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getFields(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Fields getFields(Class<?> type) {
|
||||
return delegate.getFields(type);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Document doPrefix(Document source) {
|
||||
|
||||
|
||||
@@ -1737,8 +1737,9 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||
public Document toDocument(AggregationOperationContext context) {
|
||||
|
||||
Document projections = new Document();
|
||||
ReflectionUtils.doWithFields(type, it -> projections.append(it.getName(), 1));
|
||||
|
||||
Fields fields = context.getFields(type);
|
||||
fields.asList().forEach(it -> projections.append(it.getName(), 1));
|
||||
return context.getMappedObject(projections, type);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,12 @@ package org.springframework.data.mongodb.core.aggregation;
|
||||
|
||||
import static org.springframework.data.mongodb.core.aggregation.Fields.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.bson.Document;
|
||||
import org.springframework.data.mapping.PersistentPropertyPath;
|
||||
import org.springframework.data.mapping.PropertyPath;
|
||||
import org.springframework.data.mapping.SimplePropertyHandler;
|
||||
import org.springframework.data.mapping.context.MappingContext;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.DirectFieldReference;
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
@@ -101,6 +104,23 @@ public class TypeBasedAggregationOperationContext implements AggregationOperatio
|
||||
return getReferenceFor(field(name));
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mongodb.core.aggregation.AggregationOperationContext#getFields(java.lang.Class)
|
||||
*/
|
||||
@Override
|
||||
public Fields getFields(Class<?> type) {
|
||||
|
||||
MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(type);
|
||||
if (entity == null) {
|
||||
return AggregationOperationContext.super.getFields(type);
|
||||
}
|
||||
|
||||
List<String> fields = new ArrayList<>();
|
||||
entity.doWithProperties((SimplePropertyHandler) it -> fields.add(it.getName()));
|
||||
return Fields.fields(fields.toArray(new String[fields.size()]));
|
||||
}
|
||||
|
||||
private FieldReference getReferenceFor(Field field) {
|
||||
|
||||
PersistentPropertyPath<MongoPersistentProperty> propertyPath = mappingContext
|
||||
|
||||
@@ -27,7 +27,6 @@ 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;
|
||||
@@ -2114,7 +2113,7 @@ public class ProjectionOperationUnitTests {
|
||||
Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT);
|
||||
|
||||
Assertions.assertThat(projectClause) //
|
||||
assertThat(projectClause) //
|
||||
.hasSize(2) //
|
||||
.containsEntry("title", 1) //
|
||||
.containsEntry("author", 1);
|
||||
@@ -2130,12 +2129,36 @@ public class ProjectionOperationUnitTests {
|
||||
.toDocument(new TypeBasedAggregationOperationContext(Book.class, mappingContext, new QueryMapper(converter)));
|
||||
Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT);
|
||||
|
||||
Assertions.assertThat(projectClause) //
|
||||
assertThat(projectClause) //
|
||||
.hasSize(2) //
|
||||
.containsEntry("ti_tl_e", 1) //
|
||||
.containsEntry("author", 1);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2200
|
||||
public void typeProjectionShouldIncludeInterfaceProjectionValues() {
|
||||
|
||||
ProjectionOperation operation = Aggregation.project(ProjectionInterface.class);
|
||||
|
||||
Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT);
|
||||
|
||||
assertThat(projectClause) //
|
||||
.hasSize(1) //
|
||||
.containsEntry("title", 1);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-2200
|
||||
public void typeProjectionShouldBeEmptyIfNoPropertiesFound() {
|
||||
|
||||
ProjectionOperation operation = Aggregation.project(EmptyType.class);
|
||||
|
||||
Document document = operation.toDocument(Aggregation.DEFAULT_CONTEXT);
|
||||
Document projectClause = DocumentTestUtils.getAsDocument(document, PROJECT);
|
||||
|
||||
assertThat(projectClause).isEmpty();
|
||||
}
|
||||
|
||||
private static Document exctractOperation(String field, Document fromProjectClause) {
|
||||
return (Document) fromProjectClause.get(field);
|
||||
}
|
||||
@@ -2159,4 +2182,12 @@ public class ProjectionOperationUnitTests {
|
||||
String middle;
|
||||
}
|
||||
|
||||
interface ProjectionInterface {
|
||||
String getTitle();
|
||||
}
|
||||
|
||||
static class EmptyType {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user