DATAMONGO-1548 - Add support for MongoDB 3.4 aggregation operators.
We now support the following MongoDB 3.4 aggregation operators: $indexOfBytes, $indexOfCP, $split, $strLenBytes, $strLenCP, $substrCP, $indexOfArray, $range, $reverseArray, $reduce, $zip, $in, $isoDayOfWeek, $isoWeek, $isoWeekYear, $switch and $type. Original pull request: #423.
This commit is contained in:
committed by
Mark Paluch
parent
3d8b6868c7
commit
0449719a16
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.data.mongodb.core.aggregation.ExposedFields.ExposedField;
|
||||
import org.springframework.data.mongodb.core.aggregation.Fields.AggregationField;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CompositeIterator;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
@@ -406,6 +407,11 @@ public final class ExposedFields implements Iterable<ExposedField> {
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
|
||||
if(getRaw().startsWith("$")) {
|
||||
return getRaw();
|
||||
}
|
||||
|
||||
return String.format("$%s", getRaw());
|
||||
}
|
||||
|
||||
|
||||
@@ -187,13 +187,13 @@ public final class Fields implements Iterable<Field> {
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public List<Field> asList() {
|
||||
return Collections.unmodifiableList(fields);
|
||||
}
|
||||
|
||||
/**
|
||||
* Value object to encapsulate a field in an aggregation operation.
|
||||
*
|
||||
@@ -201,6 +201,7 @@ public final class Fields implements Iterable<Field> {
|
||||
*/
|
||||
static class AggregationField implements Field {
|
||||
|
||||
private final String raw;
|
||||
private final String name;
|
||||
private final String target;
|
||||
|
||||
@@ -225,6 +226,7 @@ public final class Fields implements Iterable<Field> {
|
||||
*/
|
||||
public AggregationField(String name, String target) {
|
||||
|
||||
raw = name;
|
||||
String nameToSet = cleanUp(name);
|
||||
String targetToSet = cleanUp(target);
|
||||
|
||||
@@ -266,6 +268,11 @@ public final class Fields implements Iterable<Field> {
|
||||
* @see org.springframework.data.mongodb.core.aggregation.Field#getAlias()
|
||||
*/
|
||||
public String getTarget() {
|
||||
|
||||
if (isLocalVar()) {
|
||||
return this.getRaw();
|
||||
}
|
||||
|
||||
return StringUtils.hasText(this.target) ? this.target : this.name;
|
||||
}
|
||||
|
||||
@@ -278,6 +285,22 @@ public final class Fields implements Iterable<Field> {
|
||||
return !getName().equals(getTarget());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@literal true} in case the field name starts with {@code $$}.
|
||||
* @since 1.10
|
||||
*/
|
||||
public boolean isLocalVar() {
|
||||
return raw.startsWith("$$") && !raw.startsWith("$$$");
|
||||
}
|
||||
|
||||
/**
|
||||
* @return
|
||||
* @since 1.10
|
||||
*/
|
||||
public String getRaw() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see java.lang.Object#toString()
|
||||
|
||||
@@ -491,8 +491,10 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
|
||||
} else if (ObjectUtils.nullSafeEquals(methodReference.getArgumentType(), ArgumentType.MAP)) {
|
||||
|
||||
DBObject dbo = new BasicDBObject();
|
||||
for (int i = 0; i < methodReference.getArgumentMap().length; i++) {
|
||||
dbo.put(methodReference.getArgumentMap()[i], transform(node.getChild(i), context));
|
||||
|
||||
int i = 0;
|
||||
for(ExpressionNode child : node) {
|
||||
dbo.put(methodReference.getArgumentMap()[i++], transform(child, context));
|
||||
}
|
||||
args = dbo;
|
||||
} else {
|
||||
|
||||
@@ -90,6 +90,12 @@ public class MethodReferenceNode extends ExpressionNode {
|
||||
map.put("toLower", singleArgumentAggregationMethodReference().forOperator("$toLower"));
|
||||
map.put("toUpper", singleArgumentAggregationMethodReference().forOperator("$toUpper"));
|
||||
map.put("strcasecmp", arrayArgumentAggregationMethodReference().forOperator("$strcasecmp"));
|
||||
map.put("indexOfBytes", arrayArgumentAggregationMethodReference().forOperator("$indexOfBytes"));
|
||||
map.put("indexOfCP", arrayArgumentAggregationMethodReference().forOperator("$indexOfCP"));
|
||||
map.put("split", arrayArgumentAggregationMethodReference().forOperator("$split"));
|
||||
map.put("strLenBytes", singleArgumentAggregationMethodReference().forOperator("$strLenBytes"));
|
||||
map.put("strLenCP", singleArgumentAggregationMethodReference().forOperator("$strLenCP"));
|
||||
map.put("substrCP", arrayArgumentAggregationMethodReference().forOperator("$substrCP"));
|
||||
|
||||
// TEXT SEARCH OPERATORS
|
||||
map.put("meta", singleArgumentAggregationMethodReference().forOperator("$meta"));
|
||||
@@ -102,6 +108,12 @@ public class MethodReferenceNode extends ExpressionNode {
|
||||
map.put("isArray", singleArgumentAggregationMethodReference().forOperator("$isArray"));
|
||||
map.put("size", singleArgumentAggregationMethodReference().forOperator("$size"));
|
||||
map.put("slice", arrayArgumentAggregationMethodReference().forOperator("$slice"));
|
||||
map.put("reverseArray", singleArgumentAggregationMethodReference().forOperator("$reverseArray"));
|
||||
map.put("reduce", mapArgumentAggregationMethodReference().forOperator("$reduce").mappingParametersTo("input",
|
||||
"initialValue", "in"));
|
||||
map.put("zip", mapArgumentAggregationMethodReference().forOperator("$zip").mappingParametersTo("inputs",
|
||||
"useLongestLength", "defaults"));
|
||||
map.put("in", arrayArgumentAggregationMethodReference().forOperator("$in"));
|
||||
|
||||
// VARIABLE OPERATORS
|
||||
map.put("map", mapArgumentAggregationMethodReference().forOperator("$map") //
|
||||
@@ -124,6 +136,9 @@ public class MethodReferenceNode extends ExpressionNode {
|
||||
map.put("millisecond", singleArgumentAggregationMethodReference().forOperator("$millisecond"));
|
||||
map.put("dateToString", mapArgumentAggregationMethodReference().forOperator("$dateToString") //
|
||||
.mappingParametersTo("format", "date"));
|
||||
map.put("isoDayOfWeek", singleArgumentAggregationMethodReference().forOperator("$isoDayOfWeek"));
|
||||
map.put("isoWeek", singleArgumentAggregationMethodReference().forOperator("$isoWeek"));
|
||||
map.put("isoWeekYear", singleArgumentAggregationMethodReference().forOperator("$isoWeekYear"));
|
||||
|
||||
// CONDITIONAL OPERATORS
|
||||
map.put("cond", mapArgumentAggregationMethodReference().forOperator("$cond") //
|
||||
@@ -142,6 +157,9 @@ public class MethodReferenceNode extends ExpressionNode {
|
||||
map.put("stdDevPop", arrayArgumentAggregationMethodReference().forOperator("$stdDevPop"));
|
||||
map.put("stdDevSamp", arrayArgumentAggregationMethodReference().forOperator("$stdDevSamp"));
|
||||
|
||||
// TYPE OPERATORS
|
||||
map.put("type", singleArgumentAggregationMethodReference().forOperator("$type"));
|
||||
|
||||
FUNCTIONS = Collections.unmodifiableMap(map);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,13 +28,35 @@ import static org.springframework.data.mongodb.util.DBObjectUtils.*;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.hamcrest.Matchers;
|
||||
import org.junit.Test;
|
||||
import org.springframework.data.domain.Range;
|
||||
import org.springframework.data.mongodb.core.DBObjectTestUtils;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.And;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ArithmeticOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ArrayOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Avg;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.BooleanOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ComparisonOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.ConditionalOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.DateOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Gte;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Let.ExpressionVariable;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.LiteralOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Lt;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.RangeOperator;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Reduce.PropertyExpression;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Reduce.Variable;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.SetOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.StringOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Switch.CaseOperator;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.Type;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.VariableOperators;
|
||||
import org.springframework.data.mongodb.core.aggregation.ProjectionOperation.ProjectionOperationBuilder;
|
||||
import org.springframework.data.mongodb.core.aggregation.AggregationExpressions.*;
|
||||
|
||||
import com.mongodb.BasicDBObject;
|
||||
import com.mongodb.BasicDBObjectBuilder;
|
||||
import com.mongodb.DBObject;
|
||||
import com.mongodb.util.JSON;
|
||||
|
||||
@@ -1792,7 +1814,296 @@ public class ProjectionOperationUnitTests {
|
||||
"}}}}")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIndexOfBytesCorrectly() {
|
||||
|
||||
DBObject agg = project().and(StringOperators.valueOf("item").indexOf("foo")).as("byteLocation")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project: { byteLocation: { $indexOfBytes: [ \"$item\", \"foo\" ] } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIndexOfBytesWithRangeCorrectly() {
|
||||
|
||||
DBObject agg = project().and(StringOperators.valueOf("item").indexOf("foo").within(new Range<Long>(5L, 9L)))
|
||||
.as("byteLocation").toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, isBsonObject().containing("$project.byteLocation.$indexOfBytes.[2]", 5L)
|
||||
.containing("$project.byteLocation.$indexOfBytes.[3]", 9L));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIndexOfCPCorrectly() {
|
||||
|
||||
DBObject agg = project().and(StringOperators.valueOf("item").indexOfCP("foo")).as("cpLocation")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project: { cpLocation: { $indexOfCP: [ \"$item\", \"foo\" ] } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIndexOfCPWithRangeCorrectly() {
|
||||
|
||||
DBObject agg = project().and(StringOperators.valueOf("item").indexOfCP("foo").within(new Range<Long>(5L, 9L)))
|
||||
.as("cpLocation").toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, isBsonObject().containing("$project.cpLocation.$indexOfCP.[2]", 5L)
|
||||
.containing("$project.cpLocation.$indexOfCP.[3]", 9L));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderSplitCorrectly() {
|
||||
|
||||
DBObject agg = project().and(StringOperators.valueOf("city").split(", ")).as("city_state")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { city_state : { $split: [\"$city\", \", \"] }} }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderStrLenBytesCorrectly() {
|
||||
|
||||
DBObject agg = project().and(StringOperators.valueOf("name").length()).as("length")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { \"length\": { $strLenBytes: \"$name\" } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderStrLenCPCorrectly() {
|
||||
|
||||
DBObject agg = project().and(StringOperators.valueOf("name").lengthCP()).as("length")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { \"length\": { $strLenCP: \"$name\" } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderSubstrCPCorrectly() {
|
||||
|
||||
DBObject agg = project().and(StringOperators.valueOf("quarter").substringCP(0, 2)).as("yearSubstring")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { yearSubstring: { $substrCP: [ \"$quarter\", 0, 2 ] } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIndexOfArrayCorrectly() {
|
||||
|
||||
DBObject agg = project().and(ArrayOperators.arrayOf("items").indexOf(2)).as("index")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { index: { $indexOfArray: [ \"$items\", 2 ] } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderRangeCorrectly() {
|
||||
|
||||
DBObject agg = project().and(RangeOperator.rangeStartingAt(0L).to("distance").withStepSize(25L)).as("rest_stops")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, isBsonObject().containing("$project.rest_stops.$range.[0]", 0L)
|
||||
.containing("$project.rest_stops.$range.[1]", "$distance").containing("$project.rest_stops.$range.[2]", 25L));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderReverseArrayCorrectly() {
|
||||
|
||||
DBObject agg = project().and(ArrayOperators.arrayOf("favorites").reverse()).as("reverseFavorites")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { reverseFavorites: { $reverseArray: \"$favorites\" } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderReduceWithSimpleObjectCorrectly() {
|
||||
|
||||
DBObject agg = project()
|
||||
.and(ArrayOperators.arrayOf("probabilityArr")
|
||||
.reduce(ArithmeticOperators.valueOf("$$value").multiplyBy("$$this")).startingWith(1))
|
||||
.as("results").toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse(
|
||||
"{ $project : { \"results\": { $reduce: { input: \"$probabilityArr\", initialValue: 1, in: { $multiply: [ \"$$value\", \"$$this\" ] } } } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderReduceWithComplexObjectCorrectly() {
|
||||
|
||||
PropertyExpression sum = PropertyExpression.property("sum").definedAs(
|
||||
ArithmeticOperators.valueOf(Variable.VALUE.referingTo("sum").getName()).add(Variable.THIS.getName()));
|
||||
PropertyExpression product = PropertyExpression.property("product").definedAs(ArithmeticOperators
|
||||
.valueOf(Variable.VALUE.referingTo("product").getName()).multiplyBy(Variable.THIS.getName()));
|
||||
|
||||
DBObject agg = project()
|
||||
.and(ArrayOperators.arrayOf("probabilityArr").reduce(sum, product)
|
||||
.startingWith(new BasicDBObjectBuilder().add("sum", 5).add("product", 2).get()))
|
||||
.as("results").toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse(
|
||||
"{ $project : { \"results\": { $reduce: { input: \"$probabilityArr\", initialValue: { \"sum\" : 5 , \"product\" : 2} , in: { \"sum\": { $add : [\"$$value.sum\", \"$$this\"] }, \"product\": { $multiply: [ \"$$value.product\", \"$$this\" ] } } } } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderZipCorrectly() {
|
||||
|
||||
AggregationExpression elemAt0 = ArrayOperators.arrayOf("matrix").elementAt(0);
|
||||
AggregationExpression elemAt1 = ArrayOperators.arrayOf("matrix").elementAt(1);
|
||||
AggregationExpression elemAt2 = ArrayOperators.arrayOf("matrix").elementAt(2);
|
||||
|
||||
DBObject agg = project().and(
|
||||
ArrayOperators.arrayOf(elemAt0).zipWith(elemAt1, elemAt2).useLongestLength().defaultTo(new Object[] { 1, 2 }))
|
||||
.as("transposed").toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse(
|
||||
"{ $project : { transposed: { $zip: { inputs: [ { $arrayElemAt: [ \"$matrix\", 0 ] }, { $arrayElemAt: [ \"$matrix\", 1 ] }, { $arrayElemAt: [ \"$matrix\", 2 ] } ], useLongestLength : true, defaults: [1,2] } } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderInCorrectly() {
|
||||
|
||||
DBObject agg = project().and(ArrayOperators.arrayOf("in_stock").containsValue("bananas")).as("has_bananas")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { has_bananas : { $in : [\"bananas\", \"$in_stock\" ] } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIsoDayOfWeekCorrectly() {
|
||||
|
||||
DBObject agg = project().and(DateOperators.dateOf("birthday").isoDayOfWeek()).as("dayOfWeek")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { dayOfWeek: { $isoDayOfWeek: \"$birthday\" } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIsoWeekCorrectly() {
|
||||
|
||||
DBObject agg = project().and(DateOperators.dateOf("date").isoWeek()).as("weekNumber")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { weekNumber: { $isoWeek: \"$date\" } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderIsoWeekYearCorrectly() {
|
||||
|
||||
DBObject agg = project().and(DateOperators.dateOf("date").isoWeekYear()).as("yearNumber")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { yearNumber: { $isoWeekYear: \"$date\" } } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderSwitchCorrectly() {
|
||||
|
||||
String expected = "$switch:\n" + //
|
||||
"{\n" + //
|
||||
" branches: [\n" + //
|
||||
" {\n" + //
|
||||
" case: { $gte : [ { $avg : \"$scores\" }, 90 ] },\n" + //
|
||||
" then: \"Doing great!\"\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" case: { $and : [ { $gte : [ { $avg : \"$scores\" }, 80 ] },\n" + //
|
||||
" { $lt : [ { $avg : \"$scores\" }, 90 ] } ] },\n" + //
|
||||
" then: \"Doing pretty well.\"\n" + //
|
||||
" },\n" + //
|
||||
" {\n" + //
|
||||
" case: { $lt : [ { $avg : \"$scores\" }, 80 ] },\n" + //
|
||||
" then: \"Needs improvement.\"\n" + //
|
||||
" }\n" + //
|
||||
" ],\n" + //
|
||||
" default: \"No scores found.\"\n" + //
|
||||
" }\n" + //
|
||||
"}";
|
||||
|
||||
CaseOperator cond1 = CaseOperator.when(Gte.valueOf(Avg.avgOf("scores")).greaterThanEqualToValue(90))
|
||||
.then("Doing great!");
|
||||
CaseOperator cond2 = CaseOperator.when(And.and(Gte.valueOf(Avg.avgOf("scores")).greaterThanEqualToValue(80),
|
||||
Lt.valueOf(Avg.avgOf("scores")).lessThanValue(90))).then("Doing pretty well.");
|
||||
CaseOperator cond3 = CaseOperator.when(Lt.valueOf(Avg.avgOf("scores")).lessThanValue(80))
|
||||
.then("Needs improvement.");
|
||||
|
||||
DBObject agg = project().and(ConditionalOperators.switchCases(cond1, cond2, cond3).defaultTo("No scores found."))
|
||||
.as("summary").toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { summary: {" + expected + "} } }")));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldTypeCorrectly() {
|
||||
|
||||
DBObject agg = project().and(Type.typeOf("a")).as("a")
|
||||
.toDBObject(Aggregation.DEFAULT_CONTEXT);
|
||||
|
||||
assertThat(agg, Matchers.is(JSON.parse("{ $project : { a: { $type: \"$a\" } } }")));
|
||||
}
|
||||
|
||||
private static DBObject exctractOperation(String field, DBObject fromProjectClause) {
|
||||
return (DBObject) fromProjectClause.get(field);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -745,13 +745,13 @@ public class SpelExpressionTransformerUnitTests {
|
||||
assertThat(transform("min(a, b)"), is("{ \"$min\" : [ \"$a\" , \"$b\"]}"));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1530
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceNodePush() {
|
||||
assertThat(transform("push({'item':'$item', 'quantity':'$qty'})"), is("{ \"$push\" : { \"item\" : \"$item\" , \"quantity\" : \"$qty\"}}"));
|
||||
assertThat(transform("push({'item':'$item', 'quantity':'$qty'})"),
|
||||
is("{ \"$push\" : { \"item\" : \"$item\" , \"quantity\" : \"$qty\"}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -884,6 +884,129 @@ public class SpelExpressionTransformerUnitTests {
|
||||
assertThat(transform("!(foo > 10)"), is("{ \"$not\" : [ { \"$gt\" : [ \"$foo\" , 10]}]}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceIndexOfBytes() {
|
||||
assertThat(transform("indexOfBytes(item, 'foo')"), is("{ \"$indexOfBytes\" : [ \"$item\" , \"foo\"]}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceIndexOfCP() {
|
||||
assertThat(transform("indexOfCP(item, 'foo')"), is("{ \"$indexOfCP\" : [ \"$item\" , \"foo\"]}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceSplit() {
|
||||
assertThat(transform("split(item, ',')"), is("{ \"$split\" : [ \"$item\" , \",\"]}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceStrLenBytes() {
|
||||
assertThat(transform("strLenBytes(item)"), is("{ \"$strLenBytes\" : \"$item\"}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceStrLenCP() {
|
||||
assertThat(transform("strLenCP(item)"), is("{ \"$strLenCP\" : \"$item\"}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodSubstrCP() {
|
||||
assertThat(transform("substrCP(item, 0, 5)"), is("{ \"$substrCP\" : [ \"$item\" , 0 , 5]}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceReverseArray() {
|
||||
assertThat(transform("reverseArray(array)"), is("{ \"$reverseArray\" : \"$array\"}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceReduce() {
|
||||
assertThat(transform("reduce(field, '', {'$concat':new String[]{'$$value','$$this'}})"), is(
|
||||
"{ \"$reduce\" : { \"input\" : \"$field\" , \"initialValue\" : \"\" , \"in\" : { \"$concat\" : [ \"$$value\" , \"$$this\"]}}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceZip() {
|
||||
assertThat(transform("zip(new String[]{'$array1', '$array2'})"),
|
||||
is("{ \"$zip\" : { \"inputs\" : [ \"$array1\" , \"$array2\"]}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodReferenceZipWithOptionalArgs() {
|
||||
assertThat(transform("zip(new String[]{'$array1', '$array2'}, true, new int[]{1,2})"), is(
|
||||
"{ \"$zip\" : { \"inputs\" : [ \"$array1\" , \"$array2\"] , \"useLongestLength\" : true , \"defaults\" : [ 1 , 2]}}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodIn() {
|
||||
assertThat(transform("in('item', array)"), is("{ \"$in\" : [ \"item\" , \"$array\"]}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodRefereneIsoDayOfWeek() {
|
||||
assertThat(transform("isoDayOfWeek(date)"), is("{ \"$isoDayOfWeek\" : \"$date\"}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodRefereneIsoWeek() {
|
||||
assertThat(transform("isoWeek(date)"), is("{ \"$isoWeek\" : \"$date\"}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodRefereneIsoWeekYear() {
|
||||
assertThat(transform("isoWeekYear(date)"), is("{ \"$isoWeekYear\" : \"$date\"}"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAMONGO-1548
|
||||
*/
|
||||
@Test
|
||||
public void shouldRenderMethodRefereneType() {
|
||||
assertThat(transform("type(a)"), is("{ \"$type\" : \"$a\"}"));
|
||||
}
|
||||
|
||||
private String transform(String expression, Object... params) {
|
||||
Object result = transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
|
||||
return result == null ? null : result.toString();
|
||||
|
||||
@@ -1686,26 +1686,28 @@ At the time of this writing we provide support for the following Aggregation Ope
|
||||
| abs, add (*via plus), ceil, divide, exp, floor, ln, log, log10, mod, multiply, pow, sqrt, subtract (*via minus), trunc
|
||||
|
||||
| String Aggregation Operators
|
||||
| concat, substr, toLower, toUpper, stcasecmp
|
||||
| concat, substr, toLower, toUpper, stcasecmp, indexOfBytes, indexOfCP, split, strLenBytes, strLenCP, substrCP,
|
||||
|
||||
| Comparison Aggregation Operators
|
||||
| eq (*via: is), gt, gte, lt, lte, ne
|
||||
|
||||
| Array Aggregation Operators
|
||||
| arrayElementAt, concatArrays, filter, isArray, size, slice
|
||||
| arrayElementAt, concatArrays, filter, in, indexOfArray, isArray, range, reverseArray, reduce, size, slice, zip
|
||||
|
||||
| Literal Operators
|
||||
| literal
|
||||
|
||||
| Date Aggregation Operators
|
||||
| dayOfYear, dayOfMonth, dayOfWeek, year, month, week, hour, minute, second, millisecond, dateToString
|
||||
| dayOfYear, dayOfMonth, dayOfWeek, year, month, week, hour, minute, second, millisecond, dateToString, isoDayOfWeek, isoWeek, isoWeekYear
|
||||
|
||||
| Variable Operators
|
||||
| map
|
||||
|
||||
|
||||
| Conditional Aggregation Operators
|
||||
| cond, ifNull
|
||||
| cond, ifNull, switch
|
||||
|
||||
| Type Aggregation Operators
|
||||
| type
|
||||
|
||||
|===
|
||||
|
||||
|
||||
Reference in New Issue
Block a user