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 {
|
public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
||||||
|
|
||||||
private static final List<Projection> NONE = Collections.emptyList();
|
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;
|
private final List<Projection> projections;
|
||||||
|
|
||||||
@@ -60,7 +62,7 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
|||||||
* @param fields must not be {@literal null}.
|
* @param fields must not be {@literal null}.
|
||||||
*/
|
*/
|
||||||
public ProjectionOperation(Fields fields) {
|
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.
|
* Excludes the given fields from the projection.
|
||||||
*
|
*
|
||||||
* @param fields must not be {@literal null}.
|
* @param fieldNames must not be {@literal null}.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public ProjectionOperation andExclude(String... fields) {
|
public ProjectionOperation andExclude(String... fieldNames) {
|
||||||
List<FieldProjection> excludeProjections = FieldProjection.from(Fields.fields(fields), false);
|
|
||||||
|
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);
|
return new ProjectionOperation(this.projections, excludeProjections);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Includes the given fields into the projection.
|
* Includes the given fields into the projection.
|
||||||
*
|
*
|
||||||
* @param fields must not be {@literal null}.
|
* @param fieldNames must not be {@literal null}.
|
||||||
* @return
|
* @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);
|
return new ProjectionOperation(this.projections, projections);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -387,31 +395,31 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
|||||||
this.value = value;
|
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}.
|
* 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 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
|
* @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!");
|
Assert.notNull(fields, "Fields must not be null!");
|
||||||
List<FieldProjection> projections = new ArrayList<FieldProjection>();
|
List<FieldProjection> projections = new ArrayList<FieldProjection>();
|
||||||
|
|
||||||
for (Field field : fields) {
|
for (Field field : fields) {
|
||||||
|
projections.add(new FieldProjection(field, value));
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return projections;
|
return projections;
|
||||||
@@ -423,13 +431,32 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public DBObject toDBObject(AggregationOperationContext context) {
|
public DBObject toDBObject(AggregationOperationContext context) {
|
||||||
|
return new BasicDBObject(field.getName(), renderFieldValue(context));
|
||||||
|
}
|
||||||
|
|
||||||
if (value != null) {
|
private Object renderFieldValue(AggregationOperationContext context) {
|
||||||
return new BasicDBObject(field.getName(), value);
|
|
||||||
|
// 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 value;
|
||||||
return new BasicDBObject(field.getName(), reference.toString());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ public class ProjectionOperationUnitTests {
|
|||||||
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
DBObject dbObject = operation.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||||
DBObject projectClause = DBObjectUtils.getAsDBObject(dbObject, PROJECT);
|
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"));
|
assertThat(projectClause.get("bar"), is((Object) "$foobar"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +197,7 @@ public class ProjectionOperationUnitTests {
|
|||||||
* @see DATAMONGO-758
|
* @see DATAMONGO-758
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void excludeShouldExclusionOfUnderscoreId() {
|
public void excludeShouldAllowExclusionOfUnderscoreId() {
|
||||||
|
|
||||||
ProjectionOperation projectionOp = new ProjectionOperation().andExclude(Fields.UNDERSCORE_ID);
|
ProjectionOperation projectionOp = new ProjectionOperation().andExclude(Fields.UNDERSCORE_ID);
|
||||||
DBObject dbObject = projectionOp.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
DBObject dbObject = projectionOp.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||||
@@ -205,6 +205,25 @@ public class ProjectionOperationUnitTests {
|
|||||||
assertThat((Integer) projectClause.get(Fields.UNDERSCORE_ID), is(0));
|
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)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void arithmenticProjectionOperationModByZeroException() {
|
public void arithmenticProjectionOperationModByZeroException() {
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user