diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java index 375e7e72a..ea831b611 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/Aggregation.java @@ -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) { diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperator.java index 3a0d5575d..881894a14 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperator.java @@ -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 clauses = new ArrayList(); 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 getClauses(AggregationOperationContext context, DBObject mappedObject) { - BasicDBList clauses = new BasicDBList(); + List clauses = new ArrayList(); 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 getClauses(AggregationOperationContext context, String key, Object predicate) { - BasicDBList clauses = new BasicDBList(); + List clauses = new ArrayList(); - if (predicate instanceof BasicDBList) { + if (predicate instanceof List) { - BasicDBList args = new BasicDBList(); - for (Object clause : (BasicDBList) predicate) { - args.addAll(getClauses(context, (BasicDBObject) clause)); + List args = new ArrayList(); + 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 args = new ArrayList(); 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 args = new ArrayList(); 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) */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/IfNullOperator.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/IfNullOperator.java index bf3a59af2..b51aba01b 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/IfNullOperator.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/IfNullOperator.java @@ -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 list = new ArrayList(); 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) */ diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java index 574ecb54c..73e11bf4d 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/ProjectionOperation.java @@ -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)); } /** diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java index 871a7cd8a..9acc01001 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationTests.java @@ -519,7 +519,7 @@ public class AggregationTests { TypedAggregation 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 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 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 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 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 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 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 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 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 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 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; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java index 645e0a66b..84408bbcb 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/AggregationUnitTests.java @@ -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) 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) 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. 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. 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. 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) { diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperatorUnitTests.java index 8eaf380b4..475559a0a 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/ConditionalOperatorUnitTests.java @@ -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. 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. asList("$luminosity", 100)); + BasicDBObject hue = new BasicDBObject("$eq", Arrays. asList("$hue", 50)); + BasicDBObject saturation = new BasicDBObject("$lt", Arrays. 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. 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. asList("$luminosity", 100)); + BasicDBObject is = new BasicDBObject("$eq", Arrays. 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. 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. asList("$luminosity", 50))) // .append("then", "very-dark") // .append("else", "not-so-dark"); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/IfNullOperatorUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/IfNullOperatorUnitTests.java index 5ecf68f3a..a7809644c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/IfNullOperatorUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/IfNullOperatorUnitTests.java @@ -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. 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. asList("$optional", "$never-null"))); } } diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java index a0322a588..b1371c00c 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/TypeBasedAggregationOperationContextUnitTests.java @@ -291,7 +291,7 @@ public class TypeBasedAggregationOperationContextUnitTests { TypedAggregation 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 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); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/IsBsonObject.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/IsBsonObject.java index d42cec284..9857ad379 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/IsBsonObject.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/test/util/IsBsonObject.java @@ -83,7 +83,15 @@ public class IsBsonObject extends TypeSafeMatcher { } 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(expectation.value).matches(o)) { diff --git a/src/main/asciidoc/reference/mongodb.adoc b/src/main/asciidoc/reference/mongodb.adoc index 3d396a4db..c8b1553e4 100644 --- a/src/main/asciidoc/reference/mongodb.adoc +++ b/src/main/asciidoc/reference/mongodb.adoc @@ -1988,8 +1988,8 @@ List 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 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 result = mongoTemplate.aggregate(agg List 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]]