Skip to content

Commit cdfe2a0

Browse files
christophstroblmp911de
authored andcommitted
Add support for $lastN aggregation operator.
Closes #4139 Original pull request: #4182.
1 parent 5525a4f commit cdfe2a0

File tree

4 files changed

+122
-0
lines changed

4 files changed

+122
-0
lines changed

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

+94
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,98 @@ protected String getMongoMethod() {
216216
return "$firstN";
217217
}
218218
}
219+
220+
/**
221+
* {@link AbstractAggregationExpression} to return the {@literal $lastN} elements.
222+
*/
223+
public static class Last extends AbstractAggregationExpression {
224+
225+
protected Last(Object value) {
226+
super(value);
227+
}
228+
229+
/**
230+
* @return new instance of {@link Last}.
231+
*/
232+
public static Last last() {
233+
return new Last(Collections.emptyMap()).limit(1);
234+
}
235+
236+
/**
237+
* @return new instance of {@link Last}.
238+
*/
239+
public static Last last(int numberOfResults) {
240+
return new Last(Collections.emptyMap()).limit(numberOfResults);
241+
}
242+
243+
/**
244+
* Limits the number of returned elements to the given value.
245+
*
246+
* @param numberOfResults
247+
* @return new instance of {@link Bottom}.
248+
*/
249+
public Last limit(int numberOfResults) {
250+
return limit((Object) numberOfResults);
251+
}
252+
253+
/**
254+
* Limits the number of returned elements to the value defined by the given {@link AggregationExpression
255+
* expression}.
256+
*
257+
* @param expression must not be {@literal null}.
258+
* @return new instance of {@link Bottom}.
259+
*/
260+
public Last limit(AggregationExpression expression) {
261+
return limit((Object) expression);
262+
}
263+
264+
private Last limit(Object value) {
265+
return new Last(append("n", value));
266+
}
267+
268+
/**
269+
* Define the field to serve as source.
270+
*
271+
* @param fieldName must not be {@literal null}.
272+
* @return new instance of {@link Bottom}.
273+
*/
274+
public Last of(String fieldName) {
275+
return input(fieldName);
276+
}
277+
278+
/**
279+
* Define the expression building the value to serve as source.
280+
*
281+
* @param expression must not be {@literal null}.
282+
* @return new instance of {@link Bottom}.
283+
*/
284+
public Last of(AggregationExpression expression) {
285+
return input(expression);
286+
}
287+
288+
/**
289+
* Define the field to serve as source.
290+
*
291+
* @param fieldName must not be {@literal null}.
292+
* @return new instance of {@link Bottom}.
293+
*/
294+
public Last input(String fieldName) {
295+
return new Last(append("input", Fields.field(fieldName)));
296+
}
297+
298+
/**
299+
* Define the expression building the value to serve as source.
300+
*
301+
* @param expression must not be {@literal null}.
302+
* @return new instance of {@link Bottom}.
303+
*/
304+
public Last input(AggregationExpression expression) {
305+
return new Last(append("input", expression));
306+
}
307+
308+
@Override
309+
protected String getMongoMethod() {
310+
return "$lastN";
311+
}
312+
}
219313
}

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

+2
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,8 @@ public class MethodReferenceNode extends ExpressionNode {
218218
.mappingParametersTo("n", "output", "sortBy"));
219219
map.put("firstN", mapArgRef().forOperator("$firstN") //
220220
.mappingParametersTo("n", "input"));
221+
map.put("lastN", mapArgRef().forOperator("$lastN") //
222+
.mappingParametersTo("n", "input"));
221223

222224
// CONVERT OPERATORS
223225
map.put("convert", mapArgRef().forOperator("$convert") //

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

+21
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,27 @@ void firstNMapsFieldNamesCorrectly() {
110110
"""));
111111
}
112112

113+
@Test // GH-4139
114+
void lastNMapsFieldNamesCorrectly() {
115+
116+
MongoMappingContext mappingContext = new MongoMappingContext();
117+
RelaxedTypeBasedAggregationOperationContext aggregationContext = new RelaxedTypeBasedAggregationOperationContext(
118+
Player.class, mappingContext,
119+
new QueryMapper(new MappingMongoConverter(NoOpDbRefResolver.INSTANCE, mappingContext)));
120+
121+
Document document = SelectionOperators.Last.last(3).of("score").toDocument(aggregationContext);
122+
123+
assertThat(document).isEqualTo(Document.parse("""
124+
{
125+
$lastN:
126+
{
127+
n: 3,
128+
input: "$s_cor_e"
129+
}
130+
}
131+
"""));
132+
}
133+
113134
static class Player {
114135

115136
@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
@@ -1189,6 +1189,11 @@ void shouldRenderFirstN() {
11891189
assertThat(transform("firstN(3, \"$score\")")).isEqualTo("{ $firstN : { n : 3, input : \"$score\" }}");
11901190
}
11911191

1192+
@Test // GH-4139
1193+
void shouldRenderLastN() {
1194+
assertThat(transform("lastN(3, \"$score\")")).isEqualTo("{ $lastN : { n : 3, input : \"$score\" }}");
1195+
}
1196+
11921197
private Document transform(String expression, Object... params) {
11931198
return (Document) transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
11941199
}

0 commit comments

Comments
 (0)