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。