@@ -404,113 +404,154 @@ unaryExpression.Arg is AstGetFieldExpression innerMostGetFieldExpression &&
404
404
public override AstNode VisitMapExpression ( AstMapExpression node )
405
405
{
406
406
// { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } => { __agg0 : { $push : f(x => element) } } + "$__agg0"
407
- if ( node . Input is AstGetFieldExpression mapInputGetFieldExpression &&
408
- mapInputGetFieldExpression . FieldName . IsStringConstant ( "_elements" ) &&
409
- mapInputGetFieldExpression . Input . IsRootVar ( ) )
407
+ if ( IsElementsField ( node . Input ) )
410
408
{
411
409
var rewrittenArg = ( AstExpression ) AstNodeReplacer . Replace ( node . In , ( node . As , _element ) ) ;
412
410
var accumulatorExpression = AstExpression . UnaryAccumulator ( AstUnaryAccumulatorOperator . Push , rewrittenArg ) ;
413
- var accumulatorFieldName = _accumulators . AddAccumulatorExpression ( accumulatorExpression ) ;
414
- return AstExpression . GetField ( AstExpression . RootVar , accumulatorFieldName ) ;
411
+ return CreateOptimizedExpression ( accumulatorExpression ) ;
415
412
}
416
413
417
414
return base . VisitMapExpression ( node ) ;
418
415
}
419
416
417
+ public override AstNode VisitMedianExpression ( AstMedianExpression node )
418
+ {
419
+ // { $median : { input: { $getField : { input : "$$ROOT", field : "_elements" } }, method: "approximate" } } => { __agg0 : { $median : { input: element, method: "approximate" } } } + "$__agg0"
420
+ if ( IsElementsField ( node . Input ) )
421
+ {
422
+ var accumulator = AstExpression . ComplexAccumulator (
423
+ AstComplexAccumulatorOperator . Median ,
424
+ new Dictionary < string , AstExpression >
425
+ {
426
+ [ "input" ] = _element ,
427
+ [ "method" ] = "approximate"
428
+ } ) ;
429
+ return CreateOptimizedExpression ( accumulator ) ;
430
+ }
431
+
432
+ // { $median : { input: { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } }, method: "approximate" } }
433
+ // => { __agg0 : { $median : { input: f(x => element), method: "approximate" } } } + "$__agg0"
434
+ if ( IsMappedElementsField ( node . Input , out var mapExpression , out var rewrittenArg ) )
435
+ {
436
+ var accumulator = AstExpression . ComplexAccumulator (
437
+ AstComplexAccumulatorOperator . Median ,
438
+ new Dictionary < string , AstExpression >
439
+ {
440
+ [ "input" ] = rewrittenArg ,
441
+ [ "method" ] = "approximate"
442
+ } ) ;
443
+ return CreateOptimizedExpression ( accumulator ) ;
444
+ }
445
+
446
+ return base . VisitMedianExpression ( node ) ;
447
+ }
448
+
449
+ public override AstNode VisitPercentileExpression ( AstPercentileExpression node )
450
+ {
451
+ // { $percentile : { input: { $getField : { input : "$$ROOT", field : "_elements" } }, p: [...], method: "approximate" } }
452
+ // => { __agg0 : { $percentile : { input: element, p: [...], method: "approximate" } } } + "$__agg0"
453
+ if ( IsElementsField ( node . Input ) )
454
+ {
455
+ var accumulator = AstExpression . ComplexAccumulator (
456
+ AstComplexAccumulatorOperator . Percentile ,
457
+ new Dictionary < string , AstExpression >
458
+ {
459
+ [ "input" ] = _element ,
460
+ [ "p" ] = node . Percentiles ,
461
+ [ "method" ] = "approximate"
462
+ } ) ;
463
+ return CreateOptimizedExpression ( accumulator ) ;
464
+ }
465
+
466
+ // { $percentile : { input: { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } }, p: [...], method: "approximate" } }
467
+ // => { __agg0 : { $percentile : { input: f(x => element), p: [...], method: "approximate" } } } + "$__agg0"
468
+ if ( IsMappedElementsField ( node . Input , out var mapExpression , out var rewrittenArg ) )
469
+ {
470
+ var accumulator = AstExpression . ComplexAccumulator (
471
+ AstComplexAccumulatorOperator . Percentile ,
472
+ new Dictionary < string , AstExpression >
473
+ {
474
+ [ "input" ] = rewrittenArg ,
475
+ [ "p" ] = node . Percentiles ,
476
+ [ "method" ] = "approximate"
477
+ } ) ;
478
+ return CreateOptimizedExpression ( accumulator ) ;
479
+ }
480
+
481
+ return base . VisitPercentileExpression ( node ) ;
482
+ }
483
+
420
484
public override AstNode VisitPickExpression ( AstPickExpression node )
421
485
{
422
486
// { $pickOperator : { source : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", sortBy : s, selector : f(x) } }
423
487
// => { __agg0 : { $pickAccumulatorOperator : { sortBy : s, selector : f(x => element) } } } + "$__agg0"
424
- if ( node . Source is AstGetFieldExpression getFieldExpression &&
425
- getFieldExpression . Input . IsRootVar ( ) &&
426
- getFieldExpression . FieldName . IsStringConstant ( "_elements" ) )
488
+ if ( IsElementsField ( node . Source ) )
427
489
{
428
490
var @operator = node . Operator . ToAccumulatorOperator ( ) ;
429
491
var rewrittenSelector = ( AstExpression ) AstNodeReplacer . Replace ( node . Selector , ( node . As , _element ) ) ;
430
492
var accumulatorExpression = new AstPickAccumulatorExpression ( @operator , node . SortBy , rewrittenSelector , node . N ) ;
431
- var accumulatorFieldName = _accumulators . AddAccumulatorExpression ( accumulatorExpression ) ;
432
- return AstExpression . GetField ( AstExpression . RootVar , accumulatorFieldName ) ;
493
+ return CreateOptimizedExpression ( accumulatorExpression ) ;
433
494
}
434
495
435
496
return base . VisitPickExpression ( node ) ;
436
497
}
437
498
438
499
public override AstNode VisitUnaryExpression ( AstUnaryExpression node )
439
500
{
440
- if ( TryOptimizeSizeOfElements ( out var optimizedExpression ) )
501
+ // { $size : "$_elements" } => { __agg0 : { $sum : 1 } } + "$__agg0"
502
+ if ( node . Operator == AstUnaryOperator . Size )
441
503
{
442
- return optimizedExpression ;
504
+ if ( node . Arg is AstGetFieldExpression argGetFieldExpression &&
505
+ argGetFieldExpression . FieldName . IsStringConstant ( "_elements" ) )
506
+ {
507
+ var accumulatorExpression = AstExpression . UnaryAccumulator ( AstUnaryAccumulatorOperator . Sum , 1 ) ;
508
+ return CreateOptimizedExpression ( accumulatorExpression ) ;
509
+ }
443
510
}
444
511
445
- if ( TryOptimizeAccumulatorOfElements ( out optimizedExpression ) )
512
+ // { $accumulator : { $getField : { input : "$$ROOT", field : "_elements" } } } => { __agg0 : { $accumulator : element } } + "$__agg0"
513
+ if ( node . Operator . IsAccumulator ( out var accumulatorOperator ) && IsElementsField ( node . Arg ) )
446
514
{
447
- return optimizedExpression ;
515
+ var accumulatorExpression = AstExpression . UnaryAccumulator ( accumulatorOperator , _element ) ;
516
+ return CreateOptimizedExpression ( accumulatorExpression ) ;
448
517
}
449
518
450
- if ( TryOptimizeAccumulatorOfMappedElements ( out optimizedExpression ) )
519
+ // { $accumulator : { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } } => { __agg0 : { $accumulator : f(x => element) } } + "$__agg0"
520
+ if ( node . Operator . IsAccumulator ( out accumulatorOperator ) &&
521
+ IsMappedElementsField ( node . Arg , out var mapExpression , out var rewrittenArg ) )
451
522
{
452
- return optimizedExpression ;
523
+ var accumulatorExpression = AstExpression . UnaryAccumulator ( accumulatorOperator , rewrittenArg ) ;
524
+ return CreateOptimizedExpression ( accumulatorExpression ) ;
453
525
}
454
526
455
527
return base . VisitUnaryExpression ( node ) ;
528
+ }
456
529
457
- bool TryOptimizeSizeOfElements ( out AstExpression optimizedExpression )
458
- {
459
- // { $size : "$_elements" } => { __agg0 : { $sum : 1 } } + "$__agg0"
460
- if ( node . Operator == AstUnaryOperator . Size )
461
- {
462
- if ( node . Arg is AstGetFieldExpression argGetFieldExpression &&
463
- argGetFieldExpression . FieldName . IsStringConstant ( "_elements" ) )
464
- {
465
- var accumulatorExpression = AstExpression . UnaryAccumulator ( AstUnaryAccumulatorOperator . Sum , 1 ) ;
466
- var accumulatorFieldName = _accumulators . AddAccumulatorExpression ( accumulatorExpression ) ;
467
- optimizedExpression = AstExpression . GetField ( AstExpression . RootVar , accumulatorFieldName ) ;
468
- return true ;
469
- }
470
- }
471
-
472
- optimizedExpression = null ;
473
- return false ;
474
- }
530
+ private bool IsElementsField ( AstExpression expression )
531
+ {
532
+ return expression is AstGetFieldExpression getFieldExpression &&
533
+ getFieldExpression . FieldName . IsStringConstant ( "_elements" ) &&
534
+ getFieldExpression . Input . IsRootVar ( ) ;
535
+ }
475
536
476
- bool TryOptimizeAccumulatorOfElements ( out AstExpression optimizedExpression )
537
+ private bool IsMappedElementsField ( AstExpression expression , out AstMapExpression mapExpression , out AstExpression rewrittenArg )
538
+ {
539
+ if ( expression is AstMapExpression map && IsElementsField ( map . Input ) )
477
540
{
478
- // { $accumulator : { $getField : { input : "$$ROOT", field : "_elements" } } } => { __agg0 : { $accumulator : element } } + "$__agg0"
479
- if ( node . Operator . IsAccumulator ( out var accumulatorOperator ) &&
480
- node . Arg is AstGetFieldExpression getFieldExpression &&
481
- getFieldExpression . FieldName . IsStringConstant ( "_elements" ) &&
482
- getFieldExpression . Input . IsRootVar ( ) )
483
- {
484
- var accumulatorExpression = AstExpression . UnaryAccumulator ( accumulatorOperator , _element ) ;
485
- var accumulatorFieldName = _accumulators . AddAccumulatorExpression ( accumulatorExpression ) ;
486
- optimizedExpression = AstExpression . GetField ( AstExpression . RootVar , accumulatorFieldName ) ;
487
- return true ;
488
- }
489
-
490
- optimizedExpression = null ;
491
- return false ;
492
-
541
+ mapExpression = map ;
542
+ rewrittenArg = ( AstExpression ) AstNodeReplacer . Replace ( map . In , ( map . As , _element ) ) ;
543
+ return true ;
493
544
}
494
545
495
- bool TryOptimizeAccumulatorOfMappedElements ( out AstExpression optimizedExpression )
496
- {
497
- // { $accumulator : { $map : { input : { $getField : { input : "$$ROOT", field : "_elements" } }, as : "x", in : f(x) } } } => { __agg0 : { $accumulator : f(x => element) } } + "$__agg0"
498
- if ( node . Operator . IsAccumulator ( out var accumulatorOperator ) &&
499
- node . Arg is AstMapExpression mapExpression &&
500
- mapExpression . Input is AstGetFieldExpression mapInputGetFieldExpression &&
501
- mapInputGetFieldExpression . FieldName . IsStringConstant ( "_elements" ) &&
502
- mapInputGetFieldExpression . Input . IsRootVar ( ) )
503
- {
504
- var rewrittenArg = ( AstExpression ) AstNodeReplacer . Replace ( mapExpression . In , ( mapExpression . As , _element ) ) ;
505
- var accumulatorExpression = AstExpression . UnaryAccumulator ( accumulatorOperator , rewrittenArg ) ;
506
- var accumulatorFieldName = _accumulators . AddAccumulatorExpression ( accumulatorExpression ) ;
507
- optimizedExpression = AstExpression . GetField ( AstExpression . RootVar , accumulatorFieldName ) ;
508
- return true ;
509
- }
546
+ mapExpression = null ;
547
+ rewrittenArg = null ;
548
+ return false ;
549
+ }
510
550
511
- optimizedExpression = null ;
512
- return false ;
513
- }
551
+ private AstExpression CreateOptimizedExpression ( AstAccumulatorExpression accumulator )
552
+ {
553
+ var fieldName = _accumulators . AddAccumulatorExpression ( accumulator ) ;
554
+ return AstExpression . GetField ( AstExpression . RootVar , fieldName ) ;
514
555
}
515
556
}
516
557
0 commit comments