DATAMONGO-1906 - Add SystemVariable $$REMOVE for aggregation $project stage.
Add, document and make sure conditional projection in aggregation is treated correctly. Original pull request: #540.
This commit is contained in:
committed by
Mark Paluch
parent
f7d65cf8d4
commit
4d8f5d63c7
@@ -64,6 +64,33 @@ public class Aggregation {
|
||||
*/
|
||||
public static final String CURRENT = SystemVariable.CURRENT.toString();
|
||||
|
||||
/**
|
||||
* A variable which evaluates to the missing value. Allows for the conditional exclusion of fields. In a
|
||||
* {@code $projection}, a field set to the variable {@literal REMOVE} is excluded from the output.
|
||||
*
|
||||
* <pre>
|
||||
* <code>
|
||||
*
|
||||
* db.books.aggregate( [
|
||||
* {
|
||||
* $project: {
|
||||
* title: 1,
|
||||
* "author.first": 1,
|
||||
* "author.last" : 1,
|
||||
* "author.middle": {
|
||||
* $cond: {
|
||||
* if: { $eq: [ "", "$author.middle" ] },
|
||||
* then: "$$REMOVE",
|
||||
* else: "$author.middle"
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* } ] )
|
||||
* </code>
|
||||
* </pre>
|
||||
*/
|
||||
public static final String REMOVE = SystemVariable.REMOVE.toString();
|
||||
|
||||
public static final AggregationOperationContext DEFAULT_CONTEXT = AggregationOperationRenderer.DEFAULT_CONTEXT;
|
||||
public static final AggregationOptions DEFAULT_OPTIONS = newAggregationOptions().build();
|
||||
|
||||
@@ -648,11 +675,12 @@ public class Aggregation {
|
||||
* Describes the system variables available in MongoDB aggregation framework pipeline expressions.
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
* @see <a href="https://docs.mongodb.org/manual/reference/aggregation-variables">Aggregation Variables</a>
|
||||
* @author Christoph Strobl
|
||||
* @see <a href="https://docs.mongodb.com/manual/reference/aggregation-variables">Aggregation Variables</a>.
|
||||
*/
|
||||
enum SystemVariable {
|
||||
|
||||
ROOT, CURRENT;
|
||||
ROOT, CURRENT, REMOVE;
|
||||
|
||||
private static final String PREFIX = "$$";
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ import static org.springframework.data.mongodb.core.aggregation.Fields.*;
|
||||
import static org.springframework.data.mongodb.core.aggregation.VariableOperators.Let.ExpressionVariable.*;
|
||||
import static org.springframework.data.mongodb.test.util.Assertions.*;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -212,6 +214,21 @@ public class ProjectionOperationUnitTests {
|
||||
assertThat((Integer) projectClause.get(Fields.UNDERSCORE_ID)).isEqualTo(0);
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-1906
|
||||
public void rendersConditionalProjectionCorrectly() {
|
||||
|
||||
TypedAggregation aggregation = Aggregation.newAggregation(Book.class,
|
||||
Aggregation.project("title")
|
||||
.and(ConditionalOperators.when(ComparisonOperators.valueOf("author.middle").equalToValue(""))
|
||||
.then("$$REMOVE").otherwiseValueOf("author.middle"))
|
||||
.as("author.middle"));
|
||||
|
||||
Document document = aggregation.toDocument("books", Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(document).isEqualTo(Document.parse(
|
||||
"{\"aggregate\" : \"books\", \"pipeline\" : [{\"$project\" : {\"title\" : 1, \"author.middle\" : {\"$cond\" : {\"if\" : {\"$eq\" : [\"$author.middle\", \"\"]}, \"then\" : \"$$REMOVE\",\"else\" : \"$author.middle\"} }}}]}"));
|
||||
}
|
||||
|
||||
@Test // DATAMONGO-757
|
||||
public void usesImplictAndExplicitFieldAliasAndIncludeExclude() {
|
||||
|
||||
@@ -1715,4 +1732,17 @@ public class ProjectionOperationUnitTests {
|
||||
return (Document) fromProjectClause.get(field);
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Book {
|
||||
String title;
|
||||
Author author;
|
||||
}
|
||||
|
||||
@Data
|
||||
static class Author {
|
||||
String first;
|
||||
String last;
|
||||
String middle;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2565,6 +2565,26 @@ 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 operation for all inventory items that have a `qty` greater or equal to `250`. A 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.
|
||||
|
||||
As of MongoDB 3.6 it is possible to exclude fields from the projection using a conditional expression.
|
||||
|
||||
.Conditional aggregation projection
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
TypedAggregation<Book> agg = Aggregation.newAggregation(Book.class,
|
||||
project("title")
|
||||
.and(ConditionalOperators.when(ComparisonOperators.valueOf("author.middle") <1>
|
||||
.equalToValue("")) <2>
|
||||
.then("$$REMOVE") <3>
|
||||
.otherwiseValueOf("author.middle") <4>
|
||||
)
|
||||
.as("author.middle"));
|
||||
----
|
||||
<1> If the value of the field `author.middle`
|
||||
<2> does not contain a value
|
||||
<3> then use https://docs.mongodb.com/manual/reference/aggregation-variables/#variable.REMOVE[``$$REMOVE``] to exclude the field
|
||||
<4> else add the field value of `author.middle`.
|
||||
====
|
||||
|
||||
[[mongo.custom-converters]]
|
||||
== Overriding default mapping with custom converters
|
||||
|
||||
Reference in New Issue
Block a user