Commit 9da7a80f by Paul Klimov

Mongo query condition composition composed.

parent 1a31b238
......@@ -7,6 +7,7 @@
namespace yii\mongo;
use yii\base\InvalidParamException;
use yii\base\Object;
use Yii;
......@@ -32,23 +33,23 @@ class Collection extends Object
}
/**
* @param array $query
* @param array $condition
* @param array $fields
* @return \MongoCursor
*/
public function find($query = [], $fields = [])
public function find($condition = [], $fields = [])
{
return $this->mongoCollection->find($query, $fields);
return $this->mongoCollection->find($this->buildCondition($condition), $fields);
}
/**
* @param array $query
* @param array $condition
* @param array $fields
* @return array
*/
public function findAll($query = [], $fields = [])
public function findAll($condition = [], $fields = [])
{
$cursor = $this->find($query, $fields);
$cursor = $this->find($condition, $fields);
$result = [];
foreach ($cursor as $data) {
$result[] = $data;
......@@ -102,19 +103,19 @@ class Collection extends Object
/**
* Updates the rows, which matches given criteria by given data.
* @param array $criteria description of the objects to update.
* @param array $condition description of the objects to update.
* @param array $newData the object with which to update the matching records.
* @param array $options list of options in format: optionName => optionValue.
* @return boolean whether operation was successful.
* @throws Exception on failure.
*/
public function update($criteria, $newData, $options = [])
public function update($condition, $newData, $options = [])
{
$token = 'Updating data in ' . $this->mongoCollection->getName();
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$this->mongoCollection->update($criteria, $newData, $options);
$this->mongoCollection->update($this->buildCondition($condition), $newData, $options);
Yii::endProfile($token, __METHOD__);
return true;
} catch (\Exception $e) {
......@@ -147,18 +148,18 @@ class Collection extends Object
/**
* Removes data from the collection.
* @param array $criteria description of records to remove.
* @param array $condition description of records to remove.
* @param array $options list of options in format: optionName => optionValue.
* @return boolean whether operation was successful.
* @throws Exception on failure.
*/
public function remove($criteria = [], $options = [])
public function remove($condition = [], $options = [])
{
$token = 'Removing data from ' . $this->mongoCollection->getName();
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$this->tryResultError($this->mongoCollection->remove($criteria, $options));
$this->tryResultError($this->mongoCollection->remove($this->buildCondition($condition), $options));
Yii::endProfile($token, __METHOD__);
return true;
} catch (\Exception $e) {
......@@ -182,4 +183,68 @@ class Collection extends Object
throw new Exception('Unknown error, use "w=1" option to enable error tracking');
}
}
/**
* Converts user friendly condition keyword into actual Mongo condition keyword.
* @param string $key raw condition key.
* @return string actual key.
*/
protected function normalizeConditionKeyword($key)
{
static $map = [
'or' => '$or',
'>' => '$gt',
'>=' => '$gte',
'<' => '$lt',
'<=' => '$lte',
'!=' => '$ne',
'<>' => '$ne',
'in' => '$in',
'not in' => '$nin',
'all' => '$all',
'size' => '$size',
'type' => '$type',
'exists' => '$exists',
'notexists' => '$exists',
'elemmatch' => '$elemMatch',
'mod' => '$mod',
'%' => '$mod',
'=' => '$$eq',
'==' => '$$eq',
'where' => '$where'
];
$key = strtolower($key);
if (array_key_exists($key, $map)) {
return $map[$key];
} else {
return $key;
}
}
/**
* Builds up Mongo condition from user friendly condition.
* @param array $condition raw condition.
* @return array normalized Mongo condition.
* @throws \yii\base\InvalidParamException on invalid condition given.
*/
public function buildCondition($condition)
{
if (!is_array($condition)) {
throw new InvalidParamException('Condition should be an array.');
}
$result = [];
foreach ($condition as $key => $value) {
if (is_array($value)) {
$actualValue = $this->buildCondition($value);
} else {
$actualValue = $value;
}
if (is_numeric($key)) {
$result[] = $actualValue;
} else {
$result[$this->normalizeConditionKeyword($key)] = $actualValue;
}
}
return $result;
}
}
\ No newline at end of file
......@@ -74,26 +74,72 @@ class Query extends Component implements QueryInterface
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'AND' operator.
* @param array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see where()
* @see orWhere()
*/
public function andWhere($condition)
{
if (is_array($this->where)) {
$this->where = array_merge($this->where, $condition);
} else {
$this->where = $condition;
}
return $this;
}
/**
* Adds an additional WHERE condition to the existing one.
* The new condition and the existing one will be joined using the 'OR' operator.
* @param array $condition the new WHERE condition. Please refer to [[where()]]
* on how to specify this parameter.
* @return static the query object itself
* @see where()
* @see andWhere()
*/
public function orWhere($condition)
{
if (is_array($this->where)) {
if (array_key_exists('or', $this->where) && count($this->where) == 1) {
$this->where['or'][] = $condition;
} else {
$this->where = [
'or' => [$this->where, $condition]
];
}
} else {
$this->where = $condition;
}
return $this;
}
/**
* @param Connection $db the database connection used to execute the query.
* @return \MongoCursor mongo cursor instance.
*/
protected function buildCursor($db = null)
{
// TODO: compose query
$query = [];
$where = $this->where;
if (!is_array($where)) {
$where = [];
}
$selectFields = [];
if (!empty($this->select)) {
foreach ($this->select as $fieldName) {
$selectFields[$fieldName] = true;
}
}
$cursor = $this->getCollection($db)->find($query, $selectFields);
$cursor = $this->getCollection($db)->find($where, $selectFields);
if (!empty($this->orderBy)) {
$sort = [];
foreach ($this->orderBy as $fieldName => $sortOrder) {
$sort[$fieldName] = $sortOrder === SORT_DESC ? \MongoCollection::DESCENDING : \MongoCollection::ASCENDING;
}
$cursor->sort($this->orderBy);
$cursor->sort($sort);
}
$cursor->limit($this->limit);
$cursor->skip($this->offset);
......@@ -109,17 +155,17 @@ class Query extends Component implements QueryInterface
public function all($db = null)
{
$cursor = $this->buildCursor($db);
if ($this->indexBy === null) {
return iterator_to_array($cursor);
} else {
$result = [];
foreach ($cursor as $row) {
if ($this->indexBy !== null) {
if (is_string($this->indexBy)) {
$key = $row[$this->indexBy];
} else {
$key = call_user_func($this->indexBy, $row);
}
$result[$key] = $row;
} else {
$result[] = $row;
}
}
return $result;
......
<?php
namespace yiiunit\extensions\mongo;
use yii\mongo\Query;
/**
* @group mongo
*/
class QueryRunTest extends MongoTestCase
{
protected function setUp()
{
parent::setUp();
$this->setUpTestRows();
}
protected function tearDown()
{
$this->dropCollection('customer');
parent::tearDown();
}
/**
* Sets up test rows.
*/
protected function setUpTestRows()
{
$collection = $this->getConnection()->getCollection('customer');
$rows = [];
for ($i = 1; $i <= 10; $i++) {
$rows[] = [
'name' => 'name' . $i,
'address' => 'address' . $i,
];
}
$collection->batchInsert($rows);
}
// Tests :
public function testAll()
{
$connection = $this->getConnection();
$query = new Query;
$rows = $query->from('customer')->all($connection);
$this->assertEquals(10, count($rows));
}
public function testDirectMatch()
{
$connection = $this->getConnection();
$query = new Query;
$rows = $query->from('customer')
->where(['name' => 'name1'])
->all($connection);
$this->assertEquals(1, count($rows));
$this->assertEquals('name1', $rows[0]['name']);
}
public function testIndexBy()
{
$connection = $this->getConnection();
$query = new Query;
$rows = $query->from('customer')
->indexBy('name')
->all($connection);
$this->assertEquals(10, count($rows));
$this->assertNotEmpty($rows['name1']);
}
public function testInCondition()
{
$connection = $this->getConnection();
$query = new Query;
$rows = $query->from('customer')
->where([
'name' => [
'in' => ['name1', 'name5']
]
])
->all($connection);
$this->assertEquals(2, count($rows));
$this->assertEquals('name1', $rows[0]['name']);
$this->assertEquals('name5', $rows[1]['name']);
}
public function testOrCondition()
{
$connection = $this->getConnection();
$query = new Query;
$rows = $query->from('customer')
->where(['name' => 'name1'])
->orWhere(['address' => 'address5'])
->all($connection);
$this->assertEquals(2, count($rows));
$this->assertEquals('name1', $rows[0]['name']);
$this->assertEquals('address5', $rows[1]['address']);
}
public function testOrder()
{
$connection = $this->getConnection();
$query = new Query;
$rows = $query->from('customer')
->orderBy(['name' => SORT_DESC])
->all($connection);
$this->assertEquals('name9', $rows[0]['name']);
}
}
\ No newline at end of file
......@@ -36,6 +36,88 @@ class QueryTest extends MongoTestCase
$this->assertEquals($from, $query->from);
}
public function testWhere()
{
$query = new Query;
$query->where(['name' => 'name1']);
$this->assertEquals(['name' => 'name1'], $query->where);
$query->andWhere(['address' => 'address1']);
$this->assertEquals(
[
'name' => 'name1',
'address' => 'address1'
],
$query->where
);
$query->orWhere(['name' => 'name2']);
$this->assertEquals(
[
'or' => [
[
'name' => 'name1',
'address' => 'address1'
],
['name' => 'name2']
]
],
$query->where
);
$query->orWhere(['name' => 'name3']);
$this->assertEquals(
[
'or' => [
[
'name' => 'name1',
'address' => 'address1'
],
['name' => 'name2'],
['name' => 'name3']
]
],
$query->where
);
$query->andWhere(['address' => 'address2']);
$this->assertEquals(
[
'or' => [
[
'name' => 'name1',
'address' => 'address1'
],
['name' => 'name2'],
['name' => 'name3']
],
'address' => 'address2'
],
$query->where
);
$query->orWhere(['name' => 'name4']);
$this->assertEquals(
[
'or' => [
[
'or' => [
[
'name' => 'name1',
'address' => 'address1'
],
['name' => 'name2'],
['name' => 'name3']
],
'address' => 'address2'
],
['name' => 'name4']
],
],
$query->where
);
}
public function testOrder()
{
$query = new Query;
......
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