Skip to content

Commit 30da621

Browse files
christophstroblmp911de
authored andcommitted
Add support for $rank and $denseRank aggregation operators.
Closes: #3715 Original pull request: #3741.
1 parent a977b8a commit 30da621

File tree

5 files changed

+144
-2
lines changed

5 files changed

+144
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.aggregation;
17+
18+
import org.bson.Document;
19+
20+
/**
21+
* @author Christoph Strobl
22+
* @since 3.3
23+
*/
24+
public class DocumentOperators {
25+
26+
/**
27+
* Obtain the document position (including gaps) relative to others (rank).
28+
*
29+
* @return new instance of {@link Rank}.
30+
* @since 3.3
31+
*/
32+
public static Rank rank() {
33+
return new Rank();
34+
}
35+
36+
/**
37+
* Obtain the document position (without gaps) relative to others (rank).
38+
*
39+
* @return new instance of {@link DenseRank}.
40+
* @since 3.3
41+
*/
42+
public static DenseRank denseRank() {
43+
return new DenseRank();
44+
}
45+
46+
/**
47+
* {@link Rank} resolves the current document position (the rank) relative to other documents. If multiple documents
48+
* occupy the same rank, {@literal $rank} places the document with the subsequent value at a rank with a gap.
49+
*
50+
* @author Christoph Strobl
51+
* @since 3.3
52+
*/
53+
public static class Rank implements AggregationExpression {
54+
55+
@Override
56+
public Document toDocument(AggregationOperationContext context) {
57+
return new Document("$rank", new Document());
58+
}
59+
}
60+
61+
/**
62+
* {@link DenseRank} resolves the current document position (the rank) relative to other documents. If multiple
63+
* documents occupy the same rank, {@literal $denseRank} places the document with the subsequent value at the next rank without
64+
* any gaps.
65+
*
66+
* @author Christoph Strobl
67+
* @since 3.3
68+
*/
69+
public static class DenseRank implements AggregationExpression {
70+
71+
@Override
72+
public Document toDocument(AggregationOperationContext context) {
73+
return new Document("$denseRank", new Document());
74+
}
75+
}
76+
}

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -500,7 +500,10 @@ protected Object convert(AggregationExpressionTransformationContext<MethodRefere
500500
dbo.put(methodReference.getArgumentMap()[i++], transform(child, context));
501501
}
502502
args = dbo;
503-
} else {
503+
} else if (ObjectUtils.nullSafeEquals(methodReference.getArgumentType(), ArgumentType.EMPTY_DOCUMENT)) {
504+
args = new Document();
505+
}
506+
else {
504507

505508
List<Object> argList = new ArrayList<Object>();
506509

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

+15-1
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ public class MethodReferenceNode extends ExpressionNode {
6868
map.put("lte", arrayArgRef().forOperator("$lte"));
6969
map.put("ne", arrayArgRef().forOperator("$ne"));
7070

71+
// DOCUMENT OPERATORS
72+
map.put("rank", emptyRef().forOperator("$rank"));
73+
map.put("denseRank", emptyRef().forOperator("$denseRank"));
74+
7175
// ARITHMETIC OPERATORS
7276
map.put("abs", singleArgRef().forOperator("$abs"));
7377
map.put("add", arrayArgRef().forOperator("$add"));
@@ -307,6 +311,16 @@ static AggregationMethodReference mapArgRef() {
307311
return new AggregationMethodReference(null, ArgumentType.MAP, null);
308312
}
309313

314+
/**
315+
* Create a new {@link AggregationMethodReference} for a {@link ArgumentType#EMPTY_DOCUMENT} argument.
316+
*
317+
* @return never {@literal null}.
318+
* @since 3.3
319+
*/
320+
static AggregationMethodReference emptyRef() {
321+
return new AggregationMethodReference(null, ArgumentType.EMPTY_DOCUMENT, null);
322+
}
323+
310324
/**
311325
* Create a new {@link AggregationMethodReference} for a given {@literal aggregationExpressionOperator} reusing
312326
* previously set arguments.
@@ -342,7 +356,7 @@ AggregationMethodReference mappingParametersTo(String... aggregationExpressionPr
342356
* @since 1.10
343357
*/
344358
public enum ArgumentType {
345-
SINGLE, ARRAY, MAP
359+
SINGLE, ARRAY, MAP, EMPTY_DOCUMENT
346360
}
347361
}
348362

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.mongodb.core.aggregation;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
import static org.springframework.data.mongodb.core.aggregation.DocumentOperators.*;
20+
21+
import org.bson.Document;
22+
import org.junit.jupiter.api.Test;
23+
24+
/**
25+
* @author Christoph Strobl
26+
*/
27+
class DocumentOperatorsUnitTests {
28+
29+
@Test // GH-3715
30+
void rendersRank() {
31+
assertThat(rank().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(new Document("$rank", new Document()));
32+
}
33+
34+
@Test // GH-3715
35+
void rendersDenseRank() {
36+
assertThat(denseRank().toDocument(Aggregation.DEFAULT_CONTEXT))
37+
.isEqualTo(new Document("$denseRank", new Document()));
38+
}
39+
}

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

+10
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,16 @@ void shouldRenderCovarianceSamp() {
956956
assertThat(transform("covarianceSamp(field1, field2)")).isEqualTo(Document.parse("{ \"$covarianceSamp\" : [\"$field1\", \"$field2\"]}"));
957957
}
958958

959+
@Test // GH-3715
960+
void shouldRenderRank() {
961+
assertThat(transform("rank()")).isEqualTo(Document.parse("{ $rank : {} }"));
962+
}
963+
964+
@Test // GH-3715
965+
void shouldRenderDenseRank() {
966+
assertThat(transform("denseRank()")).isEqualTo(Document.parse("{ $denseRank : {} }"));
967+
}
968+
959969
private Object transform(String expression, Object... params) {
960970
Object result = transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
961971
return result == null ? null : (!(result instanceof org.bson.Document) ? result.toString() : result);

0 commit comments

Comments
 (0)