Yii2 mongodb 扩展的where的条件加入大于 小于号
1. mongodb的where中有比较丰富的 条件,如下:
static $builders = [
"NOT" => "buildNotCondition",
"AND" => "buildAndCondition",
"OR" => "buildOrCondition",
"BETWEEN" => "buildBetweenCondition",
"NOT BETWEEN" => "buildBetweenCondition",
"IN" => "buildInCondition",
"NOT IN" => "buildInCondition",
"REGEX" => "buildRegexCondition",
"LIKE" => "buildLikeCondition",
];但是没有大于和小于等,因此我们需要加入大于,大于等于, 小于,小于等于
加入函数部分为:
在vendor/yiisoft/yii2-mongodb/collection.php
加入函数:
public function buildGtCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
return [$column => ["?gt" => $value]];
}
public function buildGteCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
return [$column => ["?gte" => $value]];
}
public function buildLtCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
return [$column => ["?lt" => $value]];
}
public function buildLteCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
return [$column => ["?lte" => $value]];
}buildCondition函数里面加入下面的部分代码 public function buildCondition($condition)
{
static $builders = [
"NOT" => "buildNotCondition",
"AND" => "buildAndCondition",
"OR" => "buildOrCondition",
"BETWEEN" => "buildBetweenCondition",
"NOT BETWEEN" => "buildBetweenCondition",
"IN" => "buildInCondition",
"NOT IN" => "buildInCondition",
"REGEX" => "buildRegexCondition",
"LIKE" => "buildLikeCondition",
">" => "buildGtCondition",
">=" => "buildGteCondition",
"<" => "buildLtCondition",
"<=" => "buildLteCondition",
];最后collection.php的代码如下:
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yiimongodb;
use yiiaseInvalidParamException;
use yiiaseObject;
use Yii;
/**
* Collection represents the Mongo collection information.
*
* A collection object is usually created by calling [[Database::getCollection()]] or [[Connection::getCollection()]].
*
* Collection provides the basic interface for the Mongo queries, mostly: insert, update, delete operations.
* For example:
*
* ~~~
* $collection = Yii::$app->mongodb->getCollection("customer");
* $collection->insert(["name" => "John Smith", "status" => 1]);
* ~~~
*
* To perform "find" queries, please use [[Query]] instead.
*
* Mongo uses JSON format to specify query conditions with quite specific syntax.
* However Collection class provides the ability of "translating" common condition format used "yiidb*"
* into Mongo condition.
* For example:
* ~~~
* $condition = [
* [
* "OR",
* ["AND", ["first_name" => "John"], ["last_name" => "Smith"]],
* ["status" => [1, 2, 3]]
* ],
* ];
* print_r($collection->buildCondition($condition));
* // outputs :
* [
* "$or" => [
* [
* "first_name" => "John",
* "last_name" => "John",
* ],
* [
* "status" => ["$in" => [1, 2, 3]],
* ]
* ]
* ]
* ~~~
*
* Note: condition values for the key "_id" will be automatically cast to [[MongoId]] instance,
* even if they are plain strings. However, if you have other columns, containing [[MongoId]], you
* should take care of possible typecast on your own.
*
* @property string $fullName Full name of this collection, including database name. This property is
* read-only.
* @property array $lastError Last error information. This property is read-only.
* @property string $name Name of this collection. This property is read-only.
*
* @author Paul Klimov <klimov.paul@gmail.com>
* @since 2.0
*/
class Collection extends Object
{
/**
* @var MongoCollection Mongo collection instance.
*/
public $mongoCollection;
/**
* @return string name of this collection.
*/
public function getName()
{
return $this->mongoCollection->getName();
}
/**
* @return string full name of this collection, including database name.
*/
public function getFullName()
{
return $this->mongoCollection->__toString();
}
/**
* @return array last error information.
*/
public function getLastError()
{
return $this->mongoCollection->db->lastError();
}
/**
* Composes log/profile token.
* @param string $command command name
* @param array $arguments command arguments.
* @return string token.
*/
protected function composeLogToken($command, $arguments = [])
{
$parts = [];
foreach ($arguments as $argument) {
$parts[] = is_scalar($argument) ? $argument : $this->encodeLogData($argument);
}
return $this->getFullName() . "." . $command . "(" . implode(", ", $parts) . ")";
}
/**
* Encodes complex log data into JSON format string.
* @param mixed $data raw data.
* @return string encoded data string.
*/
protected function encodeLogData($data)
{
return json_encode($this->processLogData($data));
}
/**
* Pre-processes the log data before sending it to `json_encode()`.
* @param mixed $data raw data.
* @return mixed the processed data.
*/
protected function processLogData($data)
{
if (is_object($data)) {
if ($data instanceof MongoId ||
$data instanceof MongoRegex ||
$data instanceof MongoDate ||
$data instanceof MongoInt32 ||
$data instanceof MongoInt64 ||
$data instanceof MongoTimestamp
) {
$data = get_class($data) . "(" . $data->__toString() . ")";
} elseif ($data instanceof MongoCode) {
$data = "MongoCode( " . $data->__toString() . " )";
} elseif ($data instanceof MongoBinData) {
$data = "MongoBinData(...)";
} elseif ($data instanceof MongoDBRef) {
$data = "MongoDBRef(...)";
} elseif ($data instanceof MongoMinKey || $data instanceof MongoMaxKey) {
$data = get_class($data);
} else {
$result = [];
foreach ($data as $name => $value) {
$result[$name] = $value;
}
$data = $result;
}
if ($data === []) {
return new stdClass();
}
}
if (is_array($data)) {
foreach ($data as $key => $value) {
if (is_array($value) || is_object($value)) {
$data[$key] = $this->processLogData($value);
}
}
}
return $data;
}
/**
* Drops this collection.
* @throws Exception on failure.
* @return boolean whether the operation successful.
*/
public function drop()
{
$token = $this->composeLogToken("drop");
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$result = $this->mongoCollection->drop();
$this->tryResultError($result);
Yii::endProfile($token, __METHOD__);
return true;
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Creates an index on the collection and the specified fields.
* @param array|string $columns column name or list of column names.
* If array is given, each element in the array has as key the field name, and as
* value either 1 for ascending sort, or -1 for descending sort.
* You can specify field using native numeric key with the field name as a value,
* in this case ascending sort will be used.
* For example:
* ~~~
* [
* "name",
* "status" => -1,
* ]
* ~~~
* @param array $options list of options in format: optionName => optionValue.
* @throws Exception on failure.
* @return boolean whether the operation successful.
*/
public function createIndex($columns, $options = [])
{
$columns = (array)$columns;
$keys = $this->normalizeIndexKeys($columns);
$token = $this->composeLogToken("createIndex", [$keys, $options]);
$options = array_merge(["w" => 1], $options);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
if (method_exists($this->mongoCollection, "createIndex")) {
$result = $this->mongoCollection->createIndex($keys, $options);
} else {
$result = $this->mongoCollection->ensureIndex($keys, $options);
}
$this->tryResultError($result);
Yii::endProfile($token, __METHOD__);
return true;
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Drop indexes for specified column(s).
* @param string|array $columns column name or list of column names.
* If array is given, each element in the array has as key the field name, and as
* value either 1 for ascending sort, or -1 for descending sort.
* Use value "text" to specify text index.
* You can specify field using native numeric key with the field name as a value,
* in this case ascending sort will be used.
* For example:
* ~~~
* [
* "name",
* "status" => -1,
* "description" => "text",
* ]
* ~~~
* @throws Exception on failure.
* @return boolean whether the operation successful.
*/
public function dropIndex($columns)
{
$columns = (array)$columns;
$keys = $this->normalizeIndexKeys($columns);
$token = $this->composeLogToken("dropIndex", [$keys]);
Yii::info($token, __METHOD__);
try {
$result = $this->mongoCollection->deleteIndex($keys);
$this->tryResultError($result);
return true;
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Compose index keys from given columns/keys list.
* @param array $columns raw columns/keys list.
* @return array normalizes index keys array.
*/
protected function normalizeIndexKeys($columns)
{
$keys = [];
foreach ($columns as $key => $value) {
if (is_numeric($key)) {
$keys[$value] = MongoCollection::ASCENDING;
} else {
$keys[$key] = $value;
}
}
return $keys;
}
/**
* Drops all indexes for this collection.
* @throws Exception on failure.
* @return integer count of dropped indexes.
*/
public function dropAllIndexes()
{
$token = $this->composeLogToken("dropIndexes");
Yii::info($token, __METHOD__);
try {
$result = $this->mongoCollection->deleteIndexes();
$this->tryResultError($result);
return $result["nIndexesWas"];
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Returns a cursor for the search results.
* In order to perform "find" queries use [[Query]] class.
* @param array $condition query condition
* @param array $fields fields to be selected
* @return MongoCursor cursor for the search results
* @see Query
*/
public function find($condition = [], $fields = [])
{
return $this->mongoCollection->find($this->buildCondition($condition), $fields);
}
/**
* Returns a single document.
* @param array $condition query condition
* @param array $fields fields to be selected
* @return array|null the single document. Null is returned if the query results in nothing.
* @see http://www.php.net/manual/en/mongocollection.findone.php
*/
public function findOne($condition = [], $fields = [])
{
return $this->mongoCollection->findOne($this->buildCondition($condition), $fields);
}
/**
* Updates a document and returns it.
* @param array $condition query condition
* @param array $update update criteria
* @param array $fields fields to be returned
* @param array $options list of options in format: optionName => optionValue.
* @return array|null the original document, or the modified document when $options["new"] is set.
* @throws Exception on failure.
* @see http://www.php.net/manual/en/mongocollection.findandmodify.php
*/
public function findAndModify($condition, $update, $fields = [], $options = [])
{
$condition = $this->buildCondition($condition);
$token = $this->composeLogToken("findAndModify", [$condition, $update, $fields, $options]);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$result = $this->mongoCollection->findAndModify($condition, $update, $fields, $options);
Yii::endProfile($token, __METHOD__);
return $result;
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Inserts new data into collection.
* @param array|object $data data to be inserted.
* @param array $options list of options in format: optionName => optionValue.
* @return MongoId new record id instance.
* @throws Exception on failure.
*/
public function insert($data, $options = [])
{
$token = $this->composeLogToken("insert", [$data]);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$options = array_merge(["w" => 1], $options);
$this->tryResultError($this->mongoCollection->insert($data, $options));
Yii::endProfile($token, __METHOD__);
return is_array($data) ? $data["_id"] : $data->_id;
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Inserts several new rows into collection.
* @param array $rows array of arrays or objects to be inserted.
* @param array $options list of options in format: optionName => optionValue.
* @return array inserted data, each row will have "_id" key assigned to it.
* @throws Exception on failure.
*/
public function batchInsert($rows, $options = [])
{
$token = $this->composeLogToken("batchInsert", [$rows]);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$options = array_merge(["w" => 1], $options);
$this->tryResultError($this->mongoCollection->batchInsert($rows, $options));
Yii::endProfile($token, __METHOD__);
return $rows;
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Updates the rows, which matches given criteria by given data.
* Note: for "multiple" mode Mongo requires explicit strategy "$set" or "$inc"
* to be specified for the "newData". If no strategy is passed "$set" will be used.
* @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 integer|boolean number of updated documents or whether operation was successful.
* @throws Exception on failure.
*/
public function update($condition, $newData, $options = [])
{
$condition = $this->buildCondition($condition);
$options = array_merge(["w" => 1, "multiple" => true], $options);
if ($options["multiple"]) {
$keys = array_keys($newData);
if (!empty($keys) && strncmp("$", $keys[0], 1) !== 0) {
$newData = ["$set" => $newData];
}
}
$token = $this->composeLogToken("update", [$condition, $newData, $options]);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$result = $this->mongoCollection->update($condition, $newData, $options);
$this->tryResultError($result);
Yii::endProfile($token, __METHOD__);
if (is_array($result) && array_key_exists("n", $result)) {
return $result["n"];
} else {
return true;
}
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Update the existing database data, otherwise insert this data
* @param array|object $data data to be updated/inserted.
* @param array $options list of options in format: optionName => optionValue.
* @return MongoId updated/new record id instance.
* @throws Exception on failure.
*/
public function save($data, $options = [])
{
$token = $this->composeLogToken("save", [$data]);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$options = array_merge(["w" => 1], $options);
$this->tryResultError($this->mongoCollection->save($data, $options));
Yii::endProfile($token, __METHOD__);
return is_array($data) ? $data["_id"] : $data->_id;
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Removes data from the collection.
* @param array $condition description of records to remove.
* @param array $options list of options in format: optionName => optionValue.
* @return integer|boolean number of updated documents or whether operation was successful.
* @throws Exception on failure.
* @see http://www.php.net/manual/en/mongocollection.remove.php
*/
public function remove($condition = [], $options = [])
{
$condition = $this->buildCondition($condition);
$options = array_merge(["w" => 1, "justOne" => false], $options);
$token = $this->composeLogToken("remove", [$condition, $options]);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$result = $this->mongoCollection->remove($condition, $options);
$this->tryResultError($result);
Yii::endProfile($token, __METHOD__);
if (is_array($result) && array_key_exists("n", $result)) {
return $result["n"];
} else {
return true;
}
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Returns a list of distinct values for the given column across a collection.
* @param string $column column to use.
* @param array $condition query parameters.
* @return array|boolean array of distinct values, or "false" on failure.
* @throws Exception on failure.
*/
public function distinct($column, $condition = [])
{
$condition = $this->buildCondition($condition);
$token = $this->composeLogToken("distinct", [$column, $condition]);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$result = $this->mongoCollection->distinct($column, $condition);
Yii::endProfile($token, __METHOD__);
return $result;
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Performs aggregation using Mongo Aggregation Framework.
* @param array $pipeline list of pipeline operators, or just the first operator
* @param array $pipelineOperator additional pipeline operator. You can specify additional
* pipelines via third argument, fourth argument etc.
* @return array the result of the aggregation.
* @throws Exception on failure.
* @see http://docs.mongodb.org/manual/applications/aggregation/
*/
public function aggregate($pipeline, $pipelineOperator = [])
{
$args = func_get_args();
$token = $this->composeLogToken("aggregate", $args);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$result = call_user_func_array([$this->mongoCollection, "aggregate"], $args);
$this->tryResultError($result);
Yii::endProfile($token, __METHOD__);
return $result["result"];
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Performs aggregation using Mongo "group" command.
* @param mixed $keys fields to group by. If an array or non-code object is passed,
* it will be the key used to group results. If instance of [[MongoCode]] passed,
* it will be treated as a function that returns the key to group by.
* @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.
* @throws Exception on failure.
* @see http://docs.mongodb.org/manual/reference/command/group/
*/
public function group($keys, $initial, $reduce, $options = [])
{
if (!($reduce instanceof MongoCode)) {
$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"]);
}
}
$token = $this->composeLogToken("group", [$keys, $initial, $reduce, $options]);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
// Avoid possible E_DEPRECATED for $options:
if (empty($options)) {
$result = $this->mongoCollection->group($keys, $initial, $reduce);
} else {
$result = $this->mongoCollection->group($keys, $initial, $reduce, $options);
}
$this->tryResultError($result);
Yii::endProfile($token, __METHOD__);
if (array_key_exists("retval", $result)) {
return $result["retval"];
} else {
return [];
}
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Performs aggregation using Mongo "map reduce" mechanism.
* Note: this function will not return the aggregation result, instead it will
* write it inside the another Mongo collection specified by "out" parameter.
* For example:
*
* ~~~
* $customerCollection = Yii::$app->mongo->getCollection("customer");
* $resultCollectionName = $customerCollection->mapReduce(
* "function () {emit(this.status, this.amount)}",
* "function (key, values) {return Array.sum(values)}",
* "mapReduceOut",
* ["status" => 3]
* );
* $query = new Query();
* $results = $query->from($resultCollectionName)->all();
* ~~~
*
* @param MongoCode|string $map function, which emits map data from collection.
* Argument will be automatically cast to [[MongoCode]].
* @param MongoCode|string $reduce function that takes two arguments (the map key
* and the map values) and does the aggregation.
* Argument will be automatically cast to [[MongoCode]].
* @param string|array $out output collection name. It could be a string for simple output
* ("outputCollection"), or an array for parametrized output (["merge" => "outputCollection"]).
* You can pass ["inline" => true] to fetch the result at once without temporary collection usage.
* @param array $condition criteria for including a document in the aggregation.
* @param array $options additional optional parameters to the mapReduce command. Valid options include:
* - sort - array - key to sort the input documents. The sort key must be in an existing index for this collection.
* - limit - the maximum number of documents to return in the collection.
* - finalize - function, which follows the reduce method and modifies the output.
* - scope - array - specifies global variables that are accessible in the map, reduce and finalize functions.
* - jsMode - boolean -Specifies whether to convert intermediate data into BSON format between the execution of the map and reduce functions.
* - verbose - boolean - specifies whether to include the timing information in the result information.
* @return string|array the map reduce output collection name or output results.
* @throws Exception on failure.
*/
public function mapReduce($map, $reduce, $out, $condition = [], $options = [])
{
if (!($map instanceof MongoCode)) {
$map = new MongoCode((string) $map);
}
if (!($reduce instanceof MongoCode)) {
$reduce = new MongoCode((string) $reduce);
}
$command = [
"mapReduce" => $this->getName(),
"map" => $map,
"reduce" => $reduce,
"out" => $out
];
if (!empty($condition)) {
$command["query"] = $this->buildCondition($condition);
}
if (array_key_exists("finalize", $options)) {
if (!($options["finalize"] instanceof MongoCode)) {
$options["finalize"] = new MongoCode((string) $options["finalize"]);
}
}
if (!empty($options)) {
$command = array_merge($command, $options);
}
$token = $this->composeLogToken("mapReduce", [$map, $reduce, $out]);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$command = array_merge(["mapReduce" => $this->getName()], $command);
$result = $this->mongoCollection->db->command($command);
$this->tryResultError($result);
Yii::endProfile($token, __METHOD__);
return array_key_exists("results", $result) ? $result["results"] : $result["result"];
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Performs full text search.
* @param string $search string of terms that MongoDB parses and uses to query the text index.
* @param array $condition criteria for filtering a results list.
* @param array $fields list of fields to be returned in result.
* @param array $options additional optional parameters to the mapReduce command. Valid options include:
* - limit - the maximum number of documents to include in the response (by default 100).
* - language - the language that determines the list of stop words for the search
* and the rules for the stemmer and tokenizer. If not specified, the search uses the default
* language of the index.
* @return array the highest scoring documents, in descending order by score.
* @throws Exception on failure.
*/
public function fullTextSearch($search, $condition = [], $fields = [], $options = [])
{
$command = [
"search" => $search
];
if (!empty($condition)) {
$command["filter"] = $this->buildCondition($condition);
}
if (!empty($fields)) {
$command["project"] = $fields;
}
if (!empty($options)) {
$command = array_merge($command, $options);
}
$token = $this->composeLogToken("text", $command);
Yii::info($token, __METHOD__);
try {
Yii::beginProfile($token, __METHOD__);
$command = array_merge(["text" => $this->getName()], $command);
$result = $this->mongoCollection->db->command($command);
$this->tryResultError($result);
Yii::endProfile($token, __METHOD__);
return $result["results"];
} catch (Exception $e) {
Yii::endProfile($token, __METHOD__);
throw new Exception($e->getMessage(), (int) $e->getCode(), $e);
}
}
/**
* Checks if command execution result ended with an error.
* @param mixed $result raw command execution result.
* @throws Exception if an error occurred.
*/
protected function tryResultError($result)
{
if (is_array($result)) {
if (!empty($result["errmsg"])) {
$errorMessage = $result["errmsg"];
} elseif (!empty($result["err"])) {
$errorMessage = $result["err"];
}
if (isset($errorMessage)) {
if (array_key_exists("code", $result)) {
$errorCode = (int) $result["code"];
} elseif (array_key_exists("ok", $result)) {
$errorCode = (int) $result["ok"];
} else {
$errorCode = 0;
}
throw new Exception($errorMessage, $errorCode);
}
} elseif (!$result) {
throw new Exception("Unknown error, use "w=1" option to enable error tracking");
}
}
/**
* Throws an exception if there was an error on the last operation.
* @throws Exception if an error occurred.
*/
protected function tryLastError()
{
$this->tryResultError($this->getLastError());
}
/**
* Converts "yiidb*" quick condition keyword into actual Mongo condition keyword.
* @param string $key raw condition key.
* @return string actual key.
*/
protected function normalizeConditionKeyword($key)
{
static $map = [
"AND" => "$and",
"OR" => "$or",
"IN" => "$in",
"NOT IN" => "$nin",
];
$matchKey = strtoupper($key);
if (array_key_exists($matchKey, $map)) {
return $map[$matchKey];
} else {
return $key;
}
}
/**
* Converts given value into [[MongoId]] instance.
* If array given, each element of it will be processed.
* @param mixed $rawId raw id(s).
* @return array|MongoId normalized id(s).
*/
protected function ensureMongoId($rawId)
{
if (is_array($rawId)) {
$result = [];
foreach ($rawId as $key => $value) {
$result[$key] = $this->ensureMongoId($value);
}
return $result;
} elseif (is_object($rawId)) {
if ($rawId instanceof MongoId) {
return $rawId;
} else {
$rawId = (string) $rawId;
}
}
try {
$mongoId = new MongoId($rawId);
} catch (MongoException $e) {
// invalid id format
$mongoId = $rawId;
}
return $mongoId;
}
public function buildGtCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
return [$column => ["?gt" => $value]];
}
public function buildGteCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
return [$column => ["?gte" => $value]];
}
public function buildLtCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
return [$column => ["?lt" => $value]];
}
public function buildLteCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
return [$column => ["?lte" => $value]];
}
/**
* Parses the condition specification and generates the corresponding Mongo condition.
* @param array $condition the condition specification. Please refer to [[Query::where()]]
* on how to specify a condition.
* @return array the generated Mongo condition
* @throws InvalidParamException if the condition is in bad format
*/
public function buildCondition($condition)
{
static $builders = [
"NOT" => "buildNotCondition",
"AND" => "buildAndCondition",
"OR" => "buildOrCondition",
"BETWEEN" => "buildBetweenCondition",
"NOT BETWEEN" => "buildBetweenCondition",
"IN" => "buildInCondition",
"NOT IN" => "buildInCondition",
"REGEX" => "buildRegexCondition",
"LIKE" => "buildLikeCondition",
">" => "buildGtCondition",
">=" => "buildGteCondition",
"<" => "buildLtCondition",
"<=" => "buildLteCondition",
];
if (!is_array($condition)) {
throw new InvalidParamException("Condition should be an array.");
} elseif (empty($condition)) {
return [];
}
if (isset($condition[0])) { // operator format: operator, operand 1, operand 2, ...
$operator = strtoupper($condition[0]);
if (isset($builders[$operator])) {
$method = $builders[$operator];
array_shift($condition);
return $this->$method($operator, $condition);
} else {
throw new InvalidParamException("Found unknown operator in query: " . $operator);
}
} else {
// hash format: "column1" => "value1", "column2" => "value2", ...
return $this->buildHashCondition($condition);
}
}
/**
* Creates a condition based on column-value pairs.
* @param array $condition the condition specification.
* @return array the generated Mongo condition.
*/
public function buildHashCondition($condition)
{
$result = [];
foreach ($condition as $name => $value) {
if (strncmp("$", $name, 1) === 0) {
// Native Mongo condition:
$result[$name] = $value;
} else {
if (is_array($value)) {
if (array_key_exists(0, $value)) {
// Quick IN condition:
$result = array_merge($result, $this->buildInCondition("IN", [$name, $value]));
} else {
// Mongo complex condition:
$result[$name] = $value;
}
} else {
// Direct match:
if ($name == "_id") {
$value = $this->ensureMongoId($value);
}
$result[$name] = $value;
}
}
}
return $result;
}
/**
* Composes `NOT` condition.
* @param string $operator the operator to use for connecting the given operands
* @param array $operands the Mongo conditions to connect.
* @return array the generated Mongo condition.
* @throws InvalidParamException if wrong number of operands have been given.
*/
public function buildNotCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($name, $value) = $operands;
$result = [];
if (is_array($value)) {
$result[$name] = ["$not" => $this->buildCondition($value)];
} else {
if ($name == "_id") {
$value = $this->ensureMongoId($value);
}
$result[$name] = ["$ne" => $value];
}
return $result;
}
/**
* Connects two or more conditions with the `AND` operator.
* @param string $operator the operator to use for connecting the given operands
* @param array $operands the Mongo conditions to connect.
* @return array the generated Mongo condition.
*/
public function buildAndCondition($operator, $operands)
{
$operator = $this->normalizeConditionKeyword($operator);
$parts = [];
foreach ($operands as $operand) {
$parts[] = $this->buildCondition($operand);
}
return [$operator => $parts];
}
/**
* Connects two or more conditions with the `OR` operator.
* @param string $operator the operator to use for connecting the given operands
* @param array $operands the Mongo conditions to connect.
* @return array the generated Mongo condition.
*/
public function buildOrCondition($operator, $operands)
{
$operator = $this->normalizeConditionKeyword($operator);
$parts = [];
foreach ($operands as $operand) {
$parts[] = $this->buildCondition($operand);
}
return [$operator => $parts];
}
/**
* Creates an Mongo condition, which emulates the `BETWEEN` operator.
* @param string $operator the operator to use
* @param array $operands the first operand is the column name. The second and third operands
* describe the interval that column value should be in.
* @return array the generated Mongo condition.
* @throws InvalidParamException if wrong number of operands have been given.
*/
public function buildBetweenCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1], $operands[2])) {
throw new InvalidParamException("Operator "$operator" requires three operands.");
}
list($column, $value1, $value2) = $operands;
if (strncmp("NOT", $operator, 3) === 0) {
return [
$column => [
"$lt" => $value1,
"$gt" => $value2,
]
];
} else {
return [
$column => [
"$gte" => $value1,
"$lte" => $value2,
]
];
}
}
/**
* Creates an Mongo condition with the `IN` operator.
* @param string $operator the operator to use (e.g. `IN` or `NOT IN`)
* @param array $operands the first operand is the column name. If it is an array
* a composite IN condition will be generated.
* The second operand is an array of values that column value should be among.
* @return array the generated Mongo condition.
* @throws InvalidParamException if wrong number of operands have been given.
*/
public function buildInCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $values) = $operands;
$values = (array) $values;
if (!is_array($column)) {
$columns = [$column];
$values = [$column => $values];
} elseif (count($column) < 2) {
$columns = $column;
$values = [$column[0] => $values];
} else {
$columns = $column;
}
$operator = $this->normalizeConditionKeyword($operator);
$result = [];
foreach ($columns as $column) {
if ($column == "_id") {
$inValues = $this->ensureMongoId($values[$column]);
} else {
$inValues = $values[$column];
}
$result[$column][$operator] = array_values($inValues);
}
return $result;
}
/**
* Creates a Mongo regular expression condition.
* @param string $operator the operator to use
* @param array $operands the first operand is the column name.
* The second operand is a single value that column value should be compared with.
* @return array the generated Mongo condition.
* @throws InvalidParamException if wrong number of operands have been given.
*/
public function buildRegexCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
if (!($value instanceof MongoRegex)) {
$value = new MongoRegex($value);
}
return [$column => $value];
}
/**
* Creates a Mongo condition, which emulates the `LIKE` operator.
* @param string $operator the operator to use
* @param array $operands the first operand is the column name.
* The second operand is a single value that column value should be compared with.
* @return array the generated Mongo condition.
* @throws InvalidParamException if wrong number of operands have been given.
*/
public function buildLikeCondition($operator, $operands)
{
if (!isset($operands[0], $operands[1])) {
throw new InvalidParamException("Operator "$operator" requires two operands.");
}
list($column, $value) = $operands;
if (!($value instanceof MongoRegex)) {
$value = new MongoRegex("/" . preg_quote($value) . "/i");
}
return [$column => $value];
}
}
声明:该文观点仅代表作者本人,入门客AI创业平台信息发布平台仅提供信息存储空间服务,如有疑问请联系rumenke@qq.com。
