Browse Source

Add support for `$rank` and `$denseRank` aggregation operators.

Closes: #3715
Original pull request: #3741.
pull/3765/head
Christoph Strobl 5 years ago committed by Mark Paluch
parent
commit
30da62181f
No known key found for this signature in database
GPG Key ID: 4406B84C1661DCD1
  1. 76
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/DocumentOperators.java
  2. 5
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformer.java
  3. 16
      spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/spel/MethodReferenceNode.java
  4. 39
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/DocumentOperatorsUnitTests.java
  5. 10
      spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/aggregation/SpelExpressionTransformerUnitTests.java

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

@ -0,0 +1,76 @@ @@ -0,0 +1,76 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import org.bson.Document;
/**
* @author Christoph Strobl
* @since 3.3
*/
public class DocumentOperators {
/**
* Obtain the document position (including gaps) relative to others (rank).
*
* @return new instance of {@link Rank}.
* @since 3.3
*/
public static Rank rank() {
return new Rank();
}
/**
* Obtain the document position (without gaps) relative to others (rank).
*
* @return new instance of {@link DenseRank}.
* @since 3.3
*/
public static DenseRank denseRank() {
return new DenseRank();
}
/**
* {@link Rank} resolves the current document position (the rank) relative to other documents. If multiple documents
* occupy the same rank, {@literal $rank} places the document with the subsequent value at a rank with a gap.
*
* @author Christoph Strobl
* @since 3.3
*/
public static class Rank implements AggregationExpression {
@Override
public Document toDocument(AggregationOperationContext context) {
return new Document("$rank", new Document());
}
}
/**
* {@link DenseRank} resolves the current document position (the rank) relative to other documents. If multiple
* documents occupy the same rank, {@literal $denseRank} places the document with the subsequent value at the next rank without
* any gaps.
*
* @author Christoph Strobl
* @since 3.3
*/
public static class DenseRank implements AggregationExpression {
@Override
public Document toDocument(AggregationOperationContext context) {
return new Document("$denseRank", new Document());
}
}
}

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

@ -500,7 +500,10 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer { @@ -500,7 +500,10 @@ class SpelExpressionTransformer implements AggregationExpressionTransformer {
dbo.put(methodReference.getArgumentMap()[i++], transform(child, context));
}
args = dbo;
} else {
} else if (ObjectUtils.nullSafeEquals(methodReference.getArgumentType(), ArgumentType.EMPTY_DOCUMENT)) {
args = new Document();
}
else {
List<Object> argList = new ArrayList<Object>();

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

@ -68,6 +68,10 @@ public class MethodReferenceNode extends ExpressionNode { @@ -68,6 +68,10 @@ public class MethodReferenceNode extends ExpressionNode {
map.put("lte", arrayArgRef().forOperator("$lte"));
map.put("ne", arrayArgRef().forOperator("$ne"));
// DOCUMENT OPERATORS
map.put("rank", emptyRef().forOperator("$rank"));
map.put("denseRank", emptyRef().forOperator("$denseRank"));
// ARITHMETIC OPERATORS
map.put("abs", singleArgRef().forOperator("$abs"));
map.put("add", arrayArgRef().forOperator("$add"));
@ -307,6 +311,16 @@ public class MethodReferenceNode extends ExpressionNode { @@ -307,6 +311,16 @@ public class MethodReferenceNode extends ExpressionNode {
return new AggregationMethodReference(null, ArgumentType.MAP, null);
}
/**
* Create a new {@link AggregationMethodReference} for a {@link ArgumentType#EMPTY_DOCUMENT} argument.
*
* @return never {@literal null}.
* @since 3.3
*/
static AggregationMethodReference emptyRef() {
return new AggregationMethodReference(null, ArgumentType.EMPTY_DOCUMENT, null);
}
/**
* Create a new {@link AggregationMethodReference} for a given {@literal aggregationExpressionOperator} reusing
* previously set arguments.
@ -342,7 +356,7 @@ public class MethodReferenceNode extends ExpressionNode { @@ -342,7 +356,7 @@ public class MethodReferenceNode extends ExpressionNode {
* @since 1.10
*/
public enum ArgumentType {
SINGLE, ARRAY, MAP
SINGLE, ARRAY, MAP, EMPTY_DOCUMENT
}
}

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

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
/*
* Copyright 2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.mongodb.core.aggregation;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.mongodb.core.aggregation.DocumentOperators.*;
import org.bson.Document;
import org.junit.jupiter.api.Test;
/**
* @author Christoph Strobl
*/
class DocumentOperatorsUnitTests {
@Test // GH-3715
void rendersRank() {
assertThat(rank().toDocument(Aggregation.DEFAULT_CONTEXT)).isEqualTo(new Document("$rank", new Document()));
}
@Test // GH-3715
void rendersDenseRank() {
assertThat(denseRank().toDocument(Aggregation.DEFAULT_CONTEXT))
.isEqualTo(new Document("$denseRank", new Document()));
}
}

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

@ -956,6 +956,16 @@ public class SpelExpressionTransformerUnitTests { @@ -956,6 +956,16 @@ public class SpelExpressionTransformerUnitTests {
assertThat(transform("covarianceSamp(field1, field2)")).isEqualTo(Document.parse("{ \"$covarianceSamp\" : [\"$field1\", \"$field2\"]}"));
}
@Test // GH-3715
void shouldRenderRank() {
assertThat(transform("rank()")).isEqualTo(Document.parse("{ $rank : {} }"));
}
@Test // GH-3715
void shouldRenderDenseRank() {
assertThat(transform("denseRank()")).isEqualTo(Document.parse("{ $denseRank : {} }"));
}
private Object transform(String expression, Object... params) {
Object result = transformer.transform(expression, Aggregation.DEFAULT_CONTEXT, params);
return result == null ? null : (!(result instanceof org.bson.Document) ? result.toString() : result);

Loading…
Cancel
Save