Skip to content

Commit 9603949

Browse files
committed
Use aggregations for pagination, fixes #437 and #428
1 parent 9d04822 commit 9603949

File tree

3 files changed

+47
-33
lines changed

3 files changed

+47
-33
lines changed

src/Jenssegers/Mongodb/Query/Builder.php

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ class Builder extends BaseBuilder {
2929
*/
3030
public $timeout;
3131

32+
/**
33+
* Indicate if we are executing a pagination query.
34+
*
35+
* @var bool
36+
*/
37+
public $paginating = false;
38+
3239
/**
3340
* All of the available clause operators.
3441
*
@@ -140,7 +147,7 @@ public function getFresh($columns = array())
140147
$wheres = $this->compileWheres();
141148

142149
// Use MongoDB's aggregation framework when using grouping or aggregation functions.
143-
if ($this->groups or $this->aggregate)
150+
if ($this->groups or $this->aggregate or $this->paginating)
144151
{
145152
$group = array();
146153

@@ -155,12 +162,14 @@ public function getFresh($columns = array())
155162
// this mimics MySQL's behaviour a bit.
156163
$group[$column] = array('$last' => '$' . $column);
157164
}
158-
}
159-
else
160-
{
161-
// If we don't use grouping, set the _id to null to prepare the pipeline for
162-
// other aggregation functions.
163-
$group['_id'] = null;
165+
166+
// Do the same for other columns that are selected.
167+
foreach ($this->columns as $column)
168+
{
169+
$key = str_replace('.', '_', $column);
170+
171+
$group[$key] = array('$last' => '$' . $column);
172+
}
164173
}
165174

166175
// Add aggregation functions to the $group part of the aggregation pipeline,
@@ -184,22 +193,26 @@ public function getFresh($columns = array())
184193
}
185194
}
186195

187-
// If no aggregation functions are used, we add the additional select columns
188-
// to the pipeline here, aggregating them by $last.
189-
else
196+
// When using pagination, we limit the number of returned columns
197+
// by adding a projection.
198+
if ($this->paginating)
190199
{
191200
foreach ($this->columns as $column)
192201
{
193-
$key = str_replace('.', '_', $column);
194-
195-
$group[$key] = array('$last' => '$' . $column);
202+
$this->projections[$column] = 1;
196203
}
197204
}
198205

206+
// The _id field is mandatory when using grouping.
207+
if ($group and empty($group['_id']))
208+
{
209+
$group['_id'] = null;
210+
}
211+
199212
// Build the aggregation pipeline.
200213
$pipeline = array();
201214
if ($wheres) $pipeline[] = array('$match' => $wheres);
202-
$pipeline[] = array('$group' => $group);
215+
if ($group) $pipeline[] = array('$group' => $group);
203216

204217
// Apply order and limit
205218
if ($this->orders) $pipeline[] = array('$sort' => $this->orders);
@@ -370,6 +383,20 @@ public function whereBetween($column, array $values, $boolean = 'and', $not = fa
370383
return $this;
371384
}
372385

386+
/**
387+
* Set the limit and offset for a given page.
388+
*
389+
* @param int $page
390+
* @param int $perPage
391+
* @return \Illuminate\Database\Query\Builder|static
392+
*/
393+
public function forPage($page, $perPage = 15)
394+
{
395+
$this->paginating = true;
396+
397+
return $this->skip(($page - 1) * $perPage)->take($perPage);
398+
}
399+
373400
/**
374401
* Insert a new record into the database.
375402
*

tests/QueryTest.php

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ class QueryTest extends TestCase {
77
public function setUp()
88
{
99
parent::setUp();
10-
11-
// only run this stuff once
12-
if (self::$started) return;
13-
1410
User::create(array('name' => 'John Doe', 'age' => 35, 'title' => 'admin'));
1511
User::create(array('name' => 'Jane Doe', 'age' => 33, 'title' => 'admin'));
1612
User::create(array('name' => 'Harry Hoe', 'age' => 13, 'title' => 'user'));
@@ -20,8 +16,12 @@ public function setUp()
2016
User::create(array('name' => 'Tommy Toe', 'age' => 33, 'title' => 'user'));
2117
User::create(array('name' => 'Yvonne Yoe', 'age' => 35, 'title' => 'admin'));
2218
User::create(array('name' => 'Error', 'age' => null, 'title' => null));
19+
}
2320

24-
self::$started = true;
21+
public function tearDown()
22+
{
23+
User::truncate();
24+
parent::tearDown();
2525
}
2626

2727
public function testWhere()
@@ -311,16 +311,7 @@ public function testPaginate()
311311
$this->assertEquals(2, $results->count());
312312
$this->assertNull($results->first()->title);
313313
$this->assertEquals(9, $results->total());
314-
}
315-
316-
/*
317-
* FIXME: This should be done in tearDownAfterClass, but something doens't work:
318-
* https://travis-ci.org/duxet/laravel-mongodb/jobs/46657530
319-
*/
320-
public function testTruncate()
321-
{
322-
User::truncate();
323-
$this->assertEquals(0, User::count());
314+
$this->assertEquals(1, $results->currentPage());
324315
}
325316

326317
}

tests/TestCase.php

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,9 @@ protected function getEnvironmentSetUp($app)
2727
// reset base path to point to our package's src directory
2828
//$app['path.base'] = __DIR__ . '/../src';
2929

30-
// load custom config
3130
$config = require 'config/database.php';
3231

33-
// set mongodb as default connection
3432
$app['config']->set('database.default', 'mongodb');
35-
36-
// overwrite database configuration
3733
$app['config']->set('database.connections.mysql', $config['connections']['mysql']);
3834
$app['config']->set('database.connections.mongodb', $config['connections']['mongodb']);
3935

0 commit comments

Comments
 (0)