Skip to content

Commit 7baa36c

Browse files
authored
Implement array expressions (#1043)
JAVA-4805
1 parent 091ce50 commit 7baa36c

File tree

6 files changed

+379
-29
lines changed

6 files changed

+379
-29
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 elementAt(IntegerExpression i);
69+
70+
default T elementAt(final int i) {
71+
return this.elementAt(of(i));
72+
}
73+
74+
T first();
75+
76+
T last();
77+
78+
BooleanExpression contains(T contains);
79+
80+
ArrayExpression<T> concat(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> union(ArrayExpression<T> set);
89+
90+
ArrayExpression<T> distinct();
6491
}

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

Lines changed: 85 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,22 @@ public static BooleanExpression of(final boolean of) {
5858
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonBoolean(of)));
5959
}
6060

61+
/**
62+
* Returns an array expression containing the same boolean values as the
63+
* provided array of booleans.
64+
*
65+
* @param array the array of booleans
66+
* @return the boolean array expression
67+
*/
68+
public static ArrayExpression<BooleanExpression> ofBooleanArray(final boolean... array) {
69+
Assertions.notNull("array", array);
70+
List<BsonValue> list = new ArrayList<>();
71+
for (boolean b : array) {
72+
list.add(new BsonBoolean(b));
73+
}
74+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list)));
75+
}
76+
6177
/**
6278
* Returns an expression having the same integer value as the provided
6379
* int primitive.
@@ -68,21 +84,72 @@ public static BooleanExpression of(final boolean of) {
6884
public static IntegerExpression of(final int of) {
6985
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonInt32(of)));
7086
}
87+
88+
public static ArrayExpression<IntegerExpression> ofIntegerArray(final int... array) {
89+
Assertions.notNull("array", array);
90+
List<BsonValue> list = new ArrayList<>();
91+
for (int i : array) {
92+
list.add(new BsonInt32(i));
93+
}
94+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list)));
95+
}
96+
7197
public static IntegerExpression of(final long of) {
7298
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonInt64(of)));
7399
}
100+
101+
public static ArrayExpression<IntegerExpression> ofIntegerArray(final long... array) {
102+
Assertions.notNull("array", array);
103+
List<BsonValue> list = new ArrayList<>();
104+
for (long i : array) {
105+
list.add(new BsonInt64(i));
106+
}
107+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list)));
108+
}
109+
74110
public static NumberExpression of(final double of) {
75111
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDouble(of)));
76112
}
113+
114+
public static ArrayExpression<NumberExpression> ofNumberArray(final double... array) {
115+
Assertions.notNull("array", array);
116+
List<BsonValue> list = new ArrayList<>();
117+
for (double n : array) {
118+
list.add(new BsonDouble(n));
119+
}
120+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(list)));
121+
}
122+
77123
public static NumberExpression of(final Decimal128 of) {
78124
Assertions.notNull("Decimal128", of);
79125
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDecimal128(of)));
80126
}
127+
128+
public static ArrayExpression<NumberExpression> ofNumberArray(final Decimal128... array) {
129+
Assertions.notNull("array", array);
130+
List<BsonValue> result = new ArrayList<>();
131+
for (Decimal128 e : array) {
132+
Assertions.notNull("elements of array", e);
133+
result.add(new BsonDecimal128(e));
134+
}
135+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result)));
136+
}
137+
81138
public static DateExpression of(final Instant of) {
82139
Assertions.notNull("Instant", of);
83140
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonDateTime(of.toEpochMilli())));
84141
}
85142

143+
public static ArrayExpression<DateExpression> ofDateArray(final Instant... array) {
144+
Assertions.notNull("array", array);
145+
List<BsonValue> result = new ArrayList<>();
146+
for (Instant e : array) {
147+
Assertions.notNull("elements of array", e);
148+
result.add(new BsonDateTime(e.toEpochMilli()));
149+
}
150+
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result)));
151+
}
152+
86153
/**
87154
* Returns an expression having the same string value as the provided
88155
* string.
@@ -95,27 +162,28 @@ public static StringExpression of(final String of) {
95162
return new MqlExpression<>((codecRegistry) -> new AstPlaceholder(new BsonString(of)));
96163
}
97164

98-
/**
99-
* Returns an array expression containing the same boolean values as the
100-
* provided array of booleans.
101-
*
102-
* @param array the array of booleans
103-
* @return the boolean array expression
104-
*/
105-
public static ArrayExpression<BooleanExpression> ofBooleanArray(final boolean... array) {
165+
166+
public static ArrayExpression<StringExpression> ofStringArray(final String... array) {
167+
Assertions.notNull("array", array);
106168
List<BsonValue> result = new ArrayList<>();
107-
for (boolean b : array) {
108-
result.add(new BsonBoolean(b));
169+
for (String e : array) {
170+
Assertions.notNull("elements of array", e);
171+
result.add(new BsonString(e));
109172
}
110173
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(result)));
111174
}
112175

113-
114-
public static ArrayExpression<IntegerExpression> ofIntegerArray(final int... ofIntegerArray) {
115-
List<BsonValue> array = Arrays.stream(ofIntegerArray)
116-
.mapToObj(BsonInt32::new)
117-
.collect(Collectors.toList());
118-
return new MqlExpression<>((cr) -> new AstPlaceholder(new BsonArray(array)));
176+
@SafeVarargs // nothing is stored in the array
177+
public static <T extends Expression> ArrayExpression<T> ofArray(final T... array) {
178+
Assertions.notNull("array", array);
179+
return new MqlExpression<>((cr) -> {
180+
List<BsonValue> list = new ArrayList<>();
181+
for (T v : array) {
182+
Assertions.notNull("elements of array", v);
183+
list.add(((MqlExpression<?>) v).toBsonValue(cr));
184+
}
185+
return new AstPlaceholder(new BsonArray(list));
186+
});
119187
}
120188

121189
public static DocumentExpression ofDocument(final Bson document) {
@@ -133,6 +201,7 @@ public static <R extends Expression> R ofNull() {
133201
}
134202

135203
static NumberExpression numberToExpression(final Number number) {
204+
Assertions.notNull("number", number);
136205
if (number instanceof Integer) {
137206
return of((int) number);
138207
} else if (number instanceof Long) {

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

Lines changed: 72 additions & 2 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

@@ -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 elementAt(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> concat(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> union(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)