Commit 0e082c17 by Paul Klimov

Aggregation functions added to Mongo Query.

parent da88e3db
...@@ -202,9 +202,11 @@ class Collection extends Object ...@@ -202,9 +202,11 @@ class Collection extends Object
} }
/** /**
* @param $pipeline * Performs aggregation using Mongo Aggregation Framework.
* @param array $pipelineOperator * @param array $pipeline list of pipeline operators, or just the first operator
* @return array * @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 = []) public function aggregate($pipeline, $pipelineOperator = [])
{ {
...@@ -213,20 +215,30 @@ class Collection extends Object ...@@ -213,20 +215,30 @@ class Collection extends Object
} }
/** /**
* Performs aggregation using Mongo Map Reduce mechanism.
* @param mixed $keys * @param mixed $keys
* @param array $initial * @param array $initial Initial value of the aggregation counter object.
* @param \MongoCode|string $reduce * @param \MongoCode|string $reduce function that takes two arguments (the current
* @param array $options * document and the aggregation to this point) and does the aggregation.
* @return array * 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 = []) public function mapReduce($keys, $initial, $reduce, $options = [])
{ {
if (!($reduce instanceof \MongoCode)) { if (!($reduce instanceof \MongoCode)) {
$reduce = new \MongoCode($reduce); $reduce = new \MongoCode((string)$reduce);
} }
if (array_key_exists('condition', $options)) { if (array_key_exists('condition', $options)) {
$options['condition'] = $this->buildCondition($options['condition']); $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); return $this->mongoCollection->group($keys, $initial, $reduce, $options);
} }
......
...@@ -105,8 +105,8 @@ class Query extends Component implements QueryInterface ...@@ -105,8 +105,8 @@ class Query extends Component implements QueryInterface
/** /**
* Executes the query and returns all results as an array. * Executes the query and returns all results as an array.
* @param Connection $db the database connection used to execute the query. * @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `db` application component will be used. * 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. * @return array the query results. If the query results in nothing, an empty array will be returned.
*/ */
public function all($db = null) public function all($db = null)
...@@ -130,8 +130,8 @@ class Query extends Component implements QueryInterface ...@@ -130,8 +130,8 @@ class Query extends Component implements QueryInterface
/** /**
* Executes the query and returns a single row of result. * Executes the query and returns a single row of result.
* @param Connection $db the database connection used to execute the query. * @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `db` application component will be used. * 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 * @return array|boolean the first row (in terms of an array) of the query result. False is returned if the query
* results in nothing. * results in nothing.
*/ */
...@@ -148,8 +148,8 @@ class Query extends Component implements QueryInterface ...@@ -148,8 +148,8 @@ class Query extends Component implements QueryInterface
/** /**
* Returns the number of records. * Returns the number of records.
* @param string $q the COUNT expression. Defaults to '*'. * @param string $q the COUNT expression. Defaults to '*'.
* @param Connection $db the database connection used to execute the query. * @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `db` application component will be used. * If this parameter is not given, the `mongo` application component will be used.
* @return integer number of records * @return integer number of records
*/ */
public function count($q = '*', $db = null) public function count($q = '*', $db = null)
...@@ -160,12 +160,117 @@ class Query extends Component implements QueryInterface ...@@ -160,12 +160,117 @@ class Query extends Component implements QueryInterface
/** /**
* Returns a value indicating whether the query result contains any row of data. * Returns a value indicating whether the query result contains any row of data.
* @param Connection $db the database connection used to execute the query. * @param Connection $db the Mongo connection used to execute the query.
* If this parameter is not given, the `db` application component will be used. * If this parameter is not given, the `mongo` application component will be used.
* @return boolean whether the query result contains any row of data. * @return boolean whether the query result contains any row of data.
*/ */
public function exists($db = null) public function exists($db = null)
{ {
return $this->one($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;
}
}
} }
\ No newline at end of file
...@@ -83,13 +83,14 @@ class ActiveRecordTest extends MongoTestCase ...@@ -83,13 +83,14 @@ class ActiveRecordTest extends MongoTestCase
$this->assertTrue($customer instanceof Customer); $this->assertTrue($customer instanceof Customer);
$this->assertEquals(4, $customer->status); $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(10, Customer::find()->count());
$this->assertEquals(1, Customer::find()->where(['status' => 2])->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+10)/2, Customer::find()->average('status'));
$this->assertEquals(1, Customer::find()->min('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 // scope
$this->assertEquals(1, Customer::find()->activeOnly()->count()); $this->assertEquals(1, Customer::find()->activeOnly()->count());
......
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