Skip to content

Commit dcaec6e

Browse files
committed
Added tweaks and documentation to previous pull request
1 parent 6bb72e8 commit dcaec6e

File tree

3 files changed

+89
-66
lines changed

3 files changed

+89
-66
lines changed

Diff for: src/Jenssegers/Mongodb/Builder.php

+39-37
Original file line numberDiff line numberDiff line change
@@ -64,109 +64,110 @@ public function getFresh($columns = array('*'))
6464
// all of the columns on the table using the "wildcard" column character.
6565
if (is_null($this->columns)) $this->columns = $columns;
6666

67-
// Drop all columns if * is present
68-
if (in_array('*', $this->columns))
69-
{
70-
$this->columns = array();
71-
}
67+
// Drop all columns if * is present, MongoDB does not work this way
68+
if (in_array('*', $this->columns)) $this->columns = array();
7269

7370
// Compile wheres
7471
$wheres = $this->compileWheres();
7572

76-
// Use aggregation framework if needed
73+
// Aggregation query
7774
if ($this->groups || $this->aggregate)
7875
{
79-
$pipeline = array();
8076
$group = array();
8177

82-
// Grouping
78+
// Apply grouping
8379
if ($this->groups)
8480
{
8581
foreach ($this->groups as $column)
8682
{
83+
// Mark column as grouped
8784
$group['_id'][$column] = '$' . $column;
85+
86+
// Aggregate by $last when grouping, this mimics MySQL's behaviour a bit
8887
$group[$column] = array('$last' => '$' . $column);
8988
}
9089
}
9190
else
9291
{
93-
$group['_id'] = 0;
92+
// If we don't use grouping, set the _id to null to prepare the pipeline for
93+
// other aggregation functions
94+
$group['_id'] = null;
9495
}
9596

96-
// Columns
97+
// When additional columns are requested, aggregate them by $last as well
9798
foreach ($this->columns as $column)
9899
{
99-
$group[$column] = array('$last' => '$' . $column);
100+
// Replace possible dots in subdocument queries with underscores
101+
$key = str_replace('.', '_', $column);
102+
$group[$key] = array('$last' => '$' . $column);
100103
}
101104

102-
// Apply aggregation functions
105+
// Apply aggregation functions, these may override previous aggregations
103106
if ($this->aggregate)
104107
{
105108
$function = $this->aggregate['function'];
106109

107110
foreach ($this->aggregate['columns'] as $column)
108111
{
112+
// Replace possible dots in subdocument queries with underscores
113+
$key = str_replace('.', '_', $column);
114+
109115
// Translate count into sum
110116
if ($function == 'count')
111117
{
112-
$group[$column] = array('$sum' => 1);
118+
$group[$key] = array('$sum' => 1);
113119
}
114120
// Pass other functions directly
115121
else
116122
{
117-
// Normally this aggregate function would overwrite the
118-
// $last group set above, but since we are modifying
119-
// the string, we need to unset it directly.
120-
if (isset($group[$column]))
121-
{
122-
unset($group[$column]);
123-
}
124-
125-
$key = str_replace('.', '_', $column);
126123
$group[$key] = array('$' . $function => '$' . $column);
127124
}
128125
}
129126
}
130127

131-
if ($wheres) $pipeline[] = array('$match' => $wheres);
132-
128+
// Build pipeline
129+
$pipeline = array();
130+
if ($wheres) $pipeline[] = array('$match' => $wheres);
133131
$pipeline[] = array('$group' => $group);
134132

135133
// Apply order and limit
136134
if ($this->orders) $pipeline[] = array('$sort' => $this->orders);
137135
if ($this->offset) $pipeline[] = array('$skip' => $this->offset);
138136
if ($this->limit) $pipeline[] = array('$limit' => $this->limit);
139137

138+
// Execute aggregation
140139
$results = $this->collection->aggregate($pipeline);
141140

142141
// Return results
143142
return $results['result'];
144143
}
144+
145+
// Distinct query
146+
else if ($this->distinct)
147+
{
148+
// Return distinct results directly
149+
$column = isset($this->columns[0]) ? $this->columns[0] : '_id';
150+
return $this->collection->distinct($column, $wheres);
151+
}
152+
153+
// Normal query
145154
else
146155
{
147-
// Execute distinct
148-
if ($this->distinct)
149-
{
150-
$column = isset($this->columns[0]) ? $this->columns[0] : '_id';
151-
return $this->collection->distinct($column, $wheres);
152-
}
153-
154-
// Columns
155156
$columns = array();
156157
foreach ($this->columns as $column)
157158
{
158159
$columns[$column] = true;
159160
}
160161

161-
// Get the MongoCursor
162+
// Execute query and get MongoCursor
162163
$cursor = $this->collection->find($wheres, $columns);
163164

164165
// Apply order, offset and limit
165166
if ($this->orders) $cursor->sort($this->orders);
166167
if ($this->offset) $cursor->skip($this->offset);
167168
if ($this->limit) $cursor->limit($this->limit);
168169

169-
// Return results
170+
// Return results as an array with numeric keys
170171
return iterator_to_array($cursor, false);
171172
}
172173
}
@@ -212,6 +213,7 @@ public function aggregate($function, $columns = array('*'))
212213

213214
if (isset($results[0]))
214215
{
216+
// Replace possible dots in subdocument queries with underscores
215217
$key = str_replace('.', '_', $columns[0]);
216218
return $results[0][$key];
217219
}
@@ -280,7 +282,7 @@ public function insert(array $values)
280282
{
281283
// As soon as we find a value that is not an array we assume the user is
282284
// inserting a single document.
283-
if (!is_array($value))
285+
if (!is_array($value))
284286
{
285287
$batch = false; break;
286288
}
@@ -532,7 +534,7 @@ protected function performUpdate($query)
532534

533535
/**
534536
* Convert a key to MongoID if needed
535-
*
537+
*
536538
* @param mixed $id
537539
* @return mixed
538540
*/
@@ -558,7 +560,7 @@ protected function compileWheres()
558560
// The new list of compiled wheres
559561
$wheres = array();
560562

561-
foreach ($this->wheres as $i => &$where)
563+
foreach ($this->wheres as $i => &$where)
562564
{
563565
// Convert id's
564566
if (isset($where['column']) && $where['column'] == '_id')

Diff for: tests/QueryBuilderTest.php

+35
Original file line numberDiff line numberDiff line change
@@ -337,4 +337,39 @@ public function testList()
337337
$this->assertEquals(array('knife' => 'sharp', 'fork' => 'sharp', 'spoon' => 'round'), $list);
338338
}
339339

340+
public function testAggregate()
341+
{
342+
DB::collection('items')->insert(array(
343+
array('name' => 'knife', 'type' => 'sharp', 'amount' => 34),
344+
array('name' => 'fork', 'type' => 'sharp', 'amount' => 20),
345+
array('name' => 'spoon', 'type' => 'round', 'amount' => 3),
346+
array('name' => 'spoon', 'type' => 'round', 'amount' => 14)
347+
));
348+
349+
$this->assertEquals(71, DB::collection('items')->sum('amount'));
350+
$this->assertEquals(4, DB::collection('items')->count('amount'));
351+
$this->assertEquals(3, DB::collection('items')->min('amount'));
352+
$this->assertEquals(34, DB::collection('items')->max('amount'));
353+
$this->assertEquals(17.75, DB::collection('items')->avg('amount'));
354+
355+
$this->assertEquals(2, DB::collection('items')->where('name', 'spoon')->count('amount'));
356+
$this->assertEquals(14, DB::collection('items')->where('name', 'spoon')->max('amount'));
357+
}
358+
359+
public function testSubdocumentAggregate()
360+
{
361+
DB::collection('items')->insert(array(
362+
array('name' => 'knife', 'amount' => array('hidden' => 10, 'found' => 3)),
363+
array('name' => 'fork', 'amount' => array('hidden' => 35, 'found' => 12)),
364+
array('name' => 'spoon', 'amount' => array('hidden' => 14, 'found' => 21)),
365+
array('name' => 'spoon', 'amount' => array('hidden' => 6, 'found' => 4))
366+
));
367+
368+
$this->assertEquals(65, DB::collection('items')->sum('amount.hidden'));
369+
$this->assertEquals(4, DB::collection('items')->count('amount.hidden'));
370+
$this->assertEquals(6, DB::collection('items')->min('amount.hidden'));
371+
$this->assertEquals(35, DB::collection('items')->max('amount.hidden'));
372+
$this->assertEquals(16.25, DB::collection('items')->avg('amount.hidden'));
373+
}
374+
340375
}

Diff for: tests/QueryTest.php

+15-29
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,23 @@
33

44
class QueryTest extends PHPUnit_Framework_TestCase {
55

6-
public static function setUpBeforeClass()
6+
protected static $started = false;
7+
8+
public function setUp()
79
{
8-
User::create(array('name' => 'John Doe', 'age' => 35, 'title' => 'admin', 'subdocument' => array('age' => 35)));
9-
User::create(array('name' => 'Jane Doe', 'age' => 33, 'title' => 'admin', 'subdocument' => array('age' => 33)));
10-
User::create(array('name' => 'Harry Hoe', 'age' => 13, 'title' => 'user', 'subdocument' => array('age' => 13)));
11-
User::create(array('name' => 'Robert Roe', 'age' => 37, 'title' => 'user', 'subdocument' => array('age' => 37)));
12-
User::create(array('name' => 'Mark Moe', 'age' => 23, 'title' => 'user', 'subdocument' => array('age' => 23)));
13-
User::create(array('name' => 'Brett Boe', 'age' => 35, 'title' => 'user', 'subdocument' => array('age' => 35)));
14-
User::create(array('name' => 'Tommy Toe', 'age' => 33, 'title' => 'user', 'subdocument' => array('age' => 33)));
15-
User::create(array('name' => 'Yvonne Yoe', 'age' => 35, 'title' => 'admin', 'subdocument' => array('age' => 35)));
10+
if (self::$started) return;
11+
12+
User::create(array('name' => 'John Doe', 'age' => 35, 'title' => 'admin'));
13+
User::create(array('name' => 'Jane Doe', 'age' => 33, 'title' => 'admin'));
14+
User::create(array('name' => 'Harry Hoe', 'age' => 13, 'title' => 'user'));
15+
User::create(array('name' => 'Robert Roe', 'age' => 37, 'title' => 'user'));
16+
User::create(array('name' => 'Mark Moe', 'age' => 23, 'title' => 'user'));
17+
User::create(array('name' => 'Brett Boe', 'age' => 35, 'title' => 'user'));
18+
User::create(array('name' => 'Tommy Toe', 'age' => 33, 'title' => 'user'));
19+
User::create(array('name' => 'Yvonne Yoe', 'age' => 35, 'title' => 'admin'));
1620
User::create(array('name' => 'Error', 'age' => null, 'title' => null));
21+
22+
self::$started = true;
1723
}
1824

1925
public static function tearDownAfterClass()
@@ -168,26 +174,6 @@ public function testIncrements()
168174
$this->assertEquals(8, $num);
169175
}
170176

171-
public function testAggregates()
172-
{
173-
$this->assertEquals(9, User::count());
174-
$this->assertEquals(37, User::max('age'));
175-
$this->assertEquals(13, User::min('age'));
176-
$this->assertEquals(30.5, User::avg('age'));
177-
$this->assertEquals(244, User::sum('age'));
178-
179-
$this->assertEquals(37, User::max('subdocument.age'));
180-
$this->assertEquals(13, User::min('subdocument.age'));
181-
$this->assertEquals(30.5, User::avg('subdocument.age'));
182-
$this->assertEquals(244, User::sum('subdocument.age'));
183-
184-
$this->assertEquals(35, User::where('title', 'admin')->max('age'));
185-
$this->assertEquals(37, User::where('title', 'user')->max('age'));
186-
187-
$this->assertEquals(33, User::where('title', 'admin')->min('age'));
188-
$this->assertEquals(13, User::where('title', 'user')->min('age'));
189-
}
190-
191177
public function testGroupBy()
192178
{
193179
$users = User::groupBy('title')->get();

0 commit comments

Comments
 (0)