From 2bd51f2336894445686970e6c6a61e14e66ad9be Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Tue, 6 Dec 2016 13:58:02 +0100 Subject: [PATCH 1/2] DATAMONGO-1533 - Add support for SpEL in GroupOperations (aggregation). Prepare issue branch. --- pom.xml | 2 +- spring-data-mongodb-cross-store/pom.xml | 4 ++-- spring-data-mongodb-distribution/pom.xml | 2 +- spring-data-mongodb-log4j/pom.xml | 2 +- spring-data-mongodb/pom.xml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index ed9720988d..97eb01ee93 100644 --- a/pom.xml +++ b/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1533-SNAPSHOT pom Spring Data MongoDB diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml index ae0a5d6c8f..0f82fc35c5 100644 --- a/spring-data-mongodb-cross-store/pom.xml +++ b/spring-data-mongodb-cross-store/pom.xml @@ -6,7 +6,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1533-SNAPSHOT ../pom.xml @@ -48,7 +48,7 @@ org.springframework.data spring-data-mongodb - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1533-SNAPSHOT diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml index 2d02722262..b3005ea132 100644 --- a/spring-data-mongodb-distribution/pom.xml +++ b/spring-data-mongodb-distribution/pom.xml @@ -13,7 +13,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1533-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml index ee5e3336db..bd9abfb638 100644 --- a/spring-data-mongodb-log4j/pom.xml +++ b/spring-data-mongodb-log4j/pom.xml @@ -5,7 +5,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1533-SNAPSHOT ../pom.xml diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 8072d3f665..c49740ed3d 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -11,7 +11,7 @@ org.springframework.data spring-data-mongodb-parent - 1.10.0.BUILD-SNAPSHOT + 1.10.0.DATAMONGO-1533-SNAPSHOT ../pom.xml From 60d5944cdea4f43e0c5ca84d2cb826c9a1d91671 Mon Sep 17 00:00:00 2001 From: Christoph Strobl Date: Wed, 14 Dec 2016 14:41:40 +0100 Subject: [PATCH 2/2] DATAMONGO-1533 - Add AggregationExpression derived from SpEL AST. We added an AggregationExpression that renders a MongoDB Aggregation Framework expression from the AST of a SpEL expression. This allows usage with various stages (eg. $project, $group) throughout the aggregation support. // { $and: [ { $gt: [ "$qty", 100 ] }, { $lt: [ "$qty", 250 ] } ] } expressionOf("qty > 100 && qty < 250); // { $cond : { if : { $gte : [ "$a", 42 ]}, then : "answer", else : "no-answer" } } expressionOf("cond(a >= 42, 'answer', 'no-answer')"); --- .../AggregationSpELExpression.java | 70 +++++++++++++++++++ .../aggregation/AggregationUnitTests.java | 22 ++++++ 2 files changed, 92 insertions(+) create mode 100644 spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java new file mode 100644 index 0000000000..fedeb09834 --- /dev/null +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/AggregationSpELExpression.java @@ -0,0 +1,70 @@ +/* + * Copyright 2016. the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.mongodb.core.aggregation; + +import com.mongodb.DBObject; +import org.springframework.util.Assert; + +/** + * An {@link AggregationExpression} that renders a MongoDB Aggregation Framework expression from the AST of a + * SpEL + * expression.
+ *
+ * Samples:
+ * + *
+ * // { $and: [ { $gt: [ "$qty", 100 ] }, { $lt: [ "$qty", 250 ] } ] }
+ * expressionOf("qty > 100 && qty < 250);
+ *
+ * // { $cond : { if : { $gte : [ "$a", 42 ]}, then : "answer", else : "no-answer" } }
+ * expressionOf("cond(a >= 42, 'answer', 'no-answer')");
+ * 
+ *
+ * + * @author Christoph Strobl + * @see SpelExpressionTransformer + * @since 1.10 + */ +public class AggregationSpELExpression implements AggregationExpression { + + private static final SpelExpressionTransformer TRANSFORMER = new SpelExpressionTransformer(); + private final String rawExpression; + private final Object[] parameters; + + private AggregationSpELExpression(String rawExpression, Object[] parameters) { + + this.rawExpression = rawExpression; + this.parameters = parameters; + } + + /** + * Creates new {@link AggregationSpELExpression} for the given {@literal expressionString} and {@literal parameters}. + * + * @param expression must not be {@literal null}. + * @param parameters can be empty. + * @return + */ + public static AggregationSpELExpression expressionOf(String expressionString, Object... parameters) { + + Assert.notNull(expressionString, "ExpressionString must not be null!"); + return new AggregationSpELExpression(expressionString, parameters); + } + + @Override + public DBObject toDbObject(AggregationOperationContext context) { + return (DBObject) TRANSFORMER.transform(rawExpression, context, parameters); + } +} 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 c865f2a269..142e173526 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 @@ -37,6 +37,7 @@ import com.mongodb.BasicDBObject; import com.mongodb.BasicDBObjectBuilder; import com.mongodb.DBObject; +import org.springframework.data.mongodb.test.util.BasicDbListBuilder; /** * Unit tests for {@link Aggregation}. @@ -595,6 +596,27 @@ public void shouldHonorDefaultCountField() { assertThat(project, isBsonObject().containing("count", 1)); } + /** + * @see DATAMONGO-1533 + */ + @Test + public void groupOperationShouldAllowUsageOfDerivedSpELAggregationExpression() { + + DBObject agg = newAggregation( // + project("a"), // + group("a").first(AggregationSpELExpression.expressionOf("cond(a >= 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.if", + new BasicDBObject("$gte", new BasicDbListBuilder().add("$a").add(42).get()))); + assertThat(getAsDBObject(fields, "foosum"), isBsonObject().containing("$first.$cond.then", "answer")); + assertThat(getAsDBObject(fields, "foosum"), isBsonObject().containing("$first.$cond.else", "no-answer")); + } + private DBObject extractPipelineElement(DBObject agg, int index, String operation) { List pipeline = (List) agg.get("pipeline");