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.
This commit is contained in:
committed by
Oliver Gierke
parent
f7540d45c6
commit
857f366b56
@@ -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 {
|
||||
* @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 {
|
||||
/**
|
||||
* 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 {
|
||||
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 {
|
||||
*/
|
||||
@Override
|
||||
public DBObject toDBObject(AggregationOperationContext context) {
|
||||
return new BasicDBObject(field.getName(), renderFieldValue(context));
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
return new BasicDBObject(field.getName(), value);
|
||||
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)) {
|
||||
|
||||
// render field as excluded
|
||||
return 0;
|
||||
}
|
||||
|
||||
FieldReference reference = context.getReference(field.getTarget());
|
||||
return new BasicDBObject(field.getName(), reference.toString());
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
* @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 {
|
||||
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() {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user