Skip to content

Commit d6d1eed

Browse files
committed
Implement switch expression
1 parent d0099cd commit d6d1eed

File tree

8 files changed

+543
-5
lines changed

8 files changed

+543
-5
lines changed
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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 java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.function.Function;
22+
23+
public final class Branches {
24+
25+
Branches() {
26+
// package-private
27+
}
28+
29+
private <T extends Expression, R extends Expression> BranchesIntermediary<T, R> with(final Function<T, SwitchCase<R>> switchCase) {
30+
List<Function<T, SwitchCase<R>>> v = new ArrayList<>();
31+
v.add(switchCase);
32+
return new BranchesIntermediary<>(v);
33+
}
34+
35+
private static <T extends Expression> MqlExpression<?> mqlEx(final T value) {
36+
return (MqlExpression<?>) value;
37+
}
38+
39+
// is fn
40+
41+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> is(final Function<T, BooleanExpression> o, final Function<T, R> r) {
42+
return this.with(value -> new SwitchCase<>(o.apply(value), r.apply(value)));
43+
}
44+
45+
// eq lt lte
46+
47+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> eq(final T v, final Function<T, R> r) {
48+
return this.with(value -> new SwitchCase<>(value.eq(v), r.apply(value)));
49+
}
50+
51+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> lt(final T v, final Function<T, R> r) {
52+
return this.with(value -> new SwitchCase<>(value.lt(v), r.apply(value)));
53+
}
54+
55+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> lte(final T v, final Function<T, R> r) {
56+
return this.with(value -> new SwitchCase<>(value.lte(v), r.apply(value)));
57+
}
58+
59+
// is type
60+
61+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> isBoolean(final Function<BooleanExpression, R> r) {
62+
return this.with(value -> new SwitchCase<>(mqlEx(value).isBoolean(), r.apply((BooleanExpression) value)));
63+
}
64+
65+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> isNumber(final Function<NumberExpression, R> r) {
66+
return this.with(value -> new SwitchCase<>(mqlEx(value).isNumber(), r.apply((NumberExpression) value)));
67+
}
68+
69+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> isString(final Function<StringExpression, R> r) {
70+
return this.with(value -> new SwitchCase<>(mqlEx(value).isString(), r.apply((StringExpression) value)));
71+
}
72+
73+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> isDate(final Function<DateExpression, R> r) {
74+
return this.with(value -> new SwitchCase<>(mqlEx(value).isDate(), r.apply((DateExpression) value)));
75+
}
76+
77+
public <T extends Expression, R extends Expression, Q extends Expression> BranchesIntermediary<T, R> isArray(final Function<ArrayExpression<Q>, R> r) {
78+
return this.with(value -> new SwitchCase<>(mqlEx(value).isArray(), r.apply((ArrayExpression<Q>) value)));
79+
}
80+
81+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> isDocument(final Function<DocumentExpression, R> r) {
82+
return this.with(value -> new SwitchCase<>(mqlEx(value).isDocument(), r.apply((DocumentExpression) value)));
83+
}
84+
85+
public <T extends Expression, R extends Expression, Q extends Expression> BranchesIntermediary<T, R> isMap(final Function<MapExpression<Q>, R> r) {
86+
return this.with(value -> new SwitchCase<>(mqlEx(value).isDocument(), r.apply((MapExpression<Q>) value)));
87+
}
88+
89+
public <T extends Expression, R extends Expression> BranchesIntermediary<T, R> isNull(final Function<Expression, R> isNull) {
90+
return this.with(value -> new SwitchCase<>(mqlEx(value).isNull(), isNull.apply(value)));
91+
}
92+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
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 java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.function.Function;
22+
23+
public final class BranchesIntermediary<T extends Expression, R extends Expression> extends BranchesTerminal<T, R> {
24+
BranchesIntermediary(final List<Function<T, SwitchCase<R>>> branches) {
25+
super(branches, null);
26+
}
27+
28+
private BranchesIntermediary<T, R> with(final Function<T, SwitchCase<R>> switchCase) {
29+
List<Function<T, SwitchCase<R>>> v = new ArrayList<>(this.getBranches());
30+
v.add(switchCase);
31+
return new BranchesIntermediary<>(v);
32+
}
33+
34+
private static <T extends Expression> MqlExpression<?> mqlEx(final T value) {
35+
return (MqlExpression<?>) value;
36+
}
37+
38+
// is fn
39+
40+
public BranchesIntermediary<T, R> is(final Function<T, BooleanExpression> o, final Function<T, R> r) {
41+
return this.with(value -> new SwitchCase<>(o.apply(value), r.apply(value)));
42+
}
43+
44+
// eq lt lte
45+
46+
public BranchesIntermediary<T, R> eq(final T v, final Function<T, R> r) {
47+
return this.with(value -> new SwitchCase<>(value.eq(v), r.apply(value)));
48+
}
49+
50+
public BranchesIntermediary<T, R> lt(final T v, final Function<T, R> r) {
51+
return this.with(value -> new SwitchCase<>(value.lt(v), r.apply(value)));
52+
}
53+
54+
public BranchesIntermediary<T, R> lte(final T v, final Function<T, R> r) {
55+
return this.with(value -> new SwitchCase<>(value.lte(v), r.apply(value)));
56+
}
57+
58+
// is type
59+
60+
public BranchesIntermediary<T, R> isBoolean(final Function<BooleanExpression, R> r) {
61+
return this.with(v -> new SwitchCase<>(mqlEx(v).isBoolean(), r.apply((BooleanExpression) v)));
62+
}
63+
64+
public BranchesIntermediary<T, R> isNumber(final Function<NumberExpression, R> r) {
65+
return this.with(v -> new SwitchCase<>(mqlEx(v).isNumber(), r.apply((NumberExpression) v)));
66+
}
67+
68+
69+
public BranchesIntermediary<T, R> isString(final Function<StringExpression, R> r) {
70+
return this.with(value -> new SwitchCase<>(mqlEx(value).isString(), r.apply((StringExpression) value)));
71+
}
72+
73+
public BranchesIntermediary<T, R> isDate(final Function<DateExpression, R> r) {
74+
return this.with(value -> new SwitchCase<>(mqlEx(value).isDate(), r.apply((DateExpression) value)));
75+
}
76+
77+
public <Q extends Expression> BranchesIntermediary<T, R> isArray(final Function<ArrayExpression<Q>, R> r) {
78+
return this.with(value -> new SwitchCase<>(mqlEx(value).isArray(), r.apply((ArrayExpression<Q>) value)));
79+
}
80+
81+
public BranchesIntermediary<T, R> isDocument(final Function<DocumentExpression, R> r) {
82+
return this.with(value -> new SwitchCase<>(mqlEx(value).isDocument(), r.apply((DocumentExpression) value)));
83+
}
84+
85+
public <Q extends Expression> BranchesIntermediary<T, R> isMap(final Function<MapExpression<Q>, R> r) {
86+
return this.with(value -> new SwitchCase<>(mqlEx(value).isDocument(), r.apply((MapExpression<Q>) value)));
87+
}
88+
89+
public BranchesIntermediary<T, R> isNull(final Function<Expression, R> r) {
90+
return this.with(value -> new SwitchCase<>(mqlEx(value).isNull(), r.apply(value)));
91+
}
92+
93+
public BranchesTerminal<T, R> defaults(final Function<T, R> r) {
94+
return this.withDefault(value -> r.apply(value));
95+
}
96+
97+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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 com.mongodb.lang.Nullable;
20+
21+
import java.util.List;
22+
import java.util.function.Function;
23+
24+
public class BranchesTerminal<T extends Expression, R extends Expression> {
25+
26+
private final List<Function<T, SwitchCase<R>>> branches;
27+
28+
private final Function<T, R> defaults;
29+
30+
BranchesTerminal(final List<Function<T, SwitchCase<R>>> branches, @Nullable final Function<T, R> defaults) {
31+
this.branches = branches;
32+
this.defaults = defaults;
33+
}
34+
35+
protected BranchesTerminal<T, R> withDefault(final Function<T, R> defaults) {
36+
return new BranchesTerminal<>(branches, defaults);
37+
}
38+
39+
protected List<Function<T, SwitchCase<R>>> getBranches() {
40+
return branches;
41+
}
42+
43+
@Nullable
44+
protected Function<T, R> getDefaults() {
45+
return defaults;
46+
}
47+
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import com.mongodb.annotations.Evolving;
2020

21+
import java.util.function.Function;
22+
2123
/**
2224
* Expressions express values that may be represented in (or computations that
2325
* may be performed within) a MongoDB server. Each expression evaluates to some
@@ -114,4 +116,17 @@ public interface Expression {
114116
<T extends Expression> MapExpression<T> isMapOr(MapExpression<T> other);
115117

116118
StringExpression asString();
119+
120+
/**
121+
* Applies the given function to this argument. Note that "apply" usually
122+
* applies functions to arguments; here, the parameters are reversed.
123+
*
124+
* @param f
125+
* @return
126+
* @param <T>
127+
* @param <R>
128+
*/
129+
<T extends Expression, R extends Expression> R apply(Function<T, R> f);
130+
131+
<T0 extends Expression, R0 extends Expression> R0 switchMap(Function<Branches, BranchesTerminal<T0, R0>> switchMap);
117132
}

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

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import java.util.function.Function;
2929

3030
import static com.mongodb.client.model.expressions.Expressions.of;
31+
import static com.mongodb.client.model.expressions.Expressions.ofNull;
3132
import static com.mongodb.client.model.expressions.Expressions.ofStringArray;
3233

3334
final class MqlExpression<T extends Expression>
@@ -284,6 +285,11 @@ public DocumentExpression unsetField(final String fieldName) {
284285

285286
/** @see Expression */
286287

288+
@Override
289+
public <Q extends Expression, R extends Expression> R apply(final Function<Q, R> f) {
290+
return f.apply(this.assertImplementsAllExpressions());
291+
}
292+
287293
@Override
288294
public BooleanExpression eq(final Expression eq) {
289295
return new MqlExpression<>(ast("$eq", eq));
@@ -315,7 +321,7 @@ public BooleanExpression lte(final Expression lte) {
315321
}
316322

317323
public BooleanExpression isBoolean() {
318-
return new MqlExpression<>(ast("$type")).eq(of("bool"));
324+
return new MqlExpression<>(astWrapped("$type")).eq(of("bool"));
319325
}
320326

321327
@Override
@@ -342,7 +348,7 @@ public IntegerExpression isIntegerOr(final IntegerExpression other) {
342348
}
343349

344350
public BooleanExpression isString() {
345-
return new MqlExpression<>(ast("$type")).eq(of("string"));
351+
return new MqlExpression<>(astWrapped("$type")).eq(of("string"));
346352
}
347353

348354
@Override
@@ -351,7 +357,7 @@ public StringExpression isStringOr(final StringExpression other) {
351357
}
352358

353359
public BooleanExpression isDate() {
354-
return ofStringArray("date").contains(new MqlExpression<>(ast("$type")));
360+
return ofStringArray("date").contains(new MqlExpression<>(astWrapped("$type")));
355361
}
356362

357363
@Override
@@ -364,7 +370,7 @@ public BooleanExpression isArray() {
364370
}
365371

366372
public Expression ifNull(final Expression ifNull) {
367-
return new MqlExpression<>(ast("$ifNull", ifNull, Expressions.ofNull()))
373+
return new MqlExpression<>(ast("$ifNull", ifNull, ofNull()))
368374
.assertImplementsAllExpressions();
369375
}
370376

@@ -383,7 +389,7 @@ public <R extends Expression> ArrayExpression<R> isArrayOr(final ArrayExpression
383389
}
384390

385391
public BooleanExpression isDocument() {
386-
return new MqlExpression<>(ast("$type")).eq(of("object"));
392+
return new MqlExpression<>(astWrapped("$type")).eq(of("object"));
387393
}
388394

389395
@Override
@@ -400,6 +406,10 @@ public <R extends Expression> MapExpression<R> isMapOr(final MapExpression<R> ot
400406
return this.isMap().cond(this.assertImplementsAllExpressions(), other);
401407
}
402408

409+
public BooleanExpression isNull() {
410+
return this.eq(ofNull());
411+
}
412+
403413
@Override
404414
public StringExpression asString() {
405415
return new MqlExpression<>(astWrapped("$toString"));
@@ -412,6 +422,32 @@ private Function<CodecRegistry, AstPlaceholder> convertInternal(final String to,
412422
.append("to", new BsonString(to)));
413423
}
414424

425+
@Override
426+
public <T0 extends Expression, R0 extends Expression> R0 switchMap(
427+
final Function<Branches, BranchesTerminal<T0, R0>> switchMap) {
428+
T0 value = this.assertImplementsAllExpressions();
429+
BranchesTerminal<T0, R0> construct = switchMap.apply(new Branches());
430+
return switchMapInternal(value, construct);
431+
}
432+
433+
private <T0 extends Expression, R0 extends Expression> R0 switchMapInternal(
434+
final T0 value, final BranchesTerminal<T0, R0> construct) {
435+
return newMqlExpression((cr) -> {
436+
BsonArray branches = new BsonArray();
437+
for (Function<T0, SwitchCase<R0>> fn : construct.getBranches()) {
438+
SwitchCase<R0> result = fn.apply(value);
439+
branches.add(new BsonDocument()
440+
.append("case", extractBsonValue(cr, result.getCaseValue()))
441+
.append("then", extractBsonValue(cr, result.getThenValue())));
442+
}
443+
BsonDocument switchBson = new BsonDocument().append("branches", branches);
444+
if (construct.getDefaults() != null) {
445+
switchBson = switchBson.append("default", extractBsonValue(cr, construct.getDefaults().apply(value)));
446+
}
447+
return astDoc("$switch", switchBson);
448+
});
449+
}
450+
415451
@Override
416452
public IntegerExpression parseInteger() {
417453
Expression asLong = new MqlExpression<>(ast("$toLong"));

0 commit comments

Comments
 (0)