Browse Source

Add support for $firstN aggregation operator.

Closes #4139
Original pull request: #4182.
pull/4212/head
Christoph Strobl 3 years ago committed by Mark Paluch
parent
commit
5525a4fbf9
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 94
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java
  2. 3
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
  3. 21
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SelectionOperatorUnitTests.java
  4. 5
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java

94
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java

@ -122,4 +122,98 @@ public class SelectionOperators {
return new Bottom(append("output", Arrays.asList(out))); return new Bottom(append("output", Arrays.asList(out)));
} }
} }
/**
* {@link AbstractAggregationExpression} to return the {@literal $firstN} elements.
*/
public static class First extends AbstractAggregationExpression {
protected First(Object value) {
super(value);
}
/**
* @return new instance of {@link First}.
*/
public static First first() {
return new First(Collections.emptyMap()).limit(1);
}
/**
* @return new instance of {@link First}.
*/
public static First first(int numberOfResults) {
return new First(Collections.emptyMap()).limit(numberOfResults);
}
/**
* Limits the number of returned elements to the given value.
*
* @param numberOfResults
* @return new instance of {@link Bottom}.
*/
public First limit(int numberOfResults) {
return limit((Object) numberOfResults);
}
/**
* Limits the number of returned elements to the value defined by the given {@link AggregationExpression
* expression}.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Bottom}.
*/
public First limit(AggregationExpression expression) {
return limit((Object) expression);
}
private First limit(Object value) {
return new First(append("n", value));
}
/**
* Define the field to serve as source.
*
* @param fieldName must not be {@literal null}.
* @return new instance of {@link Bottom}.
*/
public First of(String fieldName) {
return input(fieldName);
}
/**
* Define the expression building the value to serve as source.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Bottom}.
*/
public First of(AggregationExpression expression) {
return input(expression);
}
/**
* Define the field to serve as source.
*
* @param fieldName must not be {@literal null}.
* @return new instance of {@link Bottom}.
*/
public First input(String fieldName) {
return new First(append("input", Fields.field(fieldName)));
}
/**
* Define the expression building the value to serve as source.
*
* @param expression must not be {@literal null}.
* @return new instance of {@link Bottom}.
*/
public First input(AggregationExpression expression) {
return new First(append("input", expression));
}
@Override
protected String getMongoMethod() {
return "$firstN";
}
}
} }

3
spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java

@ -216,6 +216,8 @@ public class MethodReferenceNode extends ExpressionNode {
.mappingParametersTo("output", "sortBy")); .mappingParametersTo("output", "sortBy"));
map.put("bottomN", mapArgRef().forOperator("$bottomN") // map.put("bottomN", mapArgRef().forOperator("$bottomN") //
.mappingParametersTo("n", "output", "sortBy")); .mappingParametersTo("n", "output", "sortBy"));
map.put("firstN", mapArgRef().forOperator("$firstN") //
.mappingParametersTo("n", "input"));
// CONVERT OPERATORS // CONVERT OPERATORS
map.put("convert", mapArgRef().forOperator("$convert") // map.put("convert", mapArgRef().forOperator("$convert") //
@ -230,7 +232,6 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("toString", singleArgRef().forOperator("$toString")); map.put("toString", singleArgRef().forOperator("$toString"));
map.put("degreesToRadians", singleArgRef().forOperator("$degreesToRadians")); map.put("degreesToRadians", singleArgRef().forOperator("$degreesToRadians"));
FUNCTIONS = Collections.unmodifiableMap(map); FUNCTIONS = Collections.unmodifiableMap(map);
} }

21
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SelectionOperatorUnitTests.java

@ -89,6 +89,27 @@ class SelectionOperatorUnitTests {
""")); """));
} }
@Test // GH-4139
void firstNMapsFieldNamesCorrectly() {
MongoMappingContext mappingContext = new MongoMappingContext();
RelaxedTypeBasedAggregationOperationContext aggregationContext = new RelaxedTypeBasedAggregationOperationContext(
Player.class, mappingContext,
new QueryMapper(new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext)));
Document document = SelectionOperators.First.first(3).of("score").toDocument(aggregationContext);
assertThat(document).isEqualTo(Document.parse("""
{
$firstN:
{
n: 3,
input: "$s_cor_e"
}
}
"""));
}
static class Player { static class Player {
@Field("player_id") String playerId; @Field("player_id") String playerId;

5
spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java

@ -1184,6 +1184,11 @@ public class SpelExpressionTransformerUnitTests {
assertThat(transform("bottomN(3, new String[]{\"$playerId\", \"$score\" }, { \"score\" : -1 })")).isEqualTo("{ $bottomN : { n : 3, output: [ \"$playerId\", \"$score\" ], sortBy: { \"score\": -1 }}}"); assertThat(transform("bottomN(3, new String[]{\"$playerId\", \"$score\" }, { \"score\" : -1 })")).isEqualTo("{ $bottomN : { n : 3, output: [ \"$playerId\", \"$score\" ], sortBy: { \"score\": -1 }}}");
} }
@Test // GH-4139
void shouldRenderFirstN() {
assertThat(transform("firstN(3, \"$score\")")).isEqualTo("{ $firstN : { n : 3, input : \"$score\" }}");
}
private Document transform(String expression, Object... params) { private Document transform(String expression, Object... params) {
return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params); return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
} }

Loading…
Cancel
Save