Skip to content

Commit 2c83ce7

Browse files
authored
Implement filter, map, reduce (#1031)
JAVA-4781
1 parent a8364dd commit 2c83ce7

File tree

4 files changed

+179
-0
lines changed

4 files changed

+179
-0
lines changed

driver-core/src/main/com/mongodb/client/model/expressions/ArrayExpression.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package com.mongodb.client.model.expressions;
1818

19+
import java.util.function.BinaryOperator;
20+
import java.util.function.Function;
21+
1922
/**
2023
* Expresses an array value. An array value is a finite, ordered collection of
2124
* elements of a certain type.
@@ -24,4 +27,38 @@
2427
*/
2528
public interface ArrayExpression<T extends Expression> extends Expression {
2629

30+
/**
31+
* Returns an array consisting of those elements in this array that match
32+
* the given predicate condition. Evaluates each expression in this array
33+
* according to the cond function. If cond evaluates to logical true, then
34+
* the element is preserved; if cond evaluates to logical false, the element
35+
* is omitted.
36+
*
37+
* @param cond the function to apply to each element
38+
* @return the new array
39+
*/
40+
ArrayExpression<T> filter(Function<? super T, ? extends BooleanExpression> cond);
41+
42+
/**
43+
* Returns an array consisting of the results of applying the given function
44+
* to the elements of this array.
45+
*
46+
* @param in the function to apply to each element
47+
* @return the new array
48+
* @param <R> the type contained in the resulting array
49+
*/
50+
<R extends Expression> ArrayExpression<R> map(Function<? super T, ? extends R> in);
51+
52+
/**
53+
* Performs a reduction on the elements of this array, using the provided
54+
* identity value and an associative reducing function, and returns
55+
* the reduced value. The initial value must be the identity value for the
56+
* reducing function.
57+
*
58+
* @param initialValue the identity for the reducing function
59+
* @param in the associative reducing function
60+
* @return the reduced value
61+
*/
62+
T reduce(T initialValue, BinaryOperator<T> in);
63+
2764
}

driver-core/src/main/com/mongodb/client/model/expressions/Expressions.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,14 @@
1616

1717
package com.mongodb.client.model.expressions;
1818

19+
import org.bson.BsonArray;
1920
import org.bson.BsonBoolean;
2021
import org.bson.BsonInt32;
2122
import org.bson.BsonString;
23+
import org.bson.BsonValue;
24+
25+
import java.util.ArrayList;
26+
import java.util.List;
2227

2328
/**
2429
* Convenience methods related to {@link Expression}.
@@ -60,4 +65,20 @@ public static IntegerExpression of(final int of) {
6065
public static StringExpression of(final String of) {
6166
return new MqlExpression<>((codecRegistry) -> new BsonString(of));
6267
}
68+
69+
/**
70+
* Returns an array expression containing the same boolean values as the
71+
* provided array of booleans.
72+
*
73+
* @param array the array of booleans
74+
* @return the boolean array expression
75+
*/
76+
public static ArrayExpression<BooleanExpression> ofBooleanArray(final boolean... array) {
77+
List<BsonValue> result = new ArrayList<>();
78+
for (boolean b : array) {
79+
result.add(new BsonBoolean(b));
80+
}
81+
return new MqlExpression<>((cr) -> new BsonArray(result));
82+
}
83+
6384
}

driver-core/src/main/com/mongodb/client/model/expressions/MqlExpression.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.bson.BsonValue;
2323
import org.bson.codecs.configuration.CodecRegistry;
2424

25+
import java.util.function.BinaryOperator;
2526
import java.util.function.Function;
2627

2728
final class MqlExpression<T extends Expression>
@@ -118,4 +119,33 @@ public <R extends Expression> R cond(final R left, final R right) {
118119
return newMqlExpression(ast("$cond", left, right));
119120
}
120121

122+
123+
/** @see ArrayExpression */
124+
125+
@Override
126+
public <R extends Expression> ArrayExpression<R> map(final Function<? super T, ? extends R> in) {
127+
T varThis = variable("$$this");
128+
return new MqlExpression<>((cr) -> astDoc("$map", new BsonDocument()
129+
.append("input", this.toBsonValue(cr))
130+
.append("in", extractBsonValue(cr, in.apply(varThis)))).apply(cr));
131+
}
132+
133+
@Override
134+
public ArrayExpression<T> filter(final Function<? super T, ? extends BooleanExpression> cond) {
135+
T varThis = variable("$$this");
136+
return new MqlExpression<T>((cr) -> astDoc("$filter", new BsonDocument()
137+
.append("input", this.toBsonValue(cr))
138+
.append("cond", extractBsonValue(cr, cond.apply(varThis)))).apply(cr));
139+
}
140+
141+
@Override
142+
public T reduce(final T initialValue, final BinaryOperator<T> in) {
143+
T varThis = variable("$$this");
144+
T varValue = variable("$$value");
145+
return newMqlExpression((cr) -> astDoc("$reduce", new BsonDocument()
146+
.append("input", this.toBsonValue(cr))
147+
.append("initialValue", extractBsonValue(cr, initialValue))
148+
.append("in", extractBsonValue(cr, in.apply(varThis, varValue)))).apply(cr));
149+
}
150+
121151
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright 2008-present MongoDB, Inc.
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+
* http://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+
17+
package com.mongodb.client.model.expressions;
18+
19+
import org.junit.jupiter.api.Test;
20+
21+
import java.util.Arrays;
22+
import java.util.stream.Collectors;
23+
import java.util.stream.Stream;
24+
25+
import static com.mongodb.client.model.expressions.Expressions.of;
26+
import static com.mongodb.client.model.expressions.Expressions.ofBooleanArray;
27+
28+
@SuppressWarnings({"PointlessBooleanExpression", "ConstantConditions", "Convert2MethodRef"})
29+
class ArrayExpressionsFunctionalTest extends AbstractExpressionsFunctionalTest {
30+
// https://www.mongodb.com/docs/manual/reference/operator/aggregation/#array-expression-operators
31+
// (Incomplete)
32+
33+
private final ArrayExpression<BooleanExpression> arrayTTF = ofBooleanArray(true, true, false);
34+
35+
@Test
36+
public void literalsTest() {
37+
assertExpression(Arrays.asList(true, true, false), arrayTTF, "[true, true, false]");
38+
}
39+
40+
@Test
41+
public void filterTest() {
42+
// https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/
43+
assertExpression(
44+
Stream.of(true, true, false)
45+
.filter(v -> v).collect(Collectors.toList()),
46+
arrayTTF.filter(v -> v),
47+
// MQL:
48+
"{'$filter': {'input': [true, true, false], 'cond': '$$this'}}");
49+
}
50+
51+
@Test
52+
public void mapTest() {
53+
// https://www.mongodb.com/docs/manual/reference/operator/aggregation/map/
54+
assertExpression(
55+
Stream.of(true, true, false)
56+
.map(v -> !v).collect(Collectors.toList()),
57+
arrayTTF.map(v -> v.not()),
58+
// MQL:
59+
"{'$map': {'input': [true, true, false], 'in': {'$not': '$$this'}}}");
60+
}
61+
62+
@Test
63+
public void reduceTest() {
64+
// https://www.mongodb.com/docs/manual/reference/operator/aggregation/reduce/
65+
assertExpression(
66+
Stream.of(true, true, false)
67+
.reduce(false, (a, b) -> a || b),
68+
arrayTTF.reduce(of(false), (a, b) -> a.or(b)),
69+
// MQL:
70+
"{'$reduce': {'input': [true, true, false], 'initialValue': false, 'in': {'$or': ['$$this', '$$value']}}}");
71+
assertExpression(
72+
Stream.of(true, true, false)
73+
.reduce(true, (a, b) -> a && b),
74+
arrayTTF.reduce(of(true), (a, b) -> a.and(b)),
75+
// MQL:
76+
"{'$reduce': {'input': [true, true, false], 'initialValue': true, 'in': {'$and': ['$$this', '$$value']}}}");
77+
// empty array
78+
assertExpression(
79+
Stream.<Boolean>empty().reduce(true, (a, b) -> a && b),
80+
ofBooleanArray().reduce(of(true), (a, b) -> a.and(b)),
81+
// MQL:
82+
"{'$reduce': {'input': [], 'initialValue': true, 'in': {'$and': ['$$this', '$$value']}}}");
83+
// constant result
84+
assertExpression(
85+
Stream.of(true, true, false)
86+
.reduce(true, (a, b) -> true),
87+
arrayTTF.reduce(of(true), (a, b) -> of(true)),
88+
// MQL:
89+
"{'$reduce': {'input': [true, true, false], 'initialValue': true, 'in': true}}");
90+
}
91+
}

0 commit comments

Comments
 (0)