Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Yii Framework 2 mongodb extension Change Log
2.0.5 under development
-----------------------

- Enh #2022: Added support for querying on values using basic generic operators to `yii\mongodb\Query`

2.0.4 May 10, 2015
------------------
Expand Down
36 changes: 30 additions & 6 deletions Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -844,21 +844,45 @@ public function buildCondition($condition)
return [];
}
if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
$operator = strtoupper($condition[0]);
if (isset($builders[$operator])) {
$operator = $condition[0];
if (strncmp($operator, '$', 1) === 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this change for?

$method = 'buildSimpleCondition';
} elseif (isset($builders[strtoupper($operator)])) {
$operator = strtoupper($operator);
$method = $builders[$operator];
array_shift($condition);

return $this->$method($operator, $condition);
} else {
throw new InvalidParamException('Found unknown operator in query: ' . $operator);
}
array_shift($condition);
return $this->$method($operator, $condition);
} else {
// hash format: 'column1' => 'value1', 'column2' => 'value2', ...
return $this->buildHashCondition($condition);
}
}


/**
* Creates a condition on a column-value pair using an abritrary operator
*
* @param string $operator The Mongo operator. Note that Mongo query operators must start with an $ (e.g. `$gt`, `$lt`, etc.)
* @param array $operands The column-value pair
* @return array The generated Mongo condition
* @throws InvalidParamException if $operator does not start with $ or when wrong number of operands have been given.
*/
public function buildSimpleCondition($operator, $operands)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this method introduced?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is based on PR yiisoft/yii2#4552 's fix for issue yiisoft/yii2#2315, which did not seem to be ported to the mongodb extension.

Please also see my last comment in PR yiisoft/yii2#8505

If there's a better way I'm happy to hear it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not justify extra method creation.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not? The buildCondition method is a gateway method that calls specific conditionBuilders. Introducing the simple condition logic straight away into that method is not following single responsibility imho. But if it makes you happy I'll put the logic immediately in that method. Idk. Let me know what you want and I'll change it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is excatly the point. Original set of method was enough to compose all kind of MongoDB conditions, including comparisions.
That is why I am asking, why the extra method is need for this feature?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I guess I misunderstood you. Well. the current functionality does not allow for yii\db\QueryBuilder's syntax of calling the querybuilder like this: $collection->buildCondition(['$gt', 'column', $value]). This change allows for this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Such abillity is not provided because it is not suitable for MongoDB.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The extra method has a valid use case imo. The extension becomes more intuitive to use the more it's behavior mimics yii\db behavior. The extra method enables you to use syntax that's already available yii\db, but currently not in yii\mongodb.

But perhaps there's another reason why adding this method is a problem?

{
if (strncmp($operator, '$', 1) !== 0) {
throw new InvalidParamException('Invalid operator in query: ' . $operator);
}
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator '$operator' requires two operands.");
}

list($name, $value) = $operands;

return [$name => [$operator => $value]];
}

/**
* Creates a condition based on column-value pairs.
* @param array $condition the condition specification.
Expand Down
60 changes: 60 additions & 0 deletions Query.php
Original file line number Diff line number Diff line change
Expand Up @@ -414,4 +414,64 @@ private function composeSort()
}
return $sort;
}

/**
* Helper method for easy querying on values containing some common operators.
*
* The comparison operator is intelligently determined based on the first few characters in the given value and
* internally translated to a MongoDB operator.
* In particular, it recognizes the following operators if they appear as the leading characters in the given value:
* <: the column must be less than the given value ($lt).
* >: the column must be greater than the given value ($gt).
* <=: the column must be less than or equal to the given value ($lte).
* >=: the column must be greater than or equal to the given value ($gte).
* <>: the column must not be the same as the given value ($ne). Note that when $partialMatch is true, this would mean the value must not be a substring of the column.
* =: the column must be equal to the given value ($eq).
* none of the above: use the $defaultOperator
*
* Note that when the value is empty, no comparison expression will be added to the search condition.
*
* @param string $name column name
* @param scalar $value column value
* @param string $defaultOperator Defaults to =, performing an exact match.
* For example: use 'LIKE' or 'REGEX' for partial cq regex matching
* @see yii\mongodb\Collection::buildCondition()
* @return static The Query object itself
*/
public function andFilterCompare($name, $value, $defaultOperator = '=')
{
$matches=[];
if (preg_match("/^(<>|>=|>|<=|<|=)/", $value, $matches)) {
$op = $matches[1];
$value = substr($value, strlen($op));
} else {
$op = $defaultOperator;
}

$op = $this->normalizeOperator($op);

return $this->andFilterWhere([$op, $name, $value]);
}

/**
* Converts "\yii\db\*" quick condition operator into actual Mongo condition operator.
* @param string $key raw operator.
* @return string actual key.
*/
protected function normalizeOperator($key)
{
static $map = [
'=' => '$eq',
'>' => '$gt',
'>=' => '$gte',
'<' => '$lt',
'<=' => '$lte',
'<>' => '$ne',
];
if (array_key_exists($key, $map)) {
return $map[$key];
} else {
return $key;
}
}
}