Skip to content

DATAMONGO-757 - Projections should follow mongodb conventions more preci... #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public ProjectionOperation() {
* @param fields must not be {@literal null}.
*/
public ProjectionOperation(Fields fields) {
this(NONE, ProjectionOperationBuilder.FieldProjection.from(fields, true));
this(NONE, ProjectionOperationBuilder.FieldProjection.from(fields));
}

/**
Expand Down Expand Up @@ -117,23 +117,34 @@ public ProjectionOperationBuilder and(String name) {
/**
* 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(
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fields.UNDERSCORE_ID.equals(fieldName),
String
.format(
"Exclusion of field %s not allowed. Projections by the mongodb aggregation framework only support the exclusion of the %s field!",
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);
}

Expand Down Expand Up @@ -387,31 +398,34 @@ private FieldProjection(Field field, Object 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.
* <p>
* A field {@code foo} will be projected as: {@code foo: '$foo'}
* <p>
*
* @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;
Expand All @@ -423,13 +437,32 @@ public static List<FieldProjection> from(Fields fields, boolean include) {
*/
@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;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public void alwaysUsesExplicitReference() {
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"));
}

Expand Down Expand Up @@ -197,14 +197,33 @@ public void excludeShouldThrowExceptionForFieldsOtherThanUnderscoreId() {
* @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);
DBObject projectClause = DBObjectUtils.getAsDBObject(dbObject, PROJECT);
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((Integer) projectClause.get("foo"), is(1)); // implicit
assertThat(projectClause.get("bar"), is((Object) "$foobar")); // explicit
assertThat((Integer) projectClause.get("inc1"), is(1)); // include shortcut
assertThat((Integer) projectClause.get("inc2"), is(1));
assertThat((Integer) projectClause.get("_id"), is(0));
}

@Test(expected = IllegalArgumentException.class)
public void arithmenticProjectionOperationModByZeroException() {

Expand Down