Browse Source

Aggregation functions added to Mongo Query.

tags/2.0.0-beta
Paul Klimov 11 years ago
parent
commit
0e082c170c
  1. 28
      extensions/mongo/Collection.php
  2. 121
      extensions/mongo/Query.php
  3. 7
      tests/unit/extensions/mongo/ActiveRecordTest.php

28
extensions/mongo/Collection.php

@ -202,9 +202,11 @@ class Collection extends Object
}
/**
* @param $pipeline
* @param array $pipelineOperator
* @return array
* Performs aggregation using Mongo Aggregation Framework.
* @param array $pipeline list of pipeline operators, or just the first operator
* @param array $pipelineOperator Additional pipeline operators
* @return array the result of the aggregation.
* @see http://docs.mongodb.org/manual/applications/aggregation/
*/
public function aggregate($pipeline, $pipelineOperator = [])
{
@ -213,20 +215,30 @@ class Collection extends Object
}
/**
* Performs aggregation using Mongo Map Reduce mechanism.
* @param mixed $keys
* @param array $initial
* @param \MongoCode|string $reduce
* @param array $options
* @return array
* @param array $initial Initial value of the aggregation counter object.
* @param \MongoCode|string $reduce function that takes two arguments (the current
* document and the aggregation to this point) and does the aggregation.
* Argument will be automatically cast to [[\MongoCode]].
* @param array $options optional parameters to the group command. Valid options include:
* - condition - criteria for including a document in the aggregation.
* - finalize - function called once per unique key that takes the final output of the reduce function.
* @return array the result of the aggregation.
*/
public function mapReduce($keys, $initial, $reduce, $options = [])
{
if (!($reduce instanceof \MongoCode)) {
$reduce = new \MongoCode($reduce);
$reduce = new \MongoCode((string)$reduce);
}
if (array_key_exists('condition', $options)) {
$options['condition'] = $this->buildCondition($options['condition']);
}
if (array_key_exists('finalize', $options)) {
if (!($options['finalize'] instanceof \MongoCode)) {
$options['finalize'] = new \MongoCode((string)$options['finalize']);
}
}
return $this->mongoCollection->group($keys, $initial, $reduce, $options);
}

121
extensions/mongo/Query.php

@ -105,8 +105,8 @@ class Query extends Component implements QueryInterface
/**
* Executes the query and returns all results as an array.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `mongo` application component will be used.
* @return array the query results. If the query results in nothing, an empty array will be returned.
*/
public function all($db = null)
@ -130,8 +130,8 @@ class Query extends Component implements QueryInterface
/**
* Executes the query and returns a single row of result.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `mongo` application component will be used.
* @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query
* results in nothing.
*/
@ -148,8 +148,8 @@ class Query extends Component implements QueryInterface
/**
* Returns the number of records.
* @param string $q the COUNT expression. Defaults to '*'.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `mongo` application component will be used.
* @return integer number of records
*/
public function count($q = '*', $db = null)
@ -160,12 +160,117 @@ class Query extends Component implements QueryInterface
/**
* Returns a value indicating whether the query result contains any row of data.
* @param Connection $db the database connection used to execute the query.
* If this parameter is not given, the `db` application component will be used.
* @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `mongo` application component will be used.
* @return boolean whether the query result contains any row of data.
*/
public function exists($db = null)
{
return $this->one($db) !== null;
}
/**
* Returns the sum of the specified column values.
* @param string $q the column name or expression.
* Make sure you properly quote column names in the expression.
* @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `mongo` application component will be used.
* @return integer the sum of the specified column values
*/
public function sum($q, $db = null)
{
return $this->aggregate($q, 'sum', $db);
}
/**
* Returns the average of the specified column values.
* @param string $q the column name or expression.
* Make sure you properly quote column names in the expression.
* @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `mongo` application component will be used.
* @return integer the average of the specified column values.
*/
public function average($q, $db = null)
{
return $this->aggregate($q, 'avg', $db);
}
/**
* Returns the minimum of the specified column values.
* @param string $q the column name or expression.
* Make sure you properly quote column names in the expression.
* @param Connection $db the database connection used to generate the SQL statement.
* If this parameter is not given, the `db` application component will be used.
* @return integer the minimum of the specified column values.
*/
public function min($q, $db = null)
{
return $this->aggregate($q, 'min', $db);
}
/**
* Returns the maximum of the specified column values.
* @param string $q the column name or expression.
* Make sure you properly quote column names in the expression.
* @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `mongo` application component will be used.
* @return integer the maximum of the specified column values.
*/
public function max($q, $db = null)
{
return $this->aggregate($q, 'max', $db);
}
/**
* Performs the aggregation for the given column.
* @param string $column column name.
* @param string $operator aggregation operator.
* @param Connection $db the database connection used to execute the query.
* @return integer aggregation result.
*/
protected function aggregate($column, $operator, $db)
{
$collection = $this->getCollection($db);
$pipelines = [];
if ($this->where !== null) {
$pipelines[] = ['$match' => $collection->buildCondition($this->where)];
}
$pipelines[] = [
'$group' => [
'_id' => '1',
'total' => [
'$' . $operator => '$' . $column
],
]
];
$result = $collection->aggregate($pipelines);
if (!empty($result['ok'])) {
return $result['result'][0]['total'];
} else {
return 0;
}
}
/**
* Returns a list of distinct values for the given column across a collection.
* @param string $q column to use.
* @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `mongo` application component will be used.
* @return array array of distinct values
*/
public function distinct($q, $db = null)
{
$collection = $this->getCollection($db);
if ($this->where !== null) {
$condition = $this->where;
} else {
$condition = [];
}
$result = $collection->distinct($q, $condition);
if ($result === false) {
return [];
} else {
return $result;
}
}
}

7
tests/unit/extensions/mongo/ActiveRecordTest.php

@ -83,13 +83,14 @@ class ActiveRecordTest extends MongoTestCase
$this->assertTrue($customer instanceof Customer);
$this->assertEquals(4, $customer->status);
// find count, sum, average, min, max, scalar
// find count, sum, average, min, max, distinct
$this->assertEquals(10, Customer::find()->count());
$this->assertEquals(1, Customer::find()->where(['status' => 2])->count());
/*$this->assertEquals((1+10)/2*10, Customer::find()->sum('status'));
$this->assertEquals((1+10)/2*10, Customer::find()->sum('status'));
$this->assertEquals((1+10)/2, Customer::find()->average('status'));
$this->assertEquals(1, Customer::find()->min('status'));
$this->assertEquals(10, Customer::find()->max('status'));*/
$this->assertEquals(10, Customer::find()->max('status'));
$this->assertEquals(range(1, 10), Customer::find()->distinct('status'));
// scope
$this->assertEquals(1, Customer::find()->activeOnly()->count());

Loading…
Cancel
Save