Skip to content

Commit 4099900

Browse files
committed
Implement array expressions
1 parent 146a6be commit 4099900

File tree

6 files changed

+371
-31
lines changed

6 files changed

+371
-31
lines changed

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.function.BinaryOperator;
2020
import java.util.function.Function;
2121

22+
import static com.mongodb.client.model.expressions.Expressions.of;
23+
2224
/**
2325
* Expresses an array value. An array value is a finite, ordered collection of
2426
* elements of a certain type.
@@ -61,4 +63,29 @@ public interface ArrayExpression<T extends Expression> extends Expression {
6163
*/
6264
T reduce(T initialValue, BinaryOperator<T> in);
6365

66+
IntegerExpression size();
67+
68+
T arrayElemAt(IntegerExpression i);
69+
70+
default T arrayElemAt(final int i) {
71+
return this.arrayElemAt(of(i));
72+
}
73+
74+
T first();
75+
76+
T last();
77+
78+
BooleanExpression contains(T contains);
79+
80+
ArrayExpression<T> concatArrays(ArrayExpression<T> array);
81+
82+
ArrayExpression<T> slice(IntegerExpression start, IntegerExpression length);
83+
84+
default ArrayExpression<T> slice(final int start, final int length) {
85+
return this.slice(of(start), of(length));
86+
}
87+
88+
ArrayExpression<T> setUnion(ArrayExpression<T> set);
89+
90+
ArrayExpression<T> distinct();
6491
}

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

Lines changed: 82 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,21 @@ public static BooleanExpression of(final boolean of) {
6060
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonBoolean(of)));
6161
}
6262

63+
/**
64+
* Returns an array expression containing the same boolean values as the
65+
* provided array of booleans.
66+
*
67+
* @param array the array of booleans
68+
* @return the boolean array expression
69+
*/
70+
public static ArrayExpression<BooleanExpression> ofBooleanArray(final boolean... array) {
71+
List<BsonValue> result = new ArrayList<>();
72+
for (boolean b : array) {
73+
result.add(new BsonBoolean(b));
74+
}
75+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result)));
76+
}
77+
6378
/**
6479
* Returns an expression having the same integer value as the provided
6580
* int primitive.
@@ -70,25 +85,78 @@ public static BooleanExpression of(final boolean of) {
7085
public static IntegerExpression of(final int of) {
7186
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonInt32(of)));
7287
}
88+
89+
public static ArrayExpression<IntegerExpression> ofIntegerArray(final int... array) {
90+
List<BsonValue> list = Arrays.stream(array)
91+
.mapToObj(BsonInt32::new)
92+
.collect(Collectors.toList());
93+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list)));
94+
}
95+
7396
public static IntegerExpression of(final long of) {
7497
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonInt64(of)));
7598
}
99+
100+
public static ArrayExpression<IntegerExpression> ofIntegerArray(final long... array) {
101+
List<BsonValue> list = Arrays.stream(array)
102+
.mapToObj(BsonInt64::new)
103+
.collect(Collectors.toList());
104+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list)));
105+
}
106+
76107
public static NumberExpression of(final double of) {
77108
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDouble(of)));
78109
}
110+
111+
public static ArrayExpression<IntegerExpression> ofNumberArray(final double... array) {
112+
List<BsonValue> list = Arrays.stream(array)
113+
.mapToObj(BsonDouble::new)
114+
.collect(Collectors.toList());
115+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list)));
116+
}
117+
79118
public static NumberExpression of(@NonNull final BigDecimal of) {
80119
Assertions.notNull("BigDecimal", of);
81120
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDecimal128(new Decimal128(of))));
82121
}
122+
123+
public static ArrayExpression<IntegerExpression> ofNumberArray(final BigDecimal... array) {
124+
List<BsonValue> result = new ArrayList<>();
125+
for (BigDecimal e : array) {
126+
Assertions.notNull("elements of array", e);
127+
result.add(new BsonDecimal128(new Decimal128(e)));
128+
}
129+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result)));
130+
}
131+
83132
public static NumberExpression of(final Decimal128 of) {
84133
Assertions.notNull("Decimal128", of);
85134
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDecimal128(of)));
86135
}
136+
137+
public static ArrayExpression<IntegerExpression> ofNumberArray(final Decimal128... array) {
138+
List<BsonValue> result = new ArrayList<>();
139+
for (Decimal128 e : array) {
140+
Assertions.notNull("elements of array", e);
141+
result.add(new BsonDecimal128(e));
142+
}
143+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result)));
144+
}
145+
87146
public static DateExpression of(@NonNull final Instant of) {
88147
Assertions.notNull("Instant", of);
89148
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDateTime(of.toEpochMilli())));
90149
}
91150

151+
public static ArrayExpression<DateExpression> ofDateArray(final Instant... array) {
152+
List<BsonValue> result = new ArrayList<>();
153+
for (Instant e : array) {
154+
Assertions.notNull("elements of array", e);
155+
result.add(new BsonDateTime(e.toEpochMilli()));
156+
}
157+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result)));
158+
}
159+
92160
/**
93161
* Returns an expression having the same string value as the provided
94162
* string.
@@ -101,27 +169,25 @@ public static StringExpression of(@NonNull final String of) {
101169
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonString(of)));
102170
}
103171

104-
/**
105-
* Returns an array expression containing the same boolean values as the
106-
* provided array of booleans.
107-
*
108-
* @param array the array of booleans
109-
* @return the boolean array expression
110-
*/
111-
public static ArrayExpression<BooleanExpression> ofBooleanArray(final boolean... array) {
172+
173+
public static ArrayExpression<StringExpression> ofStringArray(final String... array) {
112174
List<BsonValue> result = new ArrayList<>();
113-
for (boolean b : array) {
114-
result.add(new BsonBoolean(b));
175+
for (String e : array) {
176+
Assertions.notNull("elements of array", e);
177+
result.add(new BsonString(e));
115178
}
116179
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result)));
117180
}
118181

119-
120-
public static ArrayExpression<IntegerExpression> ofIntegerArray(final int... ofIntegerArray) {
121-
List<BsonValue> array = Arrays.stream(ofIntegerArray)
122-
.mapToObj(BsonInt32::new)
123-
.collect(Collectors.toList());
124-
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(array)));
182+
@SafeVarargs // nothing is stored in the array
183+
public static <T extends Expression> ArrayExpression<T> ofArray(final T... array) {
184+
Assertions.notNull("array", array);
185+
return new MqlExpression<>((cr) -> {
186+
List<BsonValue> array2 = Arrays.stream(array)
187+
.map(v -> ((MqlExpression<?>) v).toBsonValue(cr))
188+
.collect(Collectors.toList());
189+
return new AstPlaceholder(new BsonArray(array2));
190+
});
125191
}
126192

127193
public static DocumentExpression ofDocument(final Bson document) {

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

Lines changed: 73 additions & 3 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.Collections;
2526
import java.util.function.BinaryOperator;
2627
import java.util.function.Function;
2728

@@ -49,7 +50,7 @@ private AstPlaceholder astDoc(final String name, final BsonDocument value) {
4950
}
5051

5152
static class AstPlaceholder {
52-
private BsonValue bsonValue;
53+
private final BsonValue bsonValue;
5354

5455
AstPlaceholder(final BsonValue bsonValue) {
5556
this.bsonValue = bsonValue;
@@ -173,7 +174,7 @@ public <R extends Expression> ArrayExpression<R> map(final Function<? super T, ?
173174
@Override
174175
public ArrayExpression<T> filter(final Function<? super T, ? extends BooleanExpression> cond) {
175176
T varThis = variable("$$this");
176-
return new MqlExpression<T>((cr) -> astDoc("$filter", new BsonDocument()
177+
return new MqlExpression<>((cr) -> astDoc("$filter", new BsonDocument()
177178
.append("input", this.toBsonValue(cr))
178179
.append("cond", extractBsonValue(cr, cond.apply(varThis)))));
179180
}
@@ -185,7 +186,76 @@ public T reduce(final T initialValue, final BinaryOperator<T> in) {
185186
return newMqlExpression((cr) -> astDoc("$reduce", new BsonDocument()
186187
.append("input", this.toBsonValue(cr))
187188
.append("initialValue", extractBsonValue(cr, initialValue))
188-
.append("in", extractBsonValue(cr, in.apply(varThis, varValue)))));
189+
.append("in", extractBsonValue(cr, in.apply(varValue, varThis)))));
190+
}
191+
192+
@Override
193+
public IntegerExpression size() {
194+
return new MqlExpression<>(
195+
(cr) -> new AstPlaceholder(new BsonDocument("$size",
196+
// must wrap the first argument in a list
197+
new BsonArray(Collections.singletonList(this.toBsonValue(cr))))));
198+
}
199+
200+
@Override
201+
public T arrayElemAt(final IntegerExpression at) {
202+
return new MqlExpression<>(ast("$arrayElemAt", at))
203+
.assertImplementsAllExpressions();
204+
}
205+
206+
@Override
207+
public T first() {
208+
return new MqlExpression<>(
209+
(cr) -> new AstPlaceholder(new BsonDocument("$first",
210+
// must wrap the first argument in a list
211+
new BsonArray(Collections.singletonList(this.toBsonValue(cr))))))
212+
.assertImplementsAllExpressions();
213+
}
214+
215+
@Override
216+
public T last() {
217+
return new MqlExpression<>(
218+
(cr) -> new AstPlaceholder(new BsonDocument("$last",
219+
// must wrap the first argument in a list
220+
new BsonArray(Collections.singletonList(this.toBsonValue(cr))))))
221+
.assertImplementsAllExpressions();
222+
}
223+
224+
@Override
225+
public BooleanExpression contains(final T item) {
226+
String name = "$in";
227+
return new MqlExpression<>((cr) -> {
228+
BsonArray value = new BsonArray();
229+
value.add(extractBsonValue(cr, item));
230+
value.add(this.toBsonValue(cr));
231+
return new AstPlaceholder(new BsonDocument(name, value));
232+
}).assertImplementsAllExpressions();
233+
}
234+
235+
@Override
236+
public ArrayExpression<T> concatArrays(final ArrayExpression<T> array) {
237+
return new MqlExpression<>(ast("$concatArrays", array))
238+
.assertImplementsAllExpressions();
239+
}
240+
241+
@Override
242+
public ArrayExpression<T> slice(final IntegerExpression start, final IntegerExpression length) {
243+
return new MqlExpression<>(ast("$slice", start, length))
244+
.assertImplementsAllExpressions();
245+
}
246+
247+
@Override
248+
public ArrayExpression<T> setUnion(final ArrayExpression<T> set) {
249+
return new MqlExpression<>(ast("$setUnion", set))
250+
.assertImplementsAllExpressions();
251+
}
252+
253+
@Override
254+
public ArrayExpression<T> distinct() {
255+
return new MqlExpression<>(
256+
(cr) -> new AstPlaceholder(new BsonDocument("$setUnion",
257+
// must wrap the first argument in a list
258+
new BsonArray(Collections.singletonList(this.toBsonValue(cr))))));
189259
}
190260

191261

driver-core/src/test/functional/com/mongodb/client/model/expressions/AbstractExpressionsFunctionalTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,5 +113,12 @@ public BsonValue readValue(final BsonReader reader, final DecoderContext decoder
113113
return super.readValue(reader, decoderContext);
114114
}
115115
}
116+
117+
118+
static <R extends Expression> R ofRem() {
119+
// $$REMOVE is intentionally not exposed to users
120+
return new MqlExpression<>((cr) -> new MqlExpression.AstPlaceholder(new BsonString("$$REMOVE")))
121+
.assertImplementsAllExpressions();
122+
}
116123
}
117124

0 commit comments

Comments
 (0)