From 6491d7c8383c5660e1a39862ff7ad04ec287bd46 Mon Sep 17 00:00:00 2001 From: ThinkPHP Date: Fri, 19 Apr 2013 11:49:35 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0Mongo=E9=A9=B1=E5=8A=A8?= =?UTF-8?q?=E5=92=8C=E6=A8=A1=E5=9E=8B=E6=89=A9=E5=B1=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Think/Db/Driver/Mongo.php | 735 +++++++++++++++++++++++++++++ Library/Think/Model/MongoModel.php | 274 +++++++++++ 2 files changed, 1009 insertions(+) create mode 100644 Library/Think/Db/Driver/Mongo.php create mode 100644 Library/Think/Model/MongoModel.php diff --git a/Library/Think/Db/Driver/Mongo.php b/Library/Think/Db/Driver/Mongo.php new file mode 100644 index 00000000..87a909ff --- /dev/null +++ b/Library/Think/Db/Driver/Mongo.php @@ -0,0 +1,735 @@ + +// +---------------------------------------------------------------------- + +namespace Think\Db\Driver; +use Think\Db\Driver; + +/** + * Mongo数据库驱动 + */ +class Mongo extends Driver { + + protected $_mongo = null; // MongoDb Object + protected $_collection = null; // MongoCollection Object + protected $_dbName = ''; // dbName + protected $_collectionName = ''; // collectionName + protected $_cursor = null; // MongoCursor Object + protected $comparison = ['neq'=>'ne','ne'=>'ne','gt'=>'gt','egt'=>'gte','gte'=>'gte','lt'=>'lt','elt'=>'lte','lte'=>'lte','in'=>'in','not in'=>'nin','nin'=>'nin']; + + /** + * 架构函数 读取数据库配置信息 + * @access public + * @param array $config 数据库配置数组 + */ + public function __construct($config=''){ + if ( !class_exists('mongoClient') ) { + E(L('_NOT_SUPPERT_').':Mongo'); + } + if(!empty($config)) { + $this->config = array_merge($this->config,$config); + if(empty($this->config['params'])){ + $this->config['params'] = []; + } + } + } + + /** + * 连接数据库方法 + * @access public + */ + public function connect($config='',$linkNum=0) { + if ( !isset($this->linkID[$linkNum]) ) { + if(empty($config)) $config = $this->config['connection']; + $host = 'mongodb://'.($config['username']?"{$config['username']}":'').($config['password']?":{$config['password']}@":'').$config['hostname'].($config['hostport']?":{$config['hostport']}":'').'/'.($config['database']?"{$config['database']}":''); + try{ + $this->linkID[$linkNum] = new mongoClient( $host,$this->config['params']); + }catch (\MongoConnectionException $e){ + E($e->getmessage()); + } + } + return $this->linkID[$linkNum]; + } + + /** + * 切换当前操作的Db和Collection + * @access public + * @param string $collection collection + * @param string $db db + * @param boolean $master 是否主服务器 + * @return void + */ + public function switchCollection($collection,$db='',$master=true){ + // 当前没有连接 则首先进行数据库连接 + if ( !$this->_linkID ) $this->initConnect($master); + try{ + if(!empty($db)) { // 传人Db则切换数据库 + // 当前MongoDb对象 + $this->_dbName = $db; + $this->_mongo = $this->_linkID->selectDb($db); + } + // 当前MongoCollection对象 + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.getCollection('.$collection.')'; + } + if($this->_collectionName != $collection) { + $this->queryTimes++; + $this->debug(true); + $this->_collection = $this->_mongo->selectCollection($collection); + $this->debug(false); + $this->_collectionName = $collection; // 记录当前Collection名称 + } + }catch (MongoException $e){ + E($e->getMessage()); + } + } + + /** + * 释放查询结果 + * @access public + */ + public function free() { + $this->_cursor = null; + } + + /** + * 执行命令 + * @access public + * @param array $command 指令 + * @return array + */ + public function command($command=[]) { + $this->executeTimes++; + $this->debug(true); + $this->queryStr = 'command:'.json_encode($command); + $result = $this->_mongo->command($command); + $this->debug(false); + if(!$result['ok']) { + E($result['errmsg']); + } + return $result; + } + + /** + * 执行语句 + * @access public + * @param string $code sql指令 + * @param array $args 参数 + * @return mixed + */ + public function execute($code,$args=[]) { + $this->executeTimes++; + $this->debug(true); + $this->queryStr = 'execute:'.$code; + $result = $this->_mongo->execute($code,$args); + $this->debug(false); + if($result['ok']) { + return $result['retval']; + }else{ + E($result['errmsg']); + } + } + + /** + * 关闭数据库 + * @access public + */ + public function close() { + if($this->_linkID) { + $this->_linkID->close(); + $this->_linkID = null; + $this->_mongo = null; + $this->_collection = null; + $this->_cursor = null; + } + } + + /** + * 数据库错误信息 + * @access public + * @return string + */ + public function error() { + $this->error = $this->_mongo->lastError(); + Log::record($this->error,'ERR'); + return $this->error; + } + + /** + * 插入记录 + * @access public + * @param mixed $data 数据 + * @param array $options 参数表达式 + * @param boolean $replace 是否replace + * @return false | integer + */ + public function insert($data,$options=[],$replace=false) { + if(isset($options['table'])) { + $this->switchCollection($options['table']); + } + $this->model = $options['model']; + $this->executeTimes++; + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.insert('; + $this->queryStr .= $data?json_encode($data):'{}'; + $this->queryStr .= ')'; + } + try{ + $this->debug(true); + $result = $replace? $this->_collection->save($data): $this->_collection->insert($data); + $this->debug(false); + if($result) { + $_id = $data['_id']; + if(is_object($_id)) { + $_id = $_id->__toString(); + } + $this->lastInsID = $_id; + } + return $result; + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + } + + /** + * 插入多条记录 + * @access public + * @param array $dataList 数据 + * @param array $options 参数表达式 + * @return bool + */ + public function insertAll($dataList,$options=[]) { + if(isset($options['table'])) { + $this->switchCollection($options['table']); + } + $this->model = $options['model']; + $this->executeTimes++; + try{ + $this->debug(true); + $result = $this->_collection->batchInsert($dataList); + $this->debug(false); + return $result; + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + } + + /** + * 生成下一条记录ID 用于自增非MongoId主键 + * @access public + * @param string $pk 主键名 + * @return integer + */ + public function getMongoNextId($pk) { + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.find({},{'.$pk.':1}).sort({'.$pk.':-1}).limit(1)'; + } + try{ + $this->debug(true); + $result = $this->_collection->find([],[$pk=>1])->sort([$pk=>-1])->limit(1); + $this->debug(false); + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + $data = $result->getNext(); + return isset($data[$pk])?$data[$pk]+1:1; + } + + /** + * 更新记录 + * @access public + * @param mixed $data 数据 + * @param array $options 表达式 + * @return bool + */ + public function update($data,$options) { + if(isset($options['table'])) { + $this->switchCollection($options['table']); + } + $this->executeTimes++; + $this->model = $options['model']; + $query = $this->parseWhere($options['where']); + $set = $this->parseSet($data); + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.update('; + $this->queryStr .= $query?json_encode($query):'{}'; + $this->queryStr .= ','.json_encode($set).')'; + } + try{ + $this->debug(true); + if(isset($options['limit']) && $options['limit'] == 1) { + $multiple = ["multiple" => false]; + }else{ + $multiple = ["multiple" => true]; + } + $result = $this->_collection->update($query,$set,$multiple); + $this->debug(false); + return $result; + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + } + + /** + * 删除记录 + * @access public + * @param array $options 表达式 + * @return false | integer + */ + public function delete($options=[]) { + if(isset($options['table'])) { + $this->switchCollection($options['table']); + } + $query = $this->parseWhere($options['where']); + $this->model = $options['model']; + $this->executeTimes++; + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.remove('.json_encode($query).')'; + } + try{ + $this->debug(true); + $result = $this->_collection->remove($query); + $this->debug(false); + return $result; + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + } + + /** + * 清空记录 + * @access public + * @param array $options 表达式 + * @return false | integer + */ + public function clear($options=[]){ + if(isset($options['table'])) { + $this->switchCollection($options['table']); + } + $this->model = $options['model']; + $this->executeTimes++; + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.remove({})'; + } + try{ + $this->debug(true); + $result = $this->_collection->drop(); + $this->debug(false); + return $result; + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + } + + /** + * 查找记录 + * @access public + * @param array $options 表达式 + * @return iterator + */ + public function select($options=[]) { + if(isset($options['table'])) { + $this->switchCollection($options['table'],'',false); + } + $cache = isset($options['cache'])?$options['cache']:false; + if($cache) { // 查询缓存检测 + $key = is_string($cache['key'])?$cache['key']:md5(serialize($options)); + $value = S($key,'','',$cache['type']); + if(false !== $value) { + return $value; + } + } + $this->model = $options['model']; + $this->queryTimes++; + $query = $this->parseWhere($options['where']); + $field = $this->parseField($options['field']); + try{ + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.find('; + $this->queryStr .= $query? json_encode($query):'{}'; + $this->queryStr .= $field? ','.json_encode($field):''; + $this->queryStr .= ')'; + } + $this->debug(true); + $_cursor = $this->_collection->find($query,$field); + if($options['order']) { + $order = $this->parseOrder($options['order']); + if($this->config['debug']) { + $this->queryStr .= '.sort('.json_encode($order).')'; + } + $_cursor = $_cursor->sort($order); + } + if(isset($options['page'])) { // 根据页数计算limit + if(strpos($options['page'],',')) { + list($page,$length) = explode(',',$options['page']); + }else{ + $page = $options['page']; + } + $page = $page?$page:1; + $length = isset($length)?$length:(is_numeric($options['limit'])?$options['limit']:20); + $offset = $length*((int)$page-1); + $options['limit'] = $offset.','.$length; + } + if(isset($options['limit'])) { + list($offset,$length) = $this->parseLimit($options['limit']); + if(!empty($offset)) { + if($this->config['debug']) { + $this->queryStr .= '.skip('.intval($offset).')'; + } + $_cursor = $_cursor->skip(intval($offset)); + } + if($this->config['debug']) { + $this->queryStr .= '.limit('.intval($length).')'; + } + $_cursor = $_cursor->limit(intval($length)); + } + $this->debug(false); + $this->_cursor = $_cursor; + $resultSet = iterator_to_array($_cursor); + if($cache && $resultSet ) { // 查询缓存写入 + S($key,$resultSet,$cache['expire'],$cache['type']); + } + return $resultSet; + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + } + + /** + * 查找某个记录 + * @access public + * @param array $options 表达式 + * @return array + */ + public function find($options=[]){ + if(isset($options['table'])) { + $this->switchCollection($options['table'],'',false); + } + $cache = isset($options['cache'])?$options['cache']:false; + if($cache) { // 查询缓存检测 + $key = is_string($cache['key'])?$cache['key']:md5(serialize($options)); + $value = S($key,'','',$cache['type']); + if(false !== $value) { + return $value; + } + } + $this->model = $options['model']; + $this->queryTimes++; + $query = $this->parseWhere($options['where']); + $fields = $this->parseField($options['field']); + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.findOne('; + $this->queryStr .= $query?json_encode($query):'{}'; + $this->queryStr .= $fields?','.json_encode($fields):''; + $this->queryStr .= ')'; + } + try{ + $this->debug(true); + $result = $this->_collection->findOne($query,$fields); + $this->debug(false); + if($cache && $result ) { // 查询缓存写入 + S($key,$result,$cache['expire'],$cache['type']); + } + return $result; + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + } + + /** + * 统计记录数 + * @access public + * @param array $options 表达式 + * @return iterator + */ + public function count($options=[]){ + if(isset($options['table'])) { + $this->switchCollection($options['table'],'',false); + } + $this->model = $options['model']; + $this->queryTimes++; + $query = $this->parseWhere($options['where']); + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.'.$this->_collectionName; + $this->queryStr .= $query?'.find('.json_encode($query).')':''; + $this->queryStr .= '.count()'; + } + try{ + $this->debug(true); + $count = $this->_collection->count($query); + $this->debug(false); + return $count; + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + } + + public function group($keys,$initial,$reduce,$options=[]){ + $this->_collection->group($keys,$initial,$reduce,$options); + } + + /** + * 取得数据表的字段信息 + * @access public + * @return array + */ + public function getFields($collection=''){ + if(!empty($collection) && $collection != $this->_collectionName) { + $this->switchCollection($collection,'',false); + } + $this->queryTimes++; + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.'.$this->_collectionName.'.findOne()'; + } + try{ + $this->debug(true); + $result = $this->_collection->findOne(); + $this->debug(false); + } catch (\MongoCursorException $e) { + E($e->getMessage()); + } + if($result) { // 存在数据则分析字段 + $info = []; + foreach ($result as $key=>$val){ + $info[$key] = [ + 'name' => $key, + 'type' => getType($val), + ]; + } + return $info; + } + // 暂时没有数据 返回false + return false; + } + + /** + * 取得当前数据库的collection信息 + * @access public + */ + public function getTables(){ + if($this->config['debug']) { + $this->queryStr = $this->_dbName.'.getCollenctionNames()'; + } + $this->queryTimes++; + $this->debug(true); + $list = $this->_mongo->listCollections(); + $this->debug(false); + $info = []; + foreach ($list as $collection){ + $info[] = $collection->getName(); + } + return $info; + } + + /** + * set分析 + * @access protected + * @param array $data + * @return string + */ + protected function parseSet($data) { + $result = []; + foreach ($data as $key=>$val){ + if(is_array($val)) { + switch($val[0]) { + case 'inc': + $result['$inc'][$key] = (int)$val[1]; + break; + case 'set': + case 'unset': + case 'push': + case 'pushall': + case 'addtoset': + case 'pop': + case 'pull': + case 'pullall': + $result['$'.$val[0]][$key] = $val[1]; + break; + default: + $result['$set'][$key] = $val; + } + }else{ + $result['$set'][$key] = $val; + } + } + return $result; + } + + /** + * order分析 + * @access protected + * @param mixed $order + * @return array + */ + protected function parseOrder($order) { + if(is_string($order)) { + $array = explode(',',$order); + $order = []; + foreach ($array as $key=>$val){ + $arr = explode(' ',trim($val)); + if(isset($arr[1])) { + $arr[1] = $arr[1]=='asc'?1:-1; + }else{ + $arr[1] = 1; + } + $order[$arr[0]] = $arr[1]; + } + } + return $order; + } + + /** + * limit分析 + * @access protected + * @param mixed $limit + * @return array + */ + protected function parseLimit($limit) { + if(strpos($limit,',')) { + $array = explode(',',$limit); + }else{ + $array = [0,$limit]; + } + return $array; + } + + /** + * field分析 + * @access protected + * @param mixed $fields + * @return array + */ + public function parseField($fields){ + if(empty($fields)) { + $fields = []; + } + if(is_string($fields)) { + $fields = explode(',',$fields); + } + return $fields; + } + + /** + * where分析 + * @access protected + * @param mixed $where + * @return array + */ + public function parseWhere($where){ + $query = []; + foreach ($where as $key=>$val){ + if('_id' != $key && 0===strpos($key,'_')) { + // 解析特殊条件表达式 + $query = $this->parseThinkWhere($key,$val); + }else{ + // 查询字段的安全过滤 + if(!preg_match('/^[A-Z_\|\&\-.a-z0-9]+$/',trim($key))){ + E(L('_ERROR_QUERY_').':'.$key); + } + $key = trim($key); + if(strpos($key,'|')) { + $array = explode('|',$key); + $str = []; + foreach ($array as $k){ + $str[] = $this->parseWhereItem($k,$val); + } + $query['$or'] = $str; + }elseif(strpos($key,'&')){ + $array = explode('&',$key); + $str = []; + foreach ($array as $k){ + $str[] = $this->parseWhereItem($k,$val); + } + $query = array_merge($query,$str); + }else{ + $str = $this->parseWhereItem($key,$val); + $query = array_merge($query,$str); + } + } + } + return $query; + } + + /** + * 特殊条件分析 + * @access protected + * @param string $key + * @param mixed $val + * @return string + */ + protected function parseThinkWhere($key,$val) { + $query = []; + switch($key) { + case '_query': // 字符串模式查询条件 + parse_str($val,$query); + if(isset($query['_logic']) && strtolower($query['_logic']) == 'or' ) { + unset($query['_logic']); + $query['$or'] = $query; + } + break; + case '_string':// MongoCode查询 + $query['$where'] = new \MongoCode($val); + break; + } + return $query; + } + + /** + * where子单元分析 + * @access protected + * @param string $key + * @param mixed $val + * @return array + */ + protected function parseWhereItem($key,$val) { + $query = []; + if(is_array($val)) { + if(is_string($val[0])) { + $con = strtolower($val[0]); + if(in_array($con,['neq','ne','gt','egt','gte','lt','lte','elt'])) { // 比较运算 + $k = '$'.$this->comparison[$con]; + $query[$key] = [$k=>$val[1]]; + }elseif('like'== $con){ // 模糊查询 采用正则方式 + $query[$key] = new \MongoRegex("/".$val[1]."/"); + }elseif('mod'==$con){ // mod 查询 + $query[$key] = ['$mod'=>$val[1]]; + }elseif('regex'==$con){ // 正则查询 + $query[$key] = new \MongoRegex($val[1]); + }elseif(in_array($con,['in','nin','not in'])){ // IN NIN 运算 + $data = is_string($val[1])? explode(',',$val[1]):$val[1]; + $k = '$'.$this->comparison[$con]; + $query[$key] = [$k=>$data]; + }elseif('all'==$con){ // 满足所有指定条件 + $data = is_string($val[1])? explode(',',$val[1]):$val[1]; + $query[$key] = ['$all'=>$data]; + }elseif('between'==$con){ // BETWEEN运算 + $data = is_string($val[1])? explode(',',$val[1]):$val[1]; + $query[$key] = ['$gte'=>$data[0],'$lte'=>$data[1]]; + }elseif('not between'==$con){ + $data = is_string($val[1])? explode(',',$val[1]):$val[1]; + $query[$key] = ['$lt'=>$data[0],'$gt'=>$data[1]]; + }elseif('exp'==$con){ // 表达式查询 + $query['$where'] = new \MongoCode($val[1]); + }elseif('exists'==$con){ // 字段是否存在 + $query[$key] =['$exists'=>(bool)$val[1]]; + }elseif('size'==$con){ // 限制属性大小 + $query[$key] =['$size'=>intval($val[1])]; + }elseif('type'==$con){ // 限制字段类型 1 浮点型 2 字符型 3 对象或者MongoDBRef 5 MongoBinData 7 MongoId 8 布尔型 9 MongoDate 10 NULL 15 MongoCode 16 32位整型 17 MongoTimestamp 18 MongoInt64 如果是数组的话判断元素的类型 + $query[$key] =['$type'=>intval($val[1])]; + }else{ + $query[$key] = $val; + } + return $query; + } + } + $query[$key] = $val; + return $query; + } +} diff --git a/Library/Think/Model/MongoModel.php b/Library/Think/Model/MongoModel.php new file mode 100644 index 00000000..a3858e87 --- /dev/null +++ b/Library/Think/Model/MongoModel.php @@ -0,0 +1,274 @@ + +// +---------------------------------------------------------------------- +namespace Think\Model; +/** + * MongoModel模型类 + * 实现了ODM和ActiveRecords模式 + */ +class MongoModel extends \Think\Model{ + // 主键类型 + const TYPE_OBJECT = 1; + const TYPE_INT = 2; + const TYPE_STRING = 3; + + // 主键名称 + protected $pk = '_id'; + // _id 类型 1 Object 采用MongoId对象 2 Int 整形 支持自动增长 3 String 字符串Hash + protected $_idType = self::TYPE_OBJECT; + // 主键是否自动增长 支持Int型主键 + protected $_autoInc = false; + // Mongo默认关闭字段检测 可以动态追加字段 + protected $autoCheckFields = false; + + /** + * 利用__call方法实现一些特殊的Model方法 + * @access public + * @param string $method 方法名称 + * @param array $args 调用参数 + * @return mixed + */ + public function __call($method,$args) { + if(strtolower(substr($method,0,5))=='getby') { + // 根据某个字段获取记录 + $field = parse_name(substr($method,5)); + $where[$field] =$args[0]; + return $this->where($where)->find(); + }elseif(strtolower(substr($method,0,10))=='getfieldby') { + // 根据某个字段获取记录的某个值 + $name = parse_name(substr($method,10)); + $where[$name] =$args[0]; + return $this->where($where)->getField($args[1]); + }else{ + throw_exception(__CLASS__.':'.$method.L('_METHOD_NOT_EXIST_')); + return; + } + } + + // 写入数据前的回调方法 包括新增和更新 + protected function _before_write(&$data) { + $pk = $this->getPk(); + // 根据主键类型处理主键数据 + if(isset($data[$pk]) && $this->_idType == self::TYPE_OBJECT) { + $data[$pk] = new MongoId($data[$pk]); + } + } + + /** + * count统计 配合where连贯操作 + * @access public + * @return integer + */ + public function count(){ + // 分析表达式 + $options = $this->_parseOptions(); + return $this->db->count($options); + } + + /** + * 获取下一ID 用于自动增长型 + * @access public + * @param string $pk 字段名 默认为主键 + * @return mixed + */ + public function getMongoNextId($pk=''){ + if(empty($pk)) { + $pk = $this->getPk(); + } + return $this->db->getMongoNextId($pk); + } + + // 插入数据前的回调方法 + protected function _before_insert(&$data,$options) { + // 写入数据到数据库 + if($this->_autoInc && $this->_idType== self::TYPE_INT) { // 主键自动增长 + $pk = $this->getPk(); + if(!isset($data[$pk])) { + $data[$pk] = $this->db->mongo_next_id($pk); + } + } + } + + public function clear(){ + return $this->db->clear(); + } + + // 查询成功后的回调方法 + protected function _after_select(&$resultSet,$options) { + array_walk($resultSet,array($this,'checkMongoId')); + } + + /** + * 获取MongoId + * @access protected + * @param array $result 返回数据 + * @return array + */ + protected function checkMongoId(&$result){ + if(is_object($result['_id'])) { + $result['_id'] = $result['_id']->__toString(); + } + return $result; + } + + // 表达式过滤回调方法 + protected function _options_filter(&$options) { + $id = $this->getPk(); + if(isset($options['where'][$id]) && is_scalar($options['where'][$id]) && $this->_idType== self::TYPE_OBJECT) { + $options['where'][$id] = new MongoId($options['where'][$id]); + } + } + + /** + * 查询数据 + * @access public + * @param mixed $options 表达式参数 + * @return mixed + */ + public function find($options=array()) { + if( is_numeric($options) || is_string($options)) { + $id = $this->getPk(); + $where[$id] = $options; + $options = array(); + $options['where'] = $where; + } + // 分析表达式 + $options = $this->_parseOptions($options); + $result = $this->db->find($options); + if(false === $result) { + return false; + } + if(empty($result)) {// 查询结果为空 + return null; + }else{ + $this->checkMongoId($result); + } + $this->data = $result; + $this->_after_find($this->data,$options); + return $this->data; + } + + /** + * 字段值增长 + * @access public + * @param string $field 字段名 + * @param integer $step 增长值 + * @return boolean + */ + public function setInc($field,$step=1) { + return $this->setField($field,array('inc',$step)); + } + + /** + * 字段值减少 + * @access public + * @param string $field 字段名 + * @param integer $step 减少值 + * @return boolean + */ + public function setDec($field,$step=1) { + return $this->setField($field,array('inc','-'.$step)); + } + + /** + * 获取一条记录的某个字段值 + * @access public + * @param string $field 字段名 + * @param string $spea 字段数据间隔符号 + * @return mixed + */ + public function getField($field,$sepa=null) { + $options['field'] = $field; + $options = $this->_parseOptions($options); + if(strpos($field,',')) { // 多字段 + if(is_numeric($sepa)) {// 限定数量 + $options['limit'] = $sepa; + $sepa = null;// 重置为null 返回数组 + } + $resultSet = $this->db->select($options); + if(!empty($resultSet)) { + $_field = explode(',', $field); + $field = array_keys($resultSet[0]); + $key = array_shift($field); + $key2 = array_shift($field); + $cols = array(); + $count = count($_field); + foreach ($resultSet as $result){ + $name = $result[$key]; + if(2==$count) { + $cols[$name] = $result[$key2]; + }else{ + $cols[$name] = is_null($sepa)?$result:implode($sepa,$result); + } + } + return $cols; + } + }else{ + // 返回数据个数 + if(true !== $sepa) {// 当sepa指定为true的时候 返回所有数据 + $options['limit'] = is_numeric($sepa)?$sepa:1; + } // 查找一条记录 + $result = $this->db->find($options); + if(!empty($result)) { + if(1==$options['limit']) return reset($result[0]); + foreach ($result as $val){ + $array[] = $val[$field]; + } + return $array; + } + } + return null; + } + + /** + * 执行Mongo指令 + * @access public + * @param array $command 指令 + * @return mixed + */ + public function command($command) { + return $this->db->command($command); + } + + /** + * 执行MongoCode + * @access public + * @param string $code MongoCode + * @param array $args 参数 + * @return mixed + */ + public function mongoCode($code,$args=array()) { + return $this->db->execute($code,$args); + } + + // 数据库切换后回调方法 + protected function _after_db() { + // 切换Collection + $this->db->switchCollection($this->getTableName(),$this->dbName); + } + + /** + * 得到完整的数据表名 Mongo表名不带dbName + * @access public + * @return string + */ + public function getTableName() { + if(empty($this->trueTableName)) { + $tableName = !empty($this->tablePrefix) ? $this->tablePrefix : ''; + if(!empty($this->tableName)) { + $tableName .= $this->tableName; + }else{ + $tableName .= parse_name($this->name); + } + $this->trueTableName = strtolower($tableName); + } + return $this->trueTableName; + } +} \ No newline at end of file