2626import java .util .function .BinaryOperator ;
2727import java .util .function .Function ;
2828
29+ import static com .mongodb .client .model .expressions .Expressions .of ;
30+ import static com .mongodb .client .model .expressions .Expressions .ofStringArray ;
31+
2932final class MqlExpression <T extends Expression >
3033 implements Expression , BooleanExpression , IntegerExpression , NumberExpression ,
3134 StringExpression , DateExpression , DocumentExpression , ArrayExpression <T > {
@@ -61,6 +64,12 @@ private Function<CodecRegistry, AstPlaceholder> ast(final String name) {
6164 return (cr ) -> new AstPlaceholder (new BsonDocument (name , this .toBsonValue (cr )));
6265 }
6366
67+ // in cases where we must wrap the first argument in an array
68+ private Function <CodecRegistry , AstPlaceholder > astWrapped (final String name ) {
69+ return (cr ) -> new AstPlaceholder (new BsonDocument (name ,
70+ new BsonArray (Collections .singletonList (this .toBsonValue (cr )))));
71+ }
72+
6473 private Function <CodecRegistry , AstPlaceholder > ast (final String name , final Expression param1 ) {
6574 return (cr ) -> {
6675 BsonArray value = new BsonArray ();
@@ -161,6 +170,80 @@ public BooleanExpression lte(final Expression lte) {
161170 return new MqlExpression <>(ast ("$lte" , lte ));
162171 }
163172
173+ public BooleanExpression isBoolean () {
174+ return new MqlExpression <>(ast ("$type" )).eq (of ("bool" ));
175+ }
176+
177+ @ Override
178+ public BooleanExpression isBooleanOr (final BooleanExpression or ) {
179+ return this .isBoolean ().cond (this , or );
180+ }
181+
182+ public BooleanExpression isNumber () {
183+ return new MqlExpression <>(astWrapped ("$isNumber" ));
184+ }
185+
186+ @ Override
187+ public NumberExpression isNumberOr (final NumberExpression or ) {
188+ return this .isNumber ().cond (this , or );
189+ }
190+
191+ public BooleanExpression isString () {
192+ return new MqlExpression <>(ast ("$type" )).eq (of ("string" ));
193+ }
194+
195+ @ Override
196+ public StringExpression isStringOr (final StringExpression or ) {
197+ return this .isString ().cond (this , or );
198+ }
199+
200+ public BooleanExpression isDate () {
201+ return ofStringArray ("date" ).contains (new MqlExpression <>(ast ("$type" )));
202+ }
203+
204+ @ Override
205+ public DateExpression isDateOr (final DateExpression or ) {
206+ return this .isDate ().cond (this , or );
207+ }
208+
209+ public BooleanExpression isArray () {
210+ return new MqlExpression <>(astWrapped ("$isArray" ));
211+ }
212+
213+ @ SuppressWarnings ("unchecked" ) // TODO
214+ @ Override
215+ public ArrayExpression <Expression > isArrayOr (final ArrayExpression <? extends Expression > or ) {
216+ // TODO it seems that ArrEx<T> does not make sense here
217+ return (ArrayExpression <Expression >) this .isArray ().cond (this .assertImplementsAllExpressions (), or );
218+ }
219+
220+ public BooleanExpression isDocument () {
221+ return new MqlExpression <>(ast ("$type" )).eq (of ("object" ));
222+ }
223+
224+ @ Override
225+ public <R extends DocumentExpression > R isDocumentOr (final R or ) {
226+ return this .isDocument ().cond (this .assertImplementsAllExpressions (), or );
227+ }
228+
229+ @ Override
230+ public StringExpression asString () {
231+ return new MqlExpression <>(astWrapped ("$toString" ));
232+ }
233+
234+ private Function <CodecRegistry , AstPlaceholder > convertInternal (final String to , final Expression orElse ) {
235+ return (cr ) -> astDoc ("$convert" , new BsonDocument ()
236+ .append ("input" , this .fn .apply (cr ).bsonValue )
237+ .append ("onError" , extractBsonValue (cr , orElse ))
238+ .append ("to" , new BsonString (to )));
239+ }
240+
241+ @ Override
242+ public IntegerExpression parseInteger () {
243+ Expression asLong = new MqlExpression <>(ast ("$toLong" ));
244+ return new MqlExpression <>(convertInternal ("int" , asLong ));
245+ }
246+
164247 /** @see ArrayExpression */
165248
166249 @ Override
@@ -191,10 +274,7 @@ public T reduce(final T initialValue, final BinaryOperator<T> in) {
191274
192275 @ Override
193276 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 ))))));
277+ return new MqlExpression <>(astWrapped ("$size" ));
198278 }
199279
200280 @ Override
@@ -205,19 +285,13 @@ public T elementAt(final IntegerExpression at) {
205285
206286 @ Override
207287 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 ))))))
288+ return new MqlExpression <>(astWrapped ("$first" ))
212289 .assertImplementsAllExpressions ();
213290 }
214291
215292 @ Override
216293 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 ))))))
294+ return new MqlExpression <>(astWrapped ("$last" ))
221295 .assertImplementsAllExpressions ();
222296 }
223297
@@ -233,7 +307,7 @@ public BooleanExpression contains(final T item) {
233307 }
234308
235309 @ Override
236- public ArrayExpression <T > concat (final ArrayExpression <T > array ) {
310+ public ArrayExpression <T > concat (final ArrayExpression <? extends T > array ) {
237311 return new MqlExpression <>(ast ("$concatArrays" , array ))
238312 .assertImplementsAllExpressions ();
239313 }
@@ -245,17 +319,14 @@ public ArrayExpression<T> slice(final IntegerExpression start, final IntegerExpr
245319 }
246320
247321 @ Override
248- public ArrayExpression <T > union (final ArrayExpression <T > set ) {
322+ public ArrayExpression <T > union (final ArrayExpression <? extends T > set ) {
249323 return new MqlExpression <>(ast ("$setUnion" , set ))
250324 .assertImplementsAllExpressions ();
251325 }
252326
253327 @ Override
254328 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 ))))));
329+ return new MqlExpression <>(astWrapped ("$setUnion" ));
259330 }
260331
261332
@@ -307,6 +378,11 @@ public IntegerExpression abs() {
307378 return newMqlExpression (ast ("$abs" ));
308379 }
309380
381+ @ Override
382+ public DateExpression millisecondsToDate () {
383+ return newMqlExpression (ast ("$toDate" ));
384+ }
385+
310386 @ Override
311387 public NumberExpression subtract (final NumberExpression n ) {
312388 return new MqlExpression <>(ast ("$subtract" , n ));
@@ -391,19 +467,34 @@ public IntegerExpression millisecond(final StringExpression timezone) {
391467 }
392468
393469 @ Override
394- public StringExpression dateToString ( ) {
470+ public StringExpression asString ( final StringExpression timezone , final StringExpression format ) {
395471 return newMqlExpression ((cr ) -> astDoc ("$dateToString" , new BsonDocument ()
396- .append ("date" , this .toBsonValue (cr ))));
472+ .append ("date" , this .toBsonValue (cr ))
473+ .append ("format" , extractBsonValue (cr , format ))
474+ .append ("timezone" , extractBsonValue (cr , timezone ))));
397475 }
398476
399477 @ Override
400- public StringExpression dateToString (final StringExpression timezone , final StringExpression format ) {
401- return newMqlExpression ((cr ) -> astDoc ("$dateToString " , new BsonDocument ()
402- .append ("date " , this .toBsonValue (cr ))
478+ public DateExpression parseDate (final StringExpression timezone , final StringExpression format ) {
479+ return newMqlExpression ((cr ) -> astDoc ("$dateFromString " , new BsonDocument ()
480+ .append ("dateString " , this .toBsonValue (cr ))
403481 .append ("format" , extractBsonValue (cr , format ))
404482 .append ("timezone" , extractBsonValue (cr , timezone ))));
405483 }
406484
485+ @ Override
486+ public DateExpression parseDate (final StringExpression format ) {
487+ return newMqlExpression ((cr ) -> astDoc ("$dateFromString" , new BsonDocument ()
488+ .append ("dateString" , this .toBsonValue (cr ))
489+ .append ("format" , extractBsonValue (cr , format ))));
490+ }
491+
492+ @ Override
493+ public DateExpression parseDate () {
494+ return newMqlExpression ((cr ) -> astDoc ("$dateFromString" , new BsonDocument ()
495+ .append ("dateString" , this .toBsonValue (cr ))));
496+ }
497+
407498 /** @see StringExpression */
408499
409500 @ Override
0 commit comments