Commit 96039491 authored by Jens Segers's avatar Jens Segers

Use aggregations for pagination, fixes #437 and #428

parent 9d048222
...@@ -29,6 +29,13 @@ class Builder extends BaseBuilder { ...@@ -29,6 +29,13 @@ class Builder extends BaseBuilder {
*/ */
public $timeout; public $timeout;
/**
* Indicate if we are executing a pagination query.
*
* @var bool
*/
public $paginating = false;
/** /**
* All of the available clause operators. * All of the available clause operators.
* *
...@@ -140,7 +147,7 @@ class Builder extends BaseBuilder { ...@@ -140,7 +147,7 @@ class Builder extends BaseBuilder {
$wheres = $this->compileWheres(); $wheres = $this->compileWheres();
// Use MongoDB's aggregation framework when using grouping or aggregation functions. // Use MongoDB's aggregation framework when using grouping or aggregation functions.
if ($this->groups or $this->aggregate) if ($this->groups or $this->aggregate or $this->paginating)
{ {
$group = array(); $group = array();
...@@ -155,12 +162,14 @@ class Builder extends BaseBuilder { ...@@ -155,12 +162,14 @@ class Builder extends BaseBuilder {
// this mimics MySQL's behaviour a bit. // this mimics MySQL's behaviour a bit.
$group[$column] = array('$last' => '$' . $column); $group[$column] = array('$last' => '$' . $column);
} }
}
else // Do the same for other columns that are selected.
{ foreach ($this->columns as $column)
// If we don't use grouping, set the _id to null to prepare the pipeline for {
// other aggregation functions. $key = str_replace('.', '_', $column);
$group['_id'] = null;
$group[$key] = array('$last' => '$' . $column);
}
} }
// Add aggregation functions to the $group part of the aggregation pipeline, // Add aggregation functions to the $group part of the aggregation pipeline,
...@@ -184,22 +193,26 @@ class Builder extends BaseBuilder { ...@@ -184,22 +193,26 @@ class Builder extends BaseBuilder {
} }
} }
// If no aggregation functions are used, we add the additional select columns // When using pagination, we limit the number of returned columns
// to the pipeline here, aggregating them by $last. // by adding a projection.
else if ($this->paginating)
{ {
foreach ($this->columns as $column) foreach ($this->columns as $column)
{ {
$key = str_replace('.', '_', $column); $this->projections[$column] = 1;
$group[$key] = array('$last' => '$' . $column);
} }
} }
// The _id field is mandatory when using grouping.
if ($group and empty($group['_id']))
{
$group['_id'] = null;
}
// Build the aggregation pipeline. // Build the aggregation pipeline.
$pipeline = array(); $pipeline = array();
if ($wheres) $pipeline[] = array('$match' => $wheres); if ($wheres) $pipeline[] = array('$match' => $wheres);
$pipeline[] = array('$group' => $group); if ($group) $pipeline[] = array('$group' => $group);
// Apply order and limit // Apply order and limit
if ($this->orders) $pipeline[] = array('$sort' => $this->orders); if ($this->orders) $pipeline[] = array('$sort' => $this->orders);
...@@ -370,6 +383,20 @@ class Builder extends BaseBuilder { ...@@ -370,6 +383,20 @@ class Builder extends BaseBuilder {
return $this; return $this;
} }
/**
* Set the limit and offset for a given page.
*
* @param int $page
* @param int $perPage
* @return \Illuminate\Database\Query\Builder|static
*/
public function forPage($page, $perPage = 15)
{
$this->paginating = true;
return $this->skip(($page - 1) * $perPage)->take($perPage);
}
/** /**
* Insert a new record into the database. * Insert a new record into the database.
* *
......
...@@ -7,10 +7,6 @@ class QueryTest extends TestCase { ...@@ -7,10 +7,6 @@ class QueryTest extends TestCase {
public function setUp() public function setUp()
{ {
parent::setUp(); parent::setUp();
// only run this stuff once
if (self::$started) return;
User::create(array('name' => 'John Doe', 'age' => 35, 'title' => 'admin')); User::create(array('name' => 'John Doe', 'age' => 35, 'title' => 'admin'));
User::create(array('name' => 'Jane Doe', 'age' => 33, 'title' => 'admin')); User::create(array('name' => 'Jane Doe', 'age' => 33, 'title' => 'admin'));
User::create(array('name' => 'Harry Hoe', 'age' => 13, 'title' => 'user')); User::create(array('name' => 'Harry Hoe', 'age' => 13, 'title' => 'user'));
...@@ -20,8 +16,12 @@ class QueryTest extends TestCase { ...@@ -20,8 +16,12 @@ class QueryTest extends TestCase {
User::create(array('name' => 'Tommy Toe', 'age' => 33, 'title' => 'user')); User::create(array('name' => 'Tommy Toe', 'age' => 33, 'title' => 'user'));
User::create(array('name' => 'Yvonne Yoe', 'age' => 35, 'title' => 'admin')); User::create(array('name' => 'Yvonne Yoe', 'age' => 35, 'title' => 'admin'));
User::create(array('name' => 'Error', 'age' => null, 'title' => null)); User::create(array('name' => 'Error', 'age' => null, 'title' => null));
}
self::$started = true; public function tearDown()
{
User::truncate();
parent::tearDown();
} }
public function testWhere() public function testWhere()
...@@ -311,16 +311,7 @@ class QueryTest extends TestCase { ...@@ -311,16 +311,7 @@ class QueryTest extends TestCase {
$this->assertEquals(2, $results->count()); $this->assertEquals(2, $results->count());
$this->assertNull($results->first()->title); $this->assertNull($results->first()->title);
$this->assertEquals(9, $results->total()); $this->assertEquals(9, $results->total());
} $this->assertEquals(1, $results->currentPage());
/*
* FIXME: This should be done in tearDownAfterClass, but something doens't work:
* https://travis-ci.org/duxet/laravel-mongodb/jobs/46657530
*/
public function testTruncate()
{
User::truncate();
$this->assertEquals(0, User::count());
} }
} }
...@@ -27,13 +27,9 @@ class TestCase extends Orchestra\Testbench\TestCase { ...@@ -27,13 +27,9 @@ class TestCase extends Orchestra\Testbench\TestCase {
// reset base path to point to our package's src directory // reset base path to point to our package's src directory
//$app['path.base'] = __DIR__ . '/../src'; //$app['path.base'] = __DIR__ . '/../src';
// load custom config
$config = require 'config/database.php'; $config = require 'config/database.php';
// set mongodb as default connection
$app['config']->set('database.default', 'mongodb'); $app['config']->set('database.default', 'mongodb');
// overwrite database configuration
$app['config']->set('database.connections.mysql', $config['connections']['mysql']); $app['config']->set('database.connections.mysql', $config['connections']['mysql']);
$app['config']->set('database.connections.mongodb', $config['connections']['mongodb']); $app['config']->set('database.connections.mongodb', $config['connections']['mongodb']);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment