21
21
use MongoDB \BSON \ObjectID ;
22
22
use MongoDB \BSON \Regex ;
23
23
use MongoDB \BSON \UTCDateTime ;
24
- use MongoDB \Builder \Accumulator \ FirstAccumulator ;
24
+ use MongoDB \Builder \Accumulator ;
25
25
use MongoDB \Builder \BuilderEncoder ;
26
26
use MongoDB \Builder \Expression \FieldPath ;
27
- use MongoDB \Builder \Pipeline ;
28
- use MongoDB \Builder \Stage ;
29
- use MongoDB \Builder \Stage \CountStage ;
30
- use MongoDB \Builder \Stage \GroupStage ;
31
- use MongoDB \Builder \Stage \LimitStage ;
32
- use MongoDB \Builder \Stage \MatchStage ;
33
- use MongoDB \Builder \Stage \ProjectStage ;
34
- use MongoDB \Builder \Stage \ReplaceRootStage ;
35
- use MongoDB \Builder \Stage \SkipStage ;
36
- use MongoDB \Builder \Stage \SortStage ;
37
- use MongoDB \Builder \Stage \UnwindStage ;
38
- use MongoDB \Builder \Type \StageInterface ;
39
27
use MongoDB \Builder \Variable ;
40
28
use MongoDB \Driver \Cursor ;
41
29
use Override ;
@@ -295,21 +283,21 @@ public function dump(mixed ...$args)
295
283
return $ this ;
296
284
}
297
285
298
- /** @return StageInterface[] */
299
- protected function getPipeline (): array
286
+ protected function getPipelineBuilder (): PipelineBuilder
300
287
{
301
288
$ columns = $ this ->columns ?? [];
302
289
290
+ $ pipelineBuilder = new PipelineBuilder ([], $ this ->collection , $ this ->options );
291
+
303
292
// Drop all columns if * is present, MongoDB does not work this way.
304
293
if (in_array ('* ' , $ columns )) {
305
294
$ columns = [];
306
295
}
307
296
308
- $ pipeline = [];
309
297
$ wheres = $ this ->compileWheres ();
310
298
311
299
if (count ($ wheres )) {
312
- $ pipeline [] = new MatchStage ( $ wheres );
300
+ $ pipelineBuilder -> match (... $ wheres );
313
301
}
314
302
315
303
// Use MongoDB's aggregation framework when using grouping or aggregation functions.
@@ -371,81 +359,75 @@ protected function getPipeline(): array
371
359
$ group ['_id ' ] = null ;
372
360
}
373
361
374
- // Build the aggregation pipeline.
375
- $ pipeline = [];
376
- if ($ wheres ) {
377
- $ pipeline [] = Stage::match (...$ wheres );
378
- }
379
-
380
362
// apply unwinds for subdocument array aggregation
381
363
foreach ($ unwinds as $ unwind ) {
382
- $ pipeline [] = new UnwindStage ($ unwind );
364
+ $ pipelineBuilder -> unwind ($ unwind );
383
365
}
384
366
385
367
if ($ group ) {
386
- $ pipeline [] = new GroupStage (...$ group );
368
+ $ pipelineBuilder -> group (...$ group );
387
369
}
388
370
389
371
// Apply order and limit
390
372
if ($ this ->orders ) {
391
- $ pipeline [] = new SortStage ($ this ->orders );
373
+ $ pipelineBuilder -> sort ($ this ->orders );
392
374
}
393
375
394
376
if ($ this ->offset ) {
395
- $ pipeline [] = new SkipStage ($ this ->offset );
377
+ $ pipelineBuilder -> skip ($ this ->offset );
396
378
}
397
379
398
380
if ($ this ->limit ) {
399
- $ pipeline [] = new LimitStage ($ this ->limit );
381
+ $ pipelineBuilder -> limit ($ this ->limit );
400
382
}
401
383
402
384
if ($ this ->projections ) {
403
- $ pipeline [] = new ProjectStage (...$ this ->projections );
385
+ $ pipelineBuilder -> project (...$ this ->projections );
404
386
}
405
387
406
- return $ pipeline ;
388
+ return $ pipelineBuilder ;
407
389
}
408
390
409
391
// Distinct query
410
392
if ($ this ->distinct ) {
411
393
// Return distinct results directly
412
394
$ column = $ columns [0 ] ?? '_id ' ;
413
395
414
- $ pipeline [] = new GroupStage (
415
- _id: new FieldPath ($ column ),
416
- _document: new FirstAccumulator (Variable::root ()),
396
+ $ pipelineBuilder -> group (
397
+ _id: \ MongoDB \ Builder \Expression:: fieldPath ($ column ),
398
+ _document: Accumulator:: first (Variable::root ()),
417
399
);
418
- $ pipeline [] = new ReplaceRootStage (
400
+ $ pipelineBuilder -> replaceRoot (
419
401
newRoot: new FieldPath ('_document ' ),
420
402
);
421
403
}
422
404
423
- // Normal query
424
- // Convert select columns to simple projections.
425
- $ projection = array_fill_keys ($ columns , true );
426
-
427
- // Add custom projections.
428
- if ($ this ->projections ) {
429
- $ projection = array_merge ($ projection , $ this ->projections );
430
- }
431
-
432
405
if ($ this ->orders ) {
433
- $ pipeline [] = new SortStage (...$ this ->orders );
406
+ $ pipelineBuilder -> sort (...$ this ->orders );
434
407
}
435
408
436
409
if ($ this ->offset ) {
437
- $ pipeline [] = new SkipStage ($ this ->offset );
410
+ $ pipelineBuilder -> skip ($ this ->offset );
438
411
}
439
412
440
413
if ($ this ->limit ) {
441
- $ pipeline [] = new LimitStage ($ this ->limit );
414
+ $ pipelineBuilder ->limit ($ this ->limit );
415
+ }
416
+
417
+ // Normal query
418
+ // Convert select columns to simple projections.
419
+ $ projection = array_fill_keys ($ columns , true );
420
+
421
+ // Add custom projections.
422
+ if ($ this ->projections ) {
423
+ $ projection = array_merge ($ projection , $ this ->projections );
442
424
}
443
425
444
426
if ($ projection ) {
445
- $ pipeline [] = new ProjectStage (...$ projection );
427
+ $ pipelineBuilder -> project (...$ projection );
446
428
}
447
429
448
- return $ pipeline ;
430
+ return $ pipelineBuilder ;
449
431
}
450
432
451
433
/**
@@ -457,9 +439,8 @@ protected function getPipeline(): array
457
439
*/
458
440
public function toMql (): array
459
441
{
460
- $ pipeline = $ this ->getPipeline ();
461
442
$ encoder = new BuilderEncoder ();
462
- $ pipeline = $ encoder ->encode (new Pipeline (... $ pipeline ));
443
+ $ pipeline = $ encoder ->encode ($ this -> getPipelineBuilder ()-> getPipeline ( ));
463
444
464
445
$ options = ['typeMap ' => ['root ' => 'array ' , 'document ' => 'array ' ]];
465
446
@@ -557,32 +538,37 @@ public function generateCacheKey()
557
538
/** @return ($function === null ? PipelineBuilder : self) */
558
539
public function aggregate ($ function = null , $ columns = [])
559
540
{
541
+ $ builder = $ this ->getPipelineBuilder ();
542
+
560
543
if ($ function === null ) {
561
- return new PipelineBuilder ( $ this -> getPipeline (), $ this -> collection , $ this -> options ) ;
544
+ return $ builder ;
562
545
}
563
546
564
- $ this ->aggregate = [
565
- 'function ' => $ function ,
566
- 'columns ' => $ columns ,
567
- ];
568
-
569
- $ previousColumns = $ this ->columns ;
570
-
571
- // We will also back up the select bindings since the select clause will be
572
- // removed when performing the aggregate function. Once the query is run
573
- // we will add the bindings back onto this query so they can get used.
574
- $ previousSelectBindings = $ this ->bindings ['select ' ];
575
-
576
- $ this ->bindings ['select ' ] = [];
577
-
578
- $ results = $ this ->get ($ columns );
547
+ match ($ function ) {
548
+ 'count ' => $ builder ->group (
549
+ _id: null ,
550
+ aggregate: Accumulator::sum (1 ),
551
+ ),
552
+ 'sum ' => $ builder ->group (
553
+ _id: null ,
554
+ aggregate: Accumulator::sum (\MongoDB \Builder \Expression::fieldPath ($ columns [0 ])),
555
+ ),
556
+ 'avg ' => $ builder ->group (
557
+ _id: null ,
558
+ aggregate: Accumulator::avg (\MongoDB \Builder \Expression::fieldPath ($ columns [0 ])),
559
+ ),
560
+ 'min ' => $ builder ->group (
561
+ _id: null ,
562
+ aggregate: Accumulator::min (\MongoDB \Builder \Expression::fieldPath ($ columns [0 ])),
563
+ ),
564
+ 'max ' => $ builder ->group (
565
+ _id: null ,
566
+ aggregate: Accumulator::max (\MongoDB \Builder \Expression::fieldPath ($ columns [0 ])),
567
+ ),
568
+ default => throw new InvalidArgumentException ('Unknown aggregate function: ' . $ function ),
569
+ };
579
570
580
- // Once we have executed the query, we will reset the aggregate property so
581
- // that more select queries can be executed against the database without
582
- // the aggregate value getting in the way when the grammar builds it.
583
- $ this ->aggregate = null ;
584
- $ this ->columns = $ previousColumns ;
585
- $ this ->bindings ['select ' ] = $ previousSelectBindings ;
571
+ $ results = $ builder ->get ();
586
572
587
573
if (isset ($ results [0 ])) {
588
574
$ result = (array ) $ results [0 ];
@@ -977,14 +963,14 @@ public function runPaginationCountQuery($columns = ['*'])
977
963
if ($ this ->groups || $ this ->havings ) {
978
964
$ without = $ this ->unions ? ['orders ' , 'limit ' , 'offset ' ] : ['columns ' , 'orders ' , 'limit ' , 'offset ' ];
979
965
980
- $ mql = $ this ->cloneWithout ($ without )
966
+ $ pipelienBuilder = $ this ->cloneWithout ($ without )
981
967
->cloneWithoutBindings ($ this ->unions ? ['order ' ] : ['select ' , 'order ' ])
982
- ->toMql ();
968
+ ->getPipelineBuilder ();
983
969
984
970
// Adds the $count stage to the pipeline
985
- $ mql [ ' aggregate ' ][ 0 ][] = new CountStage ('aggregate ' );
971
+ $ pipelienBuilder -> count ('aggregate ' );
986
972
987
- return $ this -> collection -> aggregate ( $ mql [ ' aggregate ' ][ 0 ], $ mql [ ' aggregate ' ][ 1 ])-> toArray ();
973
+ return $ pipelienBuilder -> get ();
988
974
}
989
975
990
976
return parent ::runPaginationCountQuery ($ columns );
0 commit comments