Browse Source

DATAMONGO-757 - Align output of projection operation with MongoDB defaults.

Adjusted FieldProjection to generate an appropriate representation of included / excluded fields (namely :1 for included and :0 for excluded).
Polished guards to handle only _id is allowed to be excluded (DATAMONGO-758).

Original pull request: #76.
pull/73/merge
Thomas Darimont 12 years ago committed by Oliver Gierke
parent
commit
51bab838b0
  1. 77
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java
  2. 23
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java

77
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java

@ -44,6 +44,8 @@ import com.mongodb.DBObject; @@ -44,6 +44,8 @@ import com.mongodb.DBObject;
public class ProjectionOperation implements FieldsExposingAggregationOperation {
private static final List<Projection> NONE = Collections.emptyList();
private static final String EXCLUSION_ERROR = "Exclusion of field %s not allowed. Projections by the mongodb "
+ "aggregation framework only support the exclusion of the %s field!";
private final List<Projection> projections;
@ -60,7 +62,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { @@ -60,7 +62,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
* @param fields must not be {@literal null}.
*/
public ProjectionOperation(Fields fields) {
this(NONE, ProjectionOperationBuilder.FieldProjection.from(fields, true));
this(NONE, ProjectionOperationBuilder.FieldProjection.from(fields));
}
/**
@ -117,23 +119,29 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { @@ -117,23 +119,29 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
/**
* Excludes the given fields from the projection.
*
* @param fields must not be {@literal null}.
* @param fieldNames must not be {@literal null}.
* @return
*/
public ProjectionOperation andExclude(String... fields) {
List<FieldProjection> excludeProjections = FieldProjection.from(Fields.fields(fields), false);
public ProjectionOperation andExclude(String... fieldNames) {
for (String fieldName : fieldNames) {
Assert.isTrue(Fields.UNDERSCORE_ID.equals(fieldName),
String.format(EXCLUSION_ERROR, fieldName, Fields.UNDERSCORE_ID));
}
List<FieldProjection> excludeProjections = FieldProjection.from(Fields.fields(fieldNames), false);
return new ProjectionOperation(this.projections, excludeProjections);
}
/**
* Includes the given fields into the projection.
*
* @param fields must not be {@literal null}.
* @param fieldNames must not be {@literal null}.
* @return
*/
public ProjectionOperation andInclude(String... fields) {
public ProjectionOperation andInclude(String... fieldNames) {
List<FieldProjection> projections = FieldProjection.from(Fields.fields(fields), true);
List<FieldProjection> projections = FieldProjection.from(Fields.fields(fieldNames), true);
return new ProjectionOperation(this.projections, projections);
}
@ -387,31 +395,31 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { @@ -387,31 +395,31 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
this.value = value;
}
/**
* Factory method to easily create {@link FieldProjection}s for the given {@link Fields}. Fields are projected as
* references with their given name. A field {@code foo} will be projected as: {@code foo : 1 } .
*
* @param fields the {@link Fields} to in- or exclude, must not be {@literal null}.
* @return
*/
public static List<? extends Projection> from(Fields fields) {
return from(fields, null);
}
/**
* Factory method to easily create {@link FieldProjection}s for the given {@link Fields}.
*
* @param fields the {@link Fields} to in- or exclude, must not be {@literal null}.
* @param include whether to include or exclude the fields.
* @param value to use for the given field.
* @return
*/
public static List<FieldProjection> from(Fields fields, boolean include) {
public static List<FieldProjection> from(Fields fields, Object value) {
Assert.notNull(fields, "Fields must not be null!");
List<FieldProjection> projections = new ArrayList<FieldProjection>();
for (Field field : fields) {
if (!include) {
if (!Fields.UNDERSCORE_ID.equals(field.getName())) {
throw new IllegalArgumentException(
String
.format(
"Exclusion of field %s not allowed. Projections by the mongodb aggregation framework only support the exclusion of the %s field!",
field.getName(), Fields.UNDERSCORE_ID));
}
}
projections.add(new FieldProjection(field, include ? null : 0));
projections.add(new FieldProjection(field, value));
}
return projections;
@ -423,13 +431,32 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation { @@ -423,13 +431,32 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
*/
@Override
public DBObject toDBObject(AggregationOperationContext context) {
return new BasicDBObject(field.getName(), renderFieldValue(context));
}
private Object renderFieldValue(AggregationOperationContext context) {
// implicit reference or explicit include?
if (value == null || Boolean.TRUE.equals(value)) {
// check whether referenced field exists in the context
FieldReference reference = context.getReference(field.getTarget());
if (field.getName().equals(field.getTarget())) {
// render field as included
return 1;
}
// render field reference
return reference.toString();
} else if (Boolean.FALSE.equals(value)) {
if (value != null) {
return new BasicDBObject(field.getName(), value);
// render field as excluded
return 0;
}
FieldReference reference = context.getReference(field.getTarget());
return new BasicDBObject(field.getName(), reference.toString());
return value;
}
}

23
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperationUnitTests.java

@ -66,7 +66,7 @@ public class ProjectionOperationUnitTests { @@ -66,7 +66,7 @@ public class ProjectionOperationUnitTests {
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT);
DBObject projectClause = DBObjectUtils.getAsDBObject(dbObject, PROJECT);
assertThat(projectClause.get("foo"), is((Object) "$foo"));
assertThat((Integer) projectClause.get("foo"), is(1));
assertThat(projectClause.get("bar"), is((Object) "$foobar"));
}
@ -197,7 +197,7 @@ public class ProjectionOperationUnitTests { @@ -197,7 +197,7 @@ public class ProjectionOperationUnitTests {
* @see DATAMONGO-758
*/
@Test
public void excludeShouldExclusionOfUnderscoreId() {
public void excludeShouldAllowExclusionOfUnderscoreId() {
ProjectionOperation projectionOp = new ProjectionOperation().andExclude(Fields.UNDERSCORE_ID);
DBObject dbObject = projectionOp.toDBObject(Aggregation.DEFAULT_CONTEXT);
@ -205,6 +205,25 @@ public class ProjectionOperationUnitTests { @@ -205,6 +205,25 @@ public class ProjectionOperationUnitTests {
assertThat((Integer) projectClause.get(Fields.UNDERSCORE_ID), is(0));
}
/**
* @see DATAMONGO-757
*/
@Test
public void usesImplictAndExplicitFieldAliasAndIncludeExclude() {
ProjectionOperation operation = Aggregation.project("foo").and("foobar").as("bar").andInclude("inc1", "inc2")
.andExclude("_id");
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT);
DBObject projectClause = DBObjectUtils.getAsDBObject(dbObject, PROJECT);
assertThat(projectClause.get("foo"), is((Object) 1)); // implicit
assertThat(projectClause.get("bar"), is((Object) "$foobar")); // explicit
assertThat(projectClause.get("inc1"), is((Object) 1)); // include shortcut
assertThat(projectClause.get("inc2"), is((Object) 1));
assertThat(projectClause.get("_id"), is((Object) 0));
}
@Test(expected = IllegalArgumentException.class)
public void arithmenticProjectionOperationModByZeroException() {

Loading…
Cancel
Save