DATAMONGO-861 - Polishing.

Favor usage of List over BasicDBList.
Rename ProjectionOperation.transform to applyCondition.
Add missing author and since tags, remove trailing white spaces and fix reference documentation headline clash.

Original Pull Request: #385
This commit is contained in:
Christoph Strobl
2016-08-25 14:44:36 +02:00
parent ace01e4e6d
commit eb1392cc1a
11 changed files with 199 additions and 170 deletions

View File

@@ -25,7 +25,7 @@ import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
import org.springframework.data.mongodb.core.aggregation.ExposedFields.FieldReference;
import org.springframework.data.mongodb.core.aggregation.Fields.*;
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
import org.springframework.data.mongodb.core.aggregation.FieldsExposingAggregationOperation.InheritsFieldsAggregationOperation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.NearQuery;
@@ -331,13 +331,13 @@ public class Aggregation {
}
/**
* Creates a new {@link OutOperation} using the given collection name. This operation must be the last operation
* in the pipeline.
* Creates a new {@link OutOperation} using the given collection name. This operation must be the last operation in
* the pipeline.
*
* @param outCollectionName collection name to export aggregation results. The {@link OutOperation} creates a new
* collection in the current database if one does not already exist. The collection is
* not visible until the aggregation completes. If the aggregation fails, MongoDB does
* not create the collection. Must not be {@literal null}.
* collection in the current database if one does not already exist. The collection is not visible until the
* aggregation completes. If the aggregation fails, MongoDB does not create the collection. Must not be
* {@literal null}.
* @return
*/
public static OutOperation out(String outCollectionName) {

View File

@@ -15,12 +15,14 @@
*/
package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@@ -32,6 +34,7 @@ import com.mongodb.DBObject;
*
* @see http://docs.mongodb.com/manual/reference/operator/aggregation/cond/
* @author Mark Paluch
* @author Christoph Strobl
* @since 1.10
*/
public class ConditionalOperator implements AggregationExpression {
@@ -42,7 +45,7 @@ public class ConditionalOperator implements AggregationExpression {
/**
* Creates a new {@link ConditionalOperator} for a given {@link Field} and {@code then}/{@code otherwise} values.
*
*
* @param condition must not be {@literal null}.
* @param thenValue must not be {@literal null}.
* @param otherwiseValue must not be {@literal null}.
@@ -116,8 +119,7 @@ public class ConditionalOperator implements AggregationExpression {
return ((ConditionalOperator) value).toDbObject(context);
}
DBObject toMap = context.getMappedObject(new BasicDBObject("$set", value));
return toMap.get("$set");
return context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
}
private Object resolveCriteria(AggregationOperationContext context, Object value) {
@@ -129,7 +131,7 @@ public class ConditionalOperator implements AggregationExpression {
if (value instanceof CriteriaDefinition) {
DBObject mappedObject = context.getMappedObject(((CriteriaDefinition) value).getCriteriaObject());
BasicDBList clauses = new BasicDBList();
List<Object> clauses = new ArrayList<Object>();
clauses.addAll(getClauses(context, mappedObject));
@@ -144,9 +146,9 @@ public class ConditionalOperator implements AggregationExpression {
String.format("Invalid value in condition. Supported: DBObject, Field references, Criteria, got: %s", value));
}
private BasicDBList getClauses(AggregationOperationContext context, DBObject mappedObject) {
private List<Object> getClauses(AggregationOperationContext context, DBObject mappedObject) {
BasicDBList clauses = new BasicDBList();
List<Object> clauses = new ArrayList<Object>();
for (String key : mappedObject.keySet()) {
@@ -157,15 +159,17 @@ public class ConditionalOperator implements AggregationExpression {
return clauses;
}
private BasicDBList getClauses(AggregationOperationContext context, String key, Object predicate) {
private List<Object> getClauses(AggregationOperationContext context, String key, Object predicate) {
BasicDBList clauses = new BasicDBList();
List<Object> clauses = new ArrayList<Object>();
if (predicate instanceof BasicDBList) {
if (predicate instanceof List) {
BasicDBList args = new BasicDBList();
for (Object clause : (BasicDBList) predicate) {
args.addAll(getClauses(context, (BasicDBObject) clause));
List<Object> args = new ArrayList<Object>();
for (Object clause : (List<?>) predicate) {
if (clause instanceof DBObject) {
args.addAll(getClauses(context, (DBObject) clause));
}
}
clauses.add(new BasicDBObject(key, args));
@@ -180,7 +184,7 @@ public class ConditionalOperator implements AggregationExpression {
continue;
}
BasicDBList args = new BasicDBList();
List<Object> args = new ArrayList<Object>();
args.add("$" + key);
args.add(nested.get(s));
clauses.add(new BasicDBObject(s, args));
@@ -188,7 +192,7 @@ public class ConditionalOperator implements AggregationExpression {
} else if (!isKeyword(key)) {
BasicDBList args = new BasicDBList();
List<Object> args = new ArrayList<Object>();
args.add("$" + key);
args.add(predicate);
clauses.add(new BasicDBObject("$eq", args));
@@ -230,6 +234,9 @@ public class ConditionalOperator implements AggregationExpression {
return ConditionalExpressionBuilder.newBuilder();
}
/**
* @since 1.10
*/
public static interface WhenBuilder {
/**
@@ -257,6 +264,9 @@ public class ConditionalOperator implements AggregationExpression {
ThenBuilder when(CriteriaDefinition criteria);
}
/**
* @since 1.10
*/
public static interface ThenBuilder {
/**
@@ -268,6 +278,9 @@ public class ConditionalOperator implements AggregationExpression {
OtherwiseBuilder then(Object value);
}
/**
* @since 1.10
*/
public static interface OtherwiseBuilder {
/**
@@ -314,7 +327,7 @@ public class ConditionalOperator implements AggregationExpression {
return this;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.WhenBuilder#when(org.springframework.data.mongodb.core.query.CriteriaDefinition)
*/
@@ -327,7 +340,7 @@ public class ConditionalOperator implements AggregationExpression {
return this;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.WhenBuilder#when(org.springframework.data.mongodb.core.aggregation.Field)
*/
@@ -340,7 +353,7 @@ public class ConditionalOperator implements AggregationExpression {
return this;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.WhenBuilder#when(java.lang.String)
*/
@@ -353,7 +366,7 @@ public class ConditionalOperator implements AggregationExpression {
return this;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.ThenBuilder#then(java.lang.Object)
*/
@@ -366,7 +379,7 @@ public class ConditionalOperator implements AggregationExpression {
return this;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ConditionalOperator.OtherwiseBuilder#otherwise(java.lang.Object)
*/

View File

@@ -16,16 +16,18 @@
package org.springframework.data.mongodb.core.aggregation;
import java.util.ArrayList;
import java.util.List;
import org.springframework.util.Assert;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
/**
* Encapsulates the aggregation framework {@code $ifNull} operator. Replacement values can be either {@link Field field
* references}, values of simple MongoDB types or values that can be converted to a simple MongoDB type.
*
*
* @see http://docs.mongodb.com/manual/reference/operator/aggregation/ifNull/
* @author Mark Paluch
* @since 1.10
@@ -37,7 +39,7 @@ public class IfNullOperator implements AggregationExpression {
/**
* Creates a new {@link IfNullOperator} for the given {@link Field} and replacement {@code value}.
*
*
* @param field must not be {@literal null}.
* @param value must not be {@literal null}.
*/
@@ -50,28 +52,32 @@ public class IfNullOperator implements AggregationExpression {
this.value = value;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.AggregationExpression#toDbObject(org.springframework.data.mongodb.core.aggregation.AggregationOperationContext)
*/
@Override
public DBObject toDbObject(AggregationOperationContext context) {
BasicDBList list = new BasicDBList();
List<Object> list = new ArrayList<Object>();
list.add(context.getReference(field).toString());
if (value instanceof Field) {
list.add(context.getReference((Field) value).toString());
} else {
DBObject toMap = context.getMappedObject(new BasicDBObject("$set", value));
list.add(toMap.get("$set"));
}
list.add(resolve(value, context));
return new BasicDBObject("$ifNull", list);
}
private Object resolve(Object value, AggregationOperationContext context) {
if (value instanceof Field) {
return context.getReference((Field) value).toString();
} else if (value instanceof DBObject) {
return value;
}
return context.getMappedObject(new BasicDBObject("$set", value)).get("$set");
}
/**
* Get a builder that allows fluent creation of {@link IfNullOperator}.
*
@@ -81,6 +87,9 @@ public class IfNullOperator implements AggregationExpression {
return IfNullOperatorBuilder.newBuilder();
}
/**
* @since 1.10
*/
public static interface IfNullBuilder {
/**
@@ -96,6 +105,9 @@ public class IfNullOperator implements AggregationExpression {
ThenBuilder ifNull(String field);
}
/**
* @since 1.10
*/
public static interface ThenBuilder {
/**
@@ -134,11 +146,10 @@ public class IfNullOperator implements AggregationExpression {
return new IfNullOperatorBuilder();
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.IfNullOperator.IfNullBuilder#ifNull(org.springframework.data.mongodb.core.aggregation.Field)
*/
public ThenBuilder ifNull(Field field) {
Assert.notNull(field, "Field must not be null!");
@@ -147,7 +158,7 @@ public class IfNullOperator implements AggregationExpression {
return this;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.IfNullOperator.IfNullBuilder#ifNull(java.lang.String)
*/
@@ -159,7 +170,7 @@ public class IfNullOperator implements AggregationExpression {
return this;
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.IfNullOperator.ThenReplaceBuilder#thenReplaceWith(org.springframework.data.mongodb.core.aggregation.Field)
*/
@@ -171,7 +182,7 @@ public class IfNullOperator implements AggregationExpression {
return new IfNullOperator(this.field, replacementField);
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.IfNullOperator.ThenReplaceBuilder#thenReplaceWith(java.lang.Object)
*/

View File

@@ -42,6 +42,7 @@ import com.mongodb.DBObject;
* @author Thomas Darimont
* @author Oliver Gierke
* @author Christoph Strobl
* @author Mark Paluch
* @since 1.3
*/
public class ProjectionOperation implements FieldsExposingAggregationOperation {
@@ -244,20 +245,20 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
/**
* Apply a conditional projection using {@link ConditionalOperator}.
*
* @param conditional must not be {@literal null}.
* @param conditionalOperator must not be {@literal null}.
* @return never {@literal null}.
* @since 1.10
*/
public abstract ProjectionOperation transform(ConditionalOperator conditional);
public abstract ProjectionOperation applyCondition(ConditionalOperator conditionalOperator);
/**
* Apply a conditional value replacement for {@literal null} values using {@link IfNullOperator}.
*
* @param ifNull must not be {@literal null}.
* @param ifNullOperator must not be {@literal null}.
* @return never {@literal null}.
* @since 1.10
*/
public abstract ProjectionOperation transform(IfNullOperator ifNull);
public abstract ProjectionOperation applyCondition(IfNullOperator ifNullOperator);
}
/**
@@ -359,7 +360,8 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
return new BasicDBObject(getExposedField().getName(), toMongoExpression(context, expression, params));
}
protected static Object toMongoExpression(AggregationOperationContext context, String expression, Object[] params) {
protected static Object toMongoExpression(AggregationOperationContext context, String expression,
Object[] params) {
return TRANSFORMER.transform(expression, context, params);
}
}
@@ -455,22 +457,26 @@ public class ProjectionOperation implements FieldsExposingAggregationOperation {
return this.operation.and(new FieldProjection(Fields.field(alias, name), null));
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.AbstractProjectionOperationBuilder#transform(org.springframework.data.mongodb.core.aggregation.ConditionalOperator)
*/
@Override
public ProjectionOperation transform(ConditionalOperator conditional) {
return this.operation.and(new ExpressionProjection(Fields.field(name), conditional));
public ProjectionOperation applyCondition(ConditionalOperator conditionalOperator) {
Assert.notNull(conditionalOperator, "ConditionalOperator must not be null!");
return this.operation.and(new ExpressionProjection(Fields.field(name), conditionalOperator));
}
/*
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.core.aggregation.ProjectionOperation.AbstractProjectionOperationBuilder#transform(org.springframework.data.mongodb.core.aggregation.IfNullOperator)
*/
@Override
public ProjectionOperation transform(IfNullOperator ifNull) {
return this.operation.and(new ExpressionProjection(Fields.field(name), ifNull));
public ProjectionOperation applyCondition(IfNullOperator ifNullOperator) {
Assert.notNull(ifNullOperator, "IfNullOperator must not be null!");
return this.operation.and(new ExpressionProjection(Fields.field(name), ifNullOperator));
}
/**

View File

@@ -519,7 +519,7 @@ public class AggregationTests {
TypedAggregation<InventoryItem> aggregation = newAggregation(InventoryItem.class, //
project("item") //
.and("discount")//
.transform(ConditionalOperator.newBuilder().when(Criteria.where("qty").gte(250)) //
.applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("qty").gte(250)) //
.then(30) //
.otherwise(20)));
@@ -594,7 +594,7 @@ public class AggregationTests {
TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class, //
project() //
.and("largePopulation")//
.transform(ConditionalOperator.newBuilder().when(Criteria.where("population").gte(20000)) //
.applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("population").gte(20000)) //
.then(true) //
.otherwise(false)) //
.and("population").as("population"));
@@ -619,7 +619,7 @@ public class AggregationTests {
TypedAggregation<ZipInfo> aggregation = newAggregation(ZipInfo.class, //
project() //
.and("size")//
.transform(ConditionalOperator.newBuilder().when(Criteria.where("population").gte(20000)) //
.applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("population").gte(20000)) //
.then(ConditionalOperator.newBuilder().when(Criteria.where("population").gte(200000)).then("huge")
.otherwise("small")) //
.otherwise("small")) //
@@ -648,7 +648,7 @@ public class AggregationTests {
TypedAggregation<LineItem> aggregation = newAggregation(LineItem.class, //
project("id") //
.and("caption")//
.transform(ifNull(field("caption"), "unknown")),
.applyCondition(ifNull(field("caption"), "unknown")),
sort(ASC, "id"));
assertThat(aggregation.toString(), is(notNullValue()));
@@ -675,7 +675,7 @@ public class AggregationTests {
TypedAggregation<LineItem> aggregation = newAggregation(LineItem.class, //
project("id") //
.and("caption")//
.transform(ifNull(field("caption"), field("id"))),
.applyCondition(ifNull(field("caption"), field("id"))),
sort(ASC, "id"));
assertThat(aggregation.toString(), is(notNullValue()));
@@ -798,11 +798,12 @@ public class AggregationTests {
assertThat((Double) resultList.get(0).get("netPriceMul2"), is(product.netPrice * 2));
assertThat((Double) resultList.get(0).get("netPriceDiv119"), is(product.netPrice / 1.19));
assertThat((Integer) resultList.get(0).get("spaceUnitsMod2"), is(product.spaceUnits % 2));
assertThat((Integer) resultList.get(0).get("spaceUnitsPlusSpaceUnits"), is(product.spaceUnits + product.spaceUnits));
assertThat((Integer) resultList.get(0).get("spaceUnitsPlusSpaceUnits"),
is(product.spaceUnits + product.spaceUnits));
assertThat((Integer) resultList.get(0).get("spaceUnitsMinusSpaceUnits"),
is(product.spaceUnits - product.spaceUnits));
assertThat((Integer) resultList.get(0).get("spaceUnitsMultiplySpaceUnits"), is(product.spaceUnits
* product.spaceUnits));
assertThat((Integer) resultList.get(0).get("spaceUnitsMultiplySpaceUnits"),
is(product.spaceUnits * product.spaceUnits));
assertThat((Double) resultList.get(0).get("spaceUnitsDivideSpaceUnits"),
is((double) (product.spaceUnits / product.spaceUnits)));
assertThat((Integer) resultList.get(0).get("spaceUnitsModSpaceUnits"), is(product.spaceUnits % product.spaceUnits));
@@ -891,8 +892,8 @@ public class AggregationTests {
DBObject firstItem = resultList.get(0);
assertThat((String) firstItem.get("_id"), is(product.id));
assertThat((String) firstItem.get("name"), is(product.name));
assertThat((Double) firstItem.get("salesPrice"), is((product.netPrice * (1 - product.discountRate) + shippingCosts)
* (1 + product.taxRate)));
assertThat((Double) firstItem.get("salesPrice"),
is((product.netPrice * (1 - product.discountRate) + shippingCosts) * (1 + product.taxRate)));
}
@Test
@@ -914,7 +915,7 @@ public class AggregationTests {
/**
* @see DATAMONGO-753
* @see http
* @see http
* ://stackoverflow.com/questions/18653574/spring-data-mongodb-aggregation-framework-invalid-reference-in-group
* -operati
*/
@@ -944,7 +945,7 @@ public class AggregationTests {
/**
* @see DATAMONGO-753
* @see http
* @see http
* ://stackoverflow.com/questions/18653574/spring-data-mongodb-aggregation-framework-invalid-reference-in-group
* -operati
*/
@@ -979,12 +980,13 @@ public class AggregationTests {
data.stringValue = "ABC";
mongoTemplate.insert(data);
TypedAggregation<Data> agg = newAggregation(Data.class, project() //
.andExpression("concat(stringValue, 'DE')").as("concat") //
.andExpression("strcasecmp(stringValue,'XYZ')").as("strcasecmp") //
.andExpression("substr(stringValue,1,1)").as("substr") //
.andExpression("toLower(stringValue)").as("toLower") //
.andExpression("toUpper(toLower(stringValue))").as("toUpper") //
TypedAggregation<Data> agg = newAggregation(Data.class,
project() //
.andExpression("concat(stringValue, 'DE')").as("concat") //
.andExpression("strcasecmp(stringValue,'XYZ')").as("strcasecmp") //
.andExpression("substr(stringValue,1,1)").as("substr") //
.andExpression("toLower(stringValue)").as("toLower") //
.andExpression("toUpper(toLower(stringValue))").as("toUpper") //
);
AggregationResults<DBObject> results = mongoTemplate.aggregate(agg, DBObject.class);
@@ -1010,17 +1012,18 @@ public class AggregationTests {
data.dateValue = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SSSZ").parse("29.08.1983 12:34:56.789+0000");
mongoTemplate.insert(data);
TypedAggregation<Data> agg = newAggregation(Data.class, project() //
.andExpression("dayOfYear(dateValue)").as("dayOfYear") //
.andExpression("dayOfMonth(dateValue)").as("dayOfMonth") //
.andExpression("dayOfWeek(dateValue)").as("dayOfWeek") //
.andExpression("year(dateValue)").as("year") //
.andExpression("month(dateValue)").as("month") //
.andExpression("week(dateValue)").as("week") //
.andExpression("hour(dateValue)").as("hour") //
.andExpression("minute(dateValue)").as("minute") //
.andExpression("second(dateValue)").as("second") //
.andExpression("millisecond(dateValue)").as("millisecond") //
TypedAggregation<Data> agg = newAggregation(Data.class,
project() //
.andExpression("dayOfYear(dateValue)").as("dayOfYear") //
.andExpression("dayOfMonth(dateValue)").as("dayOfMonth") //
.andExpression("dayOfWeek(dateValue)").as("dayOfWeek") //
.andExpression("year(dateValue)").as("year") //
.andExpression("month(dateValue)").as("month") //
.andExpression("week(dateValue)").as("week") //
.andExpression("hour(dateValue)").as("hour") //
.andExpression("minute(dateValue)").as("minute") //
.andExpression("second(dateValue)").as("second") //
.andExpression("millisecond(dateValue)").as("millisecond") //
);
AggregationResults<DBObject> results = mongoTemplate.aggregate(agg, DBObject.class);
@@ -1132,7 +1135,7 @@ public class AggregationTests {
.and("orderId").previousOperation() //
.andExpression("netAmount * [0]", taxRate).as("taxAmount") //
.andExpression("netAmount * (1 + [0])", taxRate).as("totalAmount") //
), Invoice.class);
), Invoice.class);
Invoice invoice = results.getUniqueMappedResult();
@@ -1723,12 +1726,14 @@ public class AggregationTests {
public InventoryItem() {}
public InventoryItem(int id, String item, int qty) {
this.id = id;
this.item = item;
this.qty = qty;
}
public InventoryItem(int id, String item, String description, int qty) {
this.id = id;
this.item = item;
this.description = description;

View File

@@ -24,6 +24,7 @@ import static org.springframework.data.mongodb.core.query.Criteria.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.junit.Rule;
@@ -31,9 +32,7 @@ import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.BasicDBObjectBuilder;
import com.mongodb.DBObject;
@@ -247,25 +246,6 @@ public class AggregationUnitTests {
assertThat(fields.get("foosum"), is((Object) new BasicDBObject("$sum", "$foo")));
}
/**
* @see DATAMONGO-861
*/
@Test
public void conditionExpressionBasedFieldsShouldBeReferencableInFollowingOperations() {
DBObject agg = newAggregation( //
project("a"), //
group("a").first(conditional(Criteria.where("a").gte(42), "answer", "no-answer")).as("foosum") //
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
@SuppressWarnings("unchecked")
DBObject secondProjection = ((List<DBObject>) agg.get("pipeline")).get(1);
DBObject fields = getAsDBObject(secondProjection, "$group");
assertThat(getAsDBObject(fields, "foosum"), isBsonObject().containing("$first"));
assertThat(getAsDBObject(fields, "foosum"), isBsonObject().containing("$first.$cond.then", "answer"));
assertThat(getAsDBObject(fields, "foosum"), isBsonObject().containing("$first.$cond.else", "no-answer"));
}
/**
* @see DATAMONGO-908
*/
@@ -331,15 +311,16 @@ public class AggregationUnitTests {
DBObject agg = newAggregation( //
project().and("a").as("aa") //
) //
.withOptions(aggregationOptions) //
.withOptions(aggregationOptions) //
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
assertThat(agg.toString(), is("{ \"aggregate\" : \"foo\" , " //
+ "\"pipeline\" : [ { \"$project\" : { \"aa\" : \"$a\"}}] , " //
+ "\"allowDiskUse\" : true , " //
+ "\"explain\" : true , " //
+ "\"cursor\" : { \"foo\" : 1}}" //
));
assertThat(agg.toString(),
is("{ \"aggregate\" : \"foo\" , " //
+ "\"pipeline\" : [ { \"$project\" : { \"aa\" : \"$a\"}}] , " //
+ "\"allowDiskUse\" : true , " //
+ "\"explain\" : true , " //
+ "\"cursor\" : { \"foo\" : 1}}" //
));
}
/**
@@ -357,8 +338,8 @@ public class AggregationUnitTests {
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject projection0 = extractPipelineElement(agg, 0, "$project");
assertThat(projection0, is((DBObject) new BasicDBObject("someKey", 1).append("a1", "$a")
.append("a2", "$$CURRENT.a")));
assertThat(projection0,
is((DBObject) new BasicDBObject("someKey", 1).append("a1", "$a").append("a2", "$$CURRENT.a")));
DBObject sort = extractPipelineElement(agg, 1, "$sort");
assertThat(sort, is((DBObject) new BasicDBObject("a", -1)));
@@ -379,7 +360,7 @@ public class AggregationUnitTests {
.and("tags").minus(10).as("tags_count")//
, group("date")//
.sum("tags_count").as("count")//
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject group = extractPipelineElement(agg, 1, "$group");
assertThat(getAsDBObject(group, "count"), is(new BasicDBObjectBuilder().add("$sum", "$tags_count").get()));
@@ -396,12 +377,31 @@ public class AggregationUnitTests {
.andExpression("tags-10")//
, group("date")//
.sum("tags_count").as("count")//
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject group = extractPipelineElement(agg, 1, "$group");
assertThat(getAsDBObject(group, "count"), is(new BasicDBObjectBuilder().add("$sum", "$tags_count").get()));
}
/**
* @see DATAMONGO-861
*/
@Test
public void conditionExpressionBasedFieldsShouldBeReferencableInFollowingOperations() {
DBObject agg = newAggregation( //
project("a"), //
group("a").first(conditional(Criteria.where("a").gte(42), "answer", "no-answer")).as("foosum") //
).toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
@SuppressWarnings("unchecked")
DBObject secondProjection = ((List<DBObject>) agg.get("pipeline")).get(1);
DBObject fields = getAsDBObject(secondProjection, "$group");
assertThat(getAsDBObject(fields, "foosum"), isBsonObject().containing("$first"));
assertThat(getAsDBObject(fields, "foosum"), isBsonObject().containing("$first.$cond.then", "answer"));
assertThat(getAsDBObject(fields, "foosum"), isBsonObject().containing("$first.$cond.else", "no-answer"));
}
/**
* @see DATAMONGO-861
*/
@@ -432,7 +432,7 @@ public class AggregationUnitTests {
DBObject agg = Aggregation.newAggregation(//
project().and("color")
.transform(ConditionalOperator.newBuilder() //
.applyCondition(ConditionalOperator.newBuilder() //
.when("isYellow") //
.then("bright") //
.otherwise("dark")))
@@ -456,12 +456,12 @@ public class AggregationUnitTests {
DBObject agg = Aggregation
.newAggregation(project()//
.and("color")//
.transform(conditional(Criteria.where("key").gt(5), "bright", "dark"))) //
.applyCondition(conditional(Criteria.where("key").gt(5), "bright", "dark"))) //
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject project = extractPipelineElement(agg, 0, "$project");
DBObject expectedCondition = new BasicDBObject() //
.append("if", new BasicDBObject("$gt", new BasicDbListBuilder().add("$key").add(5).get())) //
.append("if", new BasicDBObject("$gt", Arrays.<Object> asList("$key", 5))) //
.append("then", "bright") //
.append("else", "dark");
@@ -478,7 +478,7 @@ public class AggregationUnitTests {
.newAggregation(//
project().and("color").as("chroma"),
project().and("luminosity") //
.transform(conditional(field("chroma"), "bright", "dark"))) //
.applyCondition(conditional(field("chroma"), "bright", "dark"))) //
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject project = extractPipelineElement(agg, 1, "$project");
@@ -500,12 +500,12 @@ public class AggregationUnitTests {
.newAggregation(//
project().and("color").as("chroma"),
project().and("luminosity") //
.transform(conditional(Criteria.where("chroma").is(100), "bright", "dark"))) //
.applyCondition(conditional(Criteria.where("chroma").is(100), "bright", "dark"))) //
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject project = extractPipelineElement(agg, 1, "$project");
DBObject expectedCondition = new BasicDBObject() //
.append("if", new BasicDBObject("$eq", new BasicDbListBuilder().add("$chroma").add(100).get())) //
.append("if", new BasicDBObject("$eq", Arrays.<Object> asList("$chroma", 100))) //
.append("then", "bright") //
.append("else", "dark");
@@ -522,16 +522,13 @@ public class AggregationUnitTests {
.newAggregation(//
project().and("color"), //
project().and("luminosity") //
.transform(ifNull(field("chroma"), "unknown"))) //
.applyCondition(ifNull(field("chroma"), "unknown"))) //
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject project = extractPipelineElement(agg, 1, "$project");
BasicDBList expectedCondition = new BasicDbListBuilder() //
.add("$chroma") //
.add("unknown")//
.get();
assertThat(getAsDBObject(project, "luminosity"), isBsonObject().containing("$ifNull", expectedCondition));
assertThat(getAsDBObject(project, "luminosity"),
isBsonObject().containing("$ifNull", Arrays.<Object> asList("$chroma", "unknown")));
}
/**
@@ -544,16 +541,13 @@ public class AggregationUnitTests {
.newAggregation(//
project("fallback").and("color").as("chroma"),
project().and("luminosity") //
.transform(ifNull(field("chroma"), field("fallback")))) //
.applyCondition(ifNull(field("chroma"), field("fallback")))) //
.toDbObject("foo", Aggregation.DEFAULT_CONTEXT);
DBObject project = extractPipelineElement(agg, 1, "$project");
BasicDBList expectedCondition = new BasicDbListBuilder() //
.add("$chroma") //
.add("$fallback")//
.get();
assertThat(getAsDBObject(project, "luminosity"), isBsonObject().containing("$ifNull", expectedCondition));
assertThat(getAsDBObject(project, "luminosity"),
isBsonObject().containing("$ifNull", Arrays.asList("$chroma", "$fallback")));
}
private DBObject extractPipelineElement(DBObject agg, int index, String operation) {

View File

@@ -19,9 +19,10 @@ import static org.junit.Assert.*;
import static org.springframework.data.mongodb.core.aggregation.ConditionalOperator.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
@@ -30,6 +31,7 @@ import com.mongodb.DBObject;
* Unit tests for {@link ConditionalOperator}.
*
* @author Mark Paluch
* @author Christoph Strobl
*/
public class ConditionalOperatorUnitTests {
@@ -117,7 +119,7 @@ public class ConditionalOperatorUnitTests {
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
DBObject expectedCondition = new BasicDBObject() //
.append("if", new BasicDBObject("$gte", new BasicDbListBuilder().add("$luminosity").add(100).get())) //
.append("if", new BasicDBObject("$gte", Arrays.<Object> asList("$luminosity", 100))) //
.append("then", "bright") //
.append("else", "dark");
@@ -138,14 +140,12 @@ public class ConditionalOperatorUnitTests {
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
BasicDBObject luminosity = new BasicDBObject("$gte", new BasicDbListBuilder().add("$luminosity").add(100).get());
BasicDBObject hue = new BasicDBObject("$eq", new BasicDbListBuilder().add("$hue").add(50).get());
BasicDBObject saturation = new BasicDBObject("$lt", new BasicDbListBuilder().add("$saturation").add(11).get());
BasicDBObject luminosity = new BasicDBObject("$gte", Arrays.<Object> asList("$luminosity", 100));
BasicDBObject hue = new BasicDBObject("$eq", Arrays.<Object> asList("$hue", 50));
BasicDBObject saturation = new BasicDBObject("$lt", Arrays.<Object> asList("$saturation", 11));
DBObject expectedCondition = new BasicDBObject() //
.append("if",
new BasicDbListBuilder().add(luminosity)
.add(new BasicDBObject("$and", new BasicDbListBuilder().add(hue).add(saturation).get())).get()) //
.append("if", Arrays.<Object> asList(luminosity, new BasicDBObject("$and", Arrays.asList(hue, saturation)))) //
.append("then", "bright") //
.append("else", "dark");
@@ -164,11 +164,11 @@ public class ConditionalOperatorUnitTests {
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
BasicDBObject gte = new BasicDBObject("$gte", new BasicDbListBuilder().add("$luminosity").add(100).get());
BasicDBObject is = new BasicDBObject("$eq", new BasicDbListBuilder().add("$chroma").add(200).get());
BasicDBObject gte = new BasicDBObject("$gte", Arrays.<Object> asList("$luminosity", 100));
BasicDBObject is = new BasicDBObject("$eq", Arrays.<Object> asList("$chroma", 200));
DBObject expectedCondition = new BasicDBObject() //
.append("if", new BasicDbListBuilder().add(gte).add(is).get()) //
.append("if", Arrays.asList(gte, is)) //
.append("then", "bright") //
.append("else", "dark");
@@ -195,12 +195,12 @@ public class ConditionalOperatorUnitTests {
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
DBObject trueCondition = new BasicDBObject() //
.append("if", new BasicDBObject("$gte", new BasicDbListBuilder().add("$luminosity").add(200).get())) //
.append("if", new BasicDBObject("$gte", Arrays.<Object> asList("$luminosity", 200))) //
.append("then", "verybright") //
.append("else", "not-so-bright");
DBObject falseCondition = new BasicDBObject() //
.append("if", new BasicDBObject("$lt", new BasicDbListBuilder().add("$luminosity").add(50).get())) //
.append("if", new BasicDBObject("$lt", Arrays.<Object> asList("$luminosity", 50))) //
.append("then", "very-dark") //
.append("else", "not-so-dark");

View File

@@ -19,16 +19,17 @@ package org.springframework.data.mongodb.core.aggregation;
import static org.junit.Assert.*;
import static org.springframework.data.mongodb.test.util.IsBsonObject.*;
import org.junit.Test;
import org.springframework.data.mongodb.test.util.BasicDbListBuilder;
import java.util.Arrays;
import org.junit.Test;
import com.mongodb.BasicDBList;
import com.mongodb.DBObject;
/**
* Unit tests for {@link IfNullOperator}.
*
* @author Mark Paluch
* @author Christoph Strobl
*/
public class IfNullOperatorUnitTests {
@@ -60,12 +61,8 @@ public class IfNullOperatorUnitTests {
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
BasicDBList expected = new BasicDbListBuilder() //
.add("$optional") //
.add("a more sophisticated value")//
.get();
assertThat(dbObject, isBsonObject().containing("$ifNull", expected));
assertThat(dbObject,
isBsonObject().containing("$ifNull", Arrays.<Object> asList("$optional", "a more sophisticated value")));
}
/**
@@ -80,11 +77,6 @@ public class IfNullOperatorUnitTests {
DBObject dbObject = operator.toDbObject(Aggregation.DEFAULT_CONTEXT);
BasicDBList expected = new BasicDbListBuilder() //
.add("$optional") //
.add("$never-null")//
.get();
assertThat(dbObject, isBsonObject().containing("$ifNull", expected));
assertThat(dbObject, isBsonObject().containing("$ifNull", Arrays.<Object> asList("$optional", "$never-null")));
}
}

View File

@@ -291,7 +291,7 @@ public class TypeBasedAggregationOperationContextUnitTests {
TypedAggregation<FooPerson> agg = newAggregation(FooPerson.class,
project("name") //
.and("age") //
.transform(conditional(Criteria.where("age.value").lt(10), new Age(0), field("age"))) //
.applyCondition(conditional(Criteria.where("age.value").lt(10), new Age(0), field("age"))) //
);
DBObject dbo = agg.toDbObject("person", context);
@@ -317,7 +317,7 @@ public class TypeBasedAggregationOperationContextUnitTests {
TypedAggregation<FooPerson> agg = newAggregation(FooPerson.class,
project("name") //
.and("age") //
.transform(ifNull("age", new Age(0))) //
.applyCondition(ifNull("age", new Age(0))) //
);
DBObject dbo = agg.toDbObject("person", context);

View File

@@ -83,7 +83,15 @@ public class IsBsonObject<T extends BSONObject> extends TypeSafeMatcher<T> {
}
if (expectation.type != null && !ClassUtils.isAssignable(expectation.type, o.getClass())) {
return false;
if (o instanceof List) {
if (!ClassUtils.isAssignable(List.class, expectation.type)) {
return false;
}
} else {
return false;
}
}
if (expectation.value != null && !new IsEqual<Object>(expectation.value).matches(o)) {

View File

@@ -1988,8 +1988,8 @@ List<DBObject> resultList = result.getMappedResults();
Note that we can also refer to other fields of the document within the SpEL expression.
[[mongo.aggregation.examples.example6]]
.Aggregation Framework Example 6
[[mongo.aggregation.examples.example7]]
.Aggregation Framework Example 7
This example uses conditional projection. It's derived from the https://docs.mongodb.com/manual/reference/operator/aggregation/cond/[$cond reference documentation].
@@ -2019,7 +2019,7 @@ import static org.springframework.data.mongodb.core.aggregation.Aggregation.*;
TypedAggregation<InventoryItem> agg = newAggregation(InventoryItem.class,
project("item").and("discount")
.transform(ConditionalOperator.newBuilder().when(Criteria.where("qty").gte(250))
.applyCondition(ConditionalOperator.newBuilder().when(Criteria.where("qty").gte(250))
.then(30)
.otherwise(20))
.and(ifNull("description", "Unspecified")).as("description")
@@ -2029,7 +2029,7 @@ AggregationResults<InventoryItemProjection> result = mongoTemplate.aggregate(agg
List<InventoryItemProjection> stateStatsList = result.getMappedResults();
----
* This one-step aggregation uses a projection operation with the `inventory` collection. We project the `discount` field using a conditional transformation for all inventory items that have a `qty` greater or equal to `250`. An second conditional projection is performed for the `description` field. We apply the description `Unspecified` to all items that either do not have a `description` field of items that have a `null` description.
* This one-step aggregation uses a projection operation with the `inventory` collection. We project the `discount` field using a conditional operation for all inventory items that have a `qty` greater or equal to `250`. An second conditional projection is performed for the `description` field. We apply the description `Unspecified` to all items that either do not have a `description` field of items that have a `null` description.
[[mongo.custom-converters]]