diff --git a/src/Jenssegers/Mongodb/Query/Builder.php b/src/Jenssegers/Mongodb/Query/Builder.php index 74b9ee3a6..4bfc69986 100644 --- a/src/Jenssegers/Mongodb/Query/Builder.php +++ b/src/Jenssegers/Mongodb/Query/Builder.php @@ -187,6 +187,7 @@ public function getFresh($columns = []) // Use MongoDB's aggregation framework when using grouping or aggregation functions. if ($this->groups or $this->aggregate or $this->paginating) { $group = []; + $unwinds = []; // Add grouping columns to the $group part of the aggregation pipeline. if ($this->groups) { @@ -212,6 +213,13 @@ public function getFresh($columns = []) $function = $this->aggregate['function']; foreach ($this->aggregate['columns'] as $column) { + // Add unwind if a subdocument array should be aggregated + // column: subarray.price => {$unwind: '$subarray'} + if (count($splitColumns = explode('.*.', $column)) == 2) { + $unwinds[] = $splitColumns[0]; + $column = implode('.', $splitColumns); + } + // Translate count into sum. if ($function == 'count') { $group['aggregate'] = ['$sum' => 1]; @@ -241,6 +249,12 @@ public function getFresh($columns = []) if ($wheres) { $pipeline[] = ['$match' => $wheres]; } + + // apply unwinds for subdocument array aggregation + foreach ($unwinds as $unwind) { + $pipeline[] = ['$unwind' => '$' . $unwind]; + } + if ($group) { $pipeline[] = ['$group' => $group]; } diff --git a/tests/QueryBuilderTest.php b/tests/QueryBuilderTest.php index ca08bf36a..9d45b17ba 100644 --- a/tests/QueryBuilderTest.php +++ b/tests/QueryBuilderTest.php @@ -419,6 +419,22 @@ public function testSubdocumentAggregate() $this->assertEquals(16.25, DB::collection('items')->avg('amount.hidden')); } + public function testSubdocumentArrayAggregate() + { + DB::collection('items')->insert([ + ['name' => 'knife', 'amount' => [['hidden' => 10, 'found' => 3],['hidden' => 5, 'found' => 2]]], + ['name' => 'fork', 'amount' => [['hidden' => 35, 'found' => 12],['hidden' => 7, 'found' => 17],['hidden' => 1, 'found' => 19]]], + ['name' => 'spoon', 'amount' => [['hidden' => 14, 'found' => 21]]], + ['name' => 'teaspoon', 'amount' => []], + ]); + + $this->assertEquals(72, DB::collection('items')->sum('amount.*.hidden')); + $this->assertEquals(6, DB::collection('items')->count('amount.*.hidden')); + $this->assertEquals(1, DB::collection('items')->min('amount.*.hidden')); + $this->assertEquals(35, DB::collection('items')->max('amount.*.hidden')); + $this->assertEquals(12, DB::collection('items')->avg('amount.*.hidden')); + } + public function testUpsert() { DB::collection('items')->where('name', 'knife')