Skip to content

Commit 59464f3

Browse files
christophstroblmp911de
authored andcommitted
Add support for $bottomN aggregation operator.
Closes #4139 Original pull request: #4182.
1 parent 052cfdf commit 59464f3

File tree

5 files changed

+59
-8
lines changed

5 files changed

+59
-8
lines changed

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SelectionOperators.java

+31-1
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,14 @@ private Bottom(Object value) {
3838
super(value);
3939
}
4040

41+
/**
42+
* In case a limit value ({@literal n}) is present {@literal $bottomN} is used instead of {@literal $bottom}.
43+
*
44+
* @return
45+
*/
4146
@Override
4247
protected String getMongoMethod() {
43-
return "$bottom";
48+
return get("n") == null ? "$bottom" : "$bottomN";
4449
}
4550

4651
/**
@@ -50,6 +55,31 @@ public static Bottom bottom() {
5055
return new Bottom(Collections.emptyMap());
5156
}
5257

58+
/**
59+
* Limits the number of returned elements to the given value.
60+
*
61+
* @param numberOfResults
62+
* @return new instance of {@link Bottom}.
63+
*/
64+
public Bottom limit(int numberOfResults) {
65+
return limit((Object) numberOfResults);
66+
}
67+
68+
/**
69+
* Limits the number of returned elements to the value defined by the given {@link AggregationExpression
70+
* expression}.
71+
*
72+
* @param expression must not be {@literal null}.
73+
* @return new instance of {@link Bottom}.
74+
*/
75+
public Bottom limit(AggregationExpression expression) {
76+
return limit((Object) expression);
77+
}
78+
79+
private Bottom limit(Object value) {
80+
return new Bottom(append("n", value));
81+
}
82+
5383
/**
5484
* Define result ordering.
5585
*

Diff for: spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java

+4-3
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@ public class MethodReferenceNode extends ExpressionNode {
212212
// OBJECT OPERATORS
213213
map.put("objectToArray", singleArgRef().forOperator("$objectToArray"));
214214
map.put("mergeObjects", arrayArgRef().forOperator("$mergeObjects"));
215+
map.put("bottom", mapArgRef().forOperator("$bottom") //
216+
.mappingParametersTo("output", "sortBy"));
217+
map.put("bottomN", mapArgRef().forOperator("$bottomN") //
218+
.mappingParametersTo("n", "output", "sortBy"));
215219

216220
// CONVERT OPERATORS
217221
map.put("convert", mapArgRef().forOperator("$convert") //
@@ -226,9 +230,6 @@ public class MethodReferenceNode extends ExpressionNode {
226230
map.put("toString", singleArgRef().forOperator("$toString"));
227231
map.put("degreesToRadians", singleArgRef().forOperator("$degreesToRadians"));
228232

229-
// SELECT OPERATORS
230-
map.put("bottom", mapArgRef().forOperator("$bottom") //
231-
.mappingParametersTo("output", "sortBy"));
232233

233234
FUNCTIONS = Collections.unmodifiableMap(map);
234235
}

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SelectionOperatorUnitTests.java

+18
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,24 @@ void bottomMapsFieldNamesCorrectly() {
7171
"""));
7272
}
7373

74+
@Test // GH-4139
75+
void bottomNRenderedCorrectly() {
76+
77+
Document document = SelectionOperators.Bottom.bottom().output(Fields.fields("playerId", "score"))
78+
.sortBy(Sort.by(Direction.DESC, "score")).limit(3).toDocument(Aggregation.DEFAULT_CONTEXT);
79+
80+
assertThat(document).isEqualTo(Document.parse("""
81+
{
82+
$bottomN:
83+
{
84+
n : 3,
85+
output: [ "$playerId", "$score" ],
86+
sortBy: { "score": -1 }
87+
}
88+
}
89+
"""));
90+
}
91+
7492
static class Player {
7593

7694
@Field("player_id") String playerId;

Diff for: spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java

+5
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,11 @@ void shouldRenderBottom() {
11791179
assertThat(transform("bottom(new String[]{\"$playerId\", \"$score\" }, { \"score\" : -1 })")).isEqualTo("{ $bottom : { output: [ \"$playerId\", \"$score\" ], sortBy: { \"score\": -1 }}}");
11801180
}
11811181

1182+
@Test // GH-4139
1183+
void shouldRenderBottomN() {
1184+
assertThat(transform("bottomN(3, new String[]{\"$playerId\", \"$score\" }, { \"score\" : -1 })")).isEqualTo("{ $bottomN : { n : 3, output: [ \"$playerId\", \"$score\" ], sortBy: { \"score\": -1 }}}");
1185+
}
1186+
11821187
private Document transform(String expression, Object... params) {
11831188
return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
11841189
}

Diff for: src/main/asciidoc/reference/aggregation-framework.adoc

+1-4
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ At the time of this writing, we provide support for the following Aggregation Op
8282
| `setEquals`, `setIntersection`, `setUnion`, `setDifference`, `setIsSubset`, `anyElementTrue`, `allElementsTrue`
8383

8484
| Group/Accumulator Aggregation Operators
85-
| `addToSet`, `covariancePop`, `covarianceSamp`, `expMovingAvg`, `first`, `last`, `max`, `min`, `avg`, `push`, `sum`, `count` (+++*+++), `stdDevPop`, `stdDevSamp`
85+
| `addToSet`, `bottom`, `bottomN`, `covariancePop`, `covarianceSamp`, `expMovingAvg`, `first`, `last`, `max`, `min`, `avg`, `push`, `sum`, `count` (+++*+++), `stdDevPop`, `stdDevSamp`
8686

8787
| Arithmetic Aggregation Operators
8888
| `abs`, `acos`, `acosh`, `add` (+++*+++ via `plus`), `asin`, `asin`, `atan`, `atan2`, `atanh`, `ceil`, `cos`, `cosh`, `derivative`, `divide`, `exp`, `floor`, `integral`, `ln`, `log`, `log10`, `mod`, `multiply`, `pow`, `round`, `sqrt`, `subtract` (+++*+++ via `minus`), `sin`, `sinh`, `tan`, `tanh`, `trunc`
@@ -120,9 +120,6 @@ At the time of this writing, we provide support for the following Aggregation Op
120120
| Script Aggregation Operators
121121
| `function`, `accumulator`
122122

123-
| Selection Aggregation Operators
124-
| `bottom`
125-
126123
|===
127124

128125
+++*+++ The operation is mapped or added by Spring Data MongoDB.

0 commit comments

Comments
 (0)