Skip to content

Commit 81ae5a6

Browse files
fieteboernerjenssegers
authored andcommitted
Aggregate subdocument arrays (#918)
* add sub document aggregation array functionality example: Model::sum('subarray.*.price'); this method is much simpler as to use as complex raw aggregations for this function a $unwind directive will be pushed in pipeline before $group * change testSubdocumentArrayAggregate change test for different scenarios * rebase to latest master and fix style ci issues
1 parent 333dd57 commit 81ae5a6

File tree

2 files changed

+30
-0
lines changed

2 files changed

+30
-0
lines changed

src/Jenssegers/Mongodb/Query/Builder.php

+14
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ public function getFresh($columns = [])
208208
// Use MongoDB's aggregation framework when using grouping or aggregation functions.
209209
if ($this->groups or $this->aggregate or $this->paginating) {
210210
$group = [];
211+
$unwinds = [];
211212

212213
// Add grouping columns to the $group part of the aggregation pipeline.
213214
if ($this->groups) {
@@ -233,6 +234,13 @@ public function getFresh($columns = [])
233234
$function = $this->aggregate['function'];
234235

235236
foreach ($this->aggregate['columns'] as $column) {
237+
// Add unwind if a subdocument array should be aggregated
238+
// column: subarray.price => {$unwind: '$subarray'}
239+
if (count($splitColumns = explode('.*.', $column)) == 2) {
240+
$unwinds[] = $splitColumns[0];
241+
$column = implode('.', $splitColumns);
242+
}
243+
236244
// Translate count into sum.
237245
if ($function == 'count') {
238246
$group['aggregate'] = ['$sum' => 1];
@@ -262,6 +270,12 @@ public function getFresh($columns = [])
262270
if ($wheres) {
263271
$pipeline[] = ['$match' => $wheres];
264272
}
273+
274+
// apply unwinds for subdocument array aggregation
275+
foreach ($unwinds as $unwind) {
276+
$pipeline[] = ['$unwind' => '$' . $unwind];
277+
}
278+
265279
if ($group) {
266280
$pipeline[] = ['$group' => $group];
267281
}

tests/QueryBuilderTest.php

+16
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,22 @@ public function testSubdocumentAggregate()
419419
$this->assertEquals(16.25, DB::collection('items')->avg('amount.hidden'));
420420
}
421421

422+
public function testSubdocumentArrayAggregate()
423+
{
424+
DB::collection('items')->insert([
425+
['name' => 'knife', 'amount' => [['hidden' => 10, 'found' => 3],['hidden' => 5, 'found' => 2]]],
426+
['name' => 'fork', 'amount' => [['hidden' => 35, 'found' => 12],['hidden' => 7, 'found' => 17],['hidden' => 1, 'found' => 19]]],
427+
['name' => 'spoon', 'amount' => [['hidden' => 14, 'found' => 21]]],
428+
['name' => 'teaspoon', 'amount' => []],
429+
]);
430+
431+
$this->assertEquals(72, DB::collection('items')->sum('amount.*.hidden'));
432+
$this->assertEquals(6, DB::collection('items')->count('amount.*.hidden'));
433+
$this->assertEquals(1, DB::collection('items')->min('amount.*.hidden'));
434+
$this->assertEquals(35, DB::collection('items')->max('amount.*.hidden'));
435+
$this->assertEquals(12, DB::collection('items')->avg('amount.*.hidden'));
436+
}
437+
422438
public function testUpsert()
423439
{
424440
DB::collection('items')->where('name', 'knife')

0 commit comments

Comments
 (0)