You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							278 lines
						
					
					
						
							8.0 KiB
						
					
					
				
			
		
		
	
	
							278 lines
						
					
					
						
							8.0 KiB
						
					
					
				| <?php | |
| /** | |
|  * @link http://www.yiiframework.com/ | |
|  * @copyright Copyright (c) 2008 Yii Software LLC | |
|  * @license http://www.yiiframework.com/license/ | |
|  */ | |
|  | |
| namespace yii\mongo; | |
|  | |
| use yii\base\InvalidParamException; | |
| use yii\base\Object; | |
| use Yii; | |
|  | |
| /** | |
|  * Collection represents the Mongo collection information. | |
|  * | |
|  * @author Paul Klimov <klimov.paul@gmail.com> | |
|  * @since 2.0 | |
|  */ | |
| class Collection extends Object | |
| { | |
| 	/** | |
| 	 * @var \MongoCollection Mongo collection instance. | |
| 	 */ | |
| 	public $mongoCollection; | |
|  | |
| 	/** | |
| 	 * Drops this collection. | |
| 	 */ | |
| 	public function drop() | |
| 	{ | |
| 		$this->mongoCollection->drop(); | |
| 	} | |
|  | |
| 	/** | |
| 	 * @param array $condition | |
| 	 * @param array $fields | |
| 	 * @return \MongoCursor | |
| 	 */ | |
| 	public function find($condition = [], $fields = []) | |
| 	{ | |
| 		return $this->mongoCollection->find($this->buildCondition($condition), $fields); | |
| 	} | |
|  | |
| 	/** | |
| 	 * @param array $condition | |
| 	 * @param array $fields | |
| 	 * @return array | |
| 	 */ | |
| 	public function findAll($condition = [], $fields = []) | |
| 	{ | |
| 		$cursor = $this->find($condition, $fields); | |
| 		$result = []; | |
| 		foreach ($cursor as $data) { | |
| 			$result[] = $data; | |
| 		} | |
| 		return $result; | |
| 	} | |
|  | |
| 	/** | |
| 	 * 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 = 'Inserting data into ' . $this->mongoCollection->getName(); | |
| 		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 = 'Inserting batch data into ' . $this->mongoCollection->getName(); | |
| 		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. | |
| 	 * @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 = []) | |
| 	{ | |
| 		$token = 'Updating data in ' . $this->mongoCollection->getName(); | |
| 		Yii::info($token, __METHOD__); | |
| 		try { | |
| 			Yii::beginProfile($token, __METHOD__); | |
| 			$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]; | |
| 				} | |
| 			} | |
| 			$condition = $this->buildCondition($condition); | |
| 			$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 = 'Saving data into ' . $this->mongoCollection->getName(); | |
| 		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. | |
| 	 */ | |
| 	public function remove($condition = [], $options = []) | |
| 	{ | |
| 		$token = 'Removing data from ' . $this->mongoCollection->getName(); | |
| 		Yii::info($token, __METHOD__); | |
| 		try { | |
| 			Yii::beginProfile($token, __METHOD__); | |
| 			$options = array_merge(['w' => 1, 'multiple' => true], $options); | |
| 			$result = $this->mongoCollection->remove($this->buildCondition($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); | |
| 		} | |
| 	} | |
|  | |
| 	/** | |
| 	 * 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['err'])) { | |
| 				throw new Exception($result['errmsg'], (int)$result['code']); | |
| 			} | |
| 		} elseif (!$result) { | |
| 			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 { | |
| 				$key = $this->normalizeConditionKeyword($key); | |
| 				if (strncmp('$', $key, 1) !== 0 && is_array($actualValue) && array_key_exists(0, $actualValue)) { | |
| 					// shortcut for IN condition | |
| 					$result[$key]['$in'] = $actualValue; | |
| 				} else { | |
| 					$result[$key] = $actualValue; | |
| 				} | |
| 			} | |
| 		} | |
| 		return $result; | |
| 	} | |
| } |