Add support for $maxN aggregation operator.
See #4139 Original pull request: #4182.
This commit is contained in:
committed by
Mark Paluch
parent
dd446472bc
commit
fb39c31986
@@ -19,6 +19,7 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -76,11 +77,11 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
|
|||||||
return context.getReference(field).toString();
|
return context.getReference(field).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(value instanceof Fields fields) {
|
if (value instanceof Fields fields) {
|
||||||
return fields.asList().stream().map(it -> unpack(it, context)).collect(Collectors.toList());
|
return fields.asList().stream().map(it -> unpack(it, context)).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
if(value instanceof Sort sort) {
|
if (value instanceof Sort sort) {
|
||||||
|
|
||||||
Document sortDoc = new Document();
|
Document sortDoc = new Document();
|
||||||
for (Order order : sort) {
|
for (Order order : sort) {
|
||||||
@@ -154,9 +155,40 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
|
|||||||
|
|
||||||
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map");
|
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map");
|
||||||
|
|
||||||
Map<String, Object> clone = new LinkedHashMap<>((java.util.Map) this.value);
|
return append((Map<String, Object>) this.value, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<String, Object> append(Map<String, Object> existing, String key, Object value) {
|
||||||
|
|
||||||
|
Map<String, Object> clone = new LinkedHashMap<>(existing);
|
||||||
clone.put(key, value);
|
clone.put(key, value);
|
||||||
return clone;
|
return clone;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Map<String, Object> appendTo(String key, Object value) {
|
||||||
|
|
||||||
|
Assert.isInstanceOf(Map.class, this.value, "Value must be a type of Map");
|
||||||
|
|
||||||
|
if (this.value instanceof Map map) {
|
||||||
|
|
||||||
|
Map<String, Object> target = new HashMap<>(map);
|
||||||
|
if (!target.containsKey(key)) {
|
||||||
|
target.put(key, value);
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
target.computeIfPresent(key, (k, v) -> {
|
||||||
|
|
||||||
|
if (v instanceof List<?> list) {
|
||||||
|
List<Object> targetList = new ArrayList<>(list);
|
||||||
|
targetList.add(value);
|
||||||
|
return targetList;
|
||||||
|
}
|
||||||
|
return Arrays.asList(v, value);
|
||||||
|
});
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(
|
||||||
|
String.format("Cannot append value to %s type", ObjectUtils.nullSafeClassName(this.value)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -247,6 +279,10 @@ abstract class AbstractAggregationExpression implements AggregationExpression {
|
|||||||
return (T) ((Map<String, Object>) this.value).get(key);
|
return (T) ((Map<String, Object>) this.value).get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected boolean isArgumentMap() {
|
||||||
|
return this.value instanceof Map;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the argument map.
|
* Get the argument map.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -112,6 +112,17 @@ public class AccumulatorOperators {
|
|||||||
return usesFieldRef() ? Max.maxOf(fieldReference) : Max.maxOf(expression);
|
return usesFieldRef() ? Max.maxOf(fieldReference) : Max.maxOf(expression);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and returns the
|
||||||
|
* requested number of maximum values.
|
||||||
|
*
|
||||||
|
* @return new instance of {@link Max}.
|
||||||
|
* @since 4.0
|
||||||
|
*/
|
||||||
|
public Max max(int numberOfResults) {
|
||||||
|
return max().limit(numberOfResults);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and returns the
|
* Creates new {@link AggregationExpression} that takes the associated numeric value expression and returns the
|
||||||
* minimum value.
|
* minimum value.
|
||||||
@@ -441,7 +452,7 @@ public class AccumulatorOperators {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getMongoMethod() {
|
protected String getMongoMethod() {
|
||||||
return "$max";
|
return contains("n") ? "$maxN" : "$max";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -453,7 +464,7 @@ public class AccumulatorOperators {
|
|||||||
public static Max maxOf(String fieldReference) {
|
public static Max maxOf(String fieldReference) {
|
||||||
|
|
||||||
Assert.notNull(fieldReference, "FieldReference must not be null");
|
Assert.notNull(fieldReference, "FieldReference must not be null");
|
||||||
return new Max(asFields(fieldReference));
|
return new Max(Collections.singletonMap("input", Fields.field(fieldReference)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -465,7 +476,7 @@ public class AccumulatorOperators {
|
|||||||
public static Max maxOf(AggregationExpression expression) {
|
public static Max maxOf(AggregationExpression expression) {
|
||||||
|
|
||||||
Assert.notNull(expression, "Expression must not be null");
|
Assert.notNull(expression, "Expression must not be null");
|
||||||
return new Max(Collections.singletonList(expression));
|
return new Max(Collections.singletonMap("input", expression));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -478,7 +489,7 @@ public class AccumulatorOperators {
|
|||||||
public Max and(String fieldReference) {
|
public Max and(String fieldReference) {
|
||||||
|
|
||||||
Assert.notNull(fieldReference, "FieldReference must not be null");
|
Assert.notNull(fieldReference, "FieldReference must not be null");
|
||||||
return new Max(append(Fields.field(fieldReference)));
|
return new Max(appendTo("input", Fields.field(fieldReference)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -491,7 +502,26 @@ public class AccumulatorOperators {
|
|||||||
public Max and(AggregationExpression expression) {
|
public Max and(AggregationExpression expression) {
|
||||||
|
|
||||||
Assert.notNull(expression, "Expression must not be null");
|
Assert.notNull(expression, "Expression must not be null");
|
||||||
return new Max(append(expression));
|
return new Max(appendTo("input", expression));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new {@link Max} that returns the given number of maxmimum values ({@literal $maxN}).
|
||||||
|
* <strong>NOTE</strong>: Cannot be used with more than one {@literal input} value.
|
||||||
|
*
|
||||||
|
* @param numberOfResults
|
||||||
|
* @return new instance of {@link Max}.
|
||||||
|
*/
|
||||||
|
public Max limit(int numberOfResults) {
|
||||||
|
return new Max(append("n", numberOfResults));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Document toDocument(AggregationOperationContext context) {
|
||||||
|
if (get("n") == null) {
|
||||||
|
return toDocument(get("input"), context);
|
||||||
|
}
|
||||||
|
return super.toDocument(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -224,6 +224,8 @@ public class MethodReferenceNode extends ExpressionNode {
|
|||||||
.mappingParametersTo("output", "sortBy"));
|
.mappingParametersTo("output", "sortBy"));
|
||||||
map.put("topN", mapArgRef().forOperator("$topN") //
|
map.put("topN", mapArgRef().forOperator("$topN") //
|
||||||
.mappingParametersTo("n", "output", "sortBy"));
|
.mappingParametersTo("n", "output", "sortBy"));
|
||||||
|
map.put("maxN", mapArgRef().forOperator("$maxN") //
|
||||||
|
.mappingParametersTo("n", "input"));
|
||||||
|
|
||||||
// CONVERT OPERATORS
|
// CONVERT OPERATORS
|
||||||
map.put("convert", mapArgRef().forOperator("$convert") //
|
map.put("convert", mapArgRef().forOperator("$convert") //
|
||||||
|
|||||||
@@ -80,6 +80,20 @@ class AccumulatorOperatorsUnitTests {
|
|||||||
.isEqualTo(Document.parse("{ $expMovingAvg: { input: \"$price\", alpha: 0.75 } }"));
|
.isEqualTo(Document.parse("{ $expMovingAvg: { input: \"$price\", alpha: 0.75 } }"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // GH-4139
|
||||||
|
void rendersMax() {
|
||||||
|
|
||||||
|
assertThat(valueOf("price").max().toDocument(Aggregation.DEFAULT_CONTEXT))
|
||||||
|
.isEqualTo(Document.parse("{ $max: \"$price\" }"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test // GH-4139
|
||||||
|
void rendersMaxN() {
|
||||||
|
|
||||||
|
assertThat(valueOf("price").max(3).toDocument(Aggregation.DEFAULT_CONTEXT))
|
||||||
|
.isEqualTo(Document.parse("{ $maxN: { n: 3, input : \"$price\" } }"));
|
||||||
|
}
|
||||||
|
|
||||||
static class Jedi {
|
static class Jedi {
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
|
|||||||
@@ -1204,6 +1204,11 @@ public class SpelExpressionTransformerUnitTests {
|
|||||||
assertThat(transform("lastN(3, \"$score\")")).isEqualTo("{ $lastN : { n : 3, input : \"$score\" }}");
|
assertThat(transform("lastN(3, \"$score\")")).isEqualTo("{ $lastN : { n : 3, input : \"$score\" }}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test // GH-4139
|
||||||
|
void shouldRenderMaxN() {
|
||||||
|
assertThat(transform("maxN(3, \"$score\")")).isEqualTo("{ $maxN : { 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);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user