<?php
/**
* mongo 类
* User: wujp <wujp@nalashop.com>
* Date: 13-10-12
* Time: 下午5:48
*/
class Mongo_DB
{
#链接
public $conn = null;
#mongodb
public $db = null;
#链接参数
private $url = '';
#判断db是否链接
private $collected = null;
#查询、更新 映射 有|
private $four_map = array( #值是字符串,值不用处理
'>' => '$gt',
'>=' => '$gte',
'<' => '$le',
'<=' => '$lte',
'!=' => '$ne',
'@' => '$exists', #键是否存在
'?' => 'perl', #正则
);
private $ele_map = array( #数组,值需要处理
'~' => '$elemMatch' #内嵌文档
);
private $log_map = array( #值必须是数组
'^' => '$in',
'!^' => '$nin',
'*' => '$all', #指定数组元素
);
#没有|
private $lgi_map = array( #必须是二维数组
'$' => '$or',
'&' => '$and',
);
private $where_map = array(
'%' => '$where', #javascript 函数
);
private $sort_map = array(
'DESC' => -1,
'ASC' => 1
);
#当前db下所有集合=>键数组
public $collections = null;
#基本配置参数
private $config = array(
'host' => 'localhost',
'port' => '27017',
'dbname' => '',
'user' => '',
'pass' => '',
'logpath' => '', #日志目录
'replicaset' => array( #集群
'host' => array()
// 'host' => array('host:port'), #主机群
// 'options' => array('readPreference', 'readPreferenceTags', 'replicaSet','slaveok')
),
);
/**
* 构造函数,基本参数设置
* @param $config
*
*/
public function __construct($config)
{
if (is_array($config) && $config) {
foreach ($this->config as $k => $v) {
if (isset($config[$k]) && $config[$k])
$this->config[$k] = !is_array($config[$k]) ? trim($config[$k]) : $config[$k];
}
$this->connect();
}
}
/**
* 初始化mongo
*
*/
private function connect()
{
if (!extension_loaded('mongo'))
exit('ERROR:MONGO EXTENSION NOT LOAD');
if ($this->conn === NULL) {
try {
if ($this->config['replicaset']['host']) {
#todo 主从配置,或者副本集,读扩展,
} else {
$this->url .= "mongodb://{$this->config['user']}:{$this->config['pass']}@{$this->config['host']}:
{$this->config['port']}/{$this->config['dbname']}";
$this->conn = new MongoClient($this->url);
}
$this->db = $this->conn->selectDB($this->config['dbname']);
if ($this->db) {
$this->collected = true;
$this->GetAllCollection(); #获取所有集合列表
}
} catch (MongoConnectionException $e) {
$this->ErrLog($e->getMessage());
exit('MONGO CONNECT ERROR');
}
}
}
/**
* 析构函数
*/
public function __destruct()
{
$this->collected = $this->collections = $this->db = $this->conn = null;
}
/**
* 获取db下所有集合名称
*
* @internal param $dbname
* @return bool
*/
private function GetAllCollection()
{
if (!$this->collected)
return false;
$colls = $this->db->listCollections();
if ($colls) {
foreach ($colls as $v) {
$pos = strpos($v, $this->config['dbname']);
if ($pos !== false)
$this->collections[] = substr($v, ($pos + strlen($this->config['dbname']) + 1));
}
return true;
} else {
return false;
}
}
/**
* 初始化集合对象
* @param $collname 集合名
* @return mixed
*/
public function GetMonCollection($collname)
{
if (!$this->collected)
return false;
return $this->db->selectCollection($collname);
}
/**
* 转换字符编码
* @param $array
* @return bool
*/
private function ConvertEncode($array)
{
if (is_array($array)) {
foreach ($array as &$v) {
if (is_array($v)) {
$v = $this->ConvertEncode($v);
if (!$v)
return false;
} else {
$code = mb_detect_encoding($v, array('UTF-8', 'ASCII', 'GB2312', 'GBK', 'CP936'), true);
if ($code !== 'UTF-8')
$v = mb_convert_encoding($v, 'UTF-8', $code);
}
}
return $array;
}
return false;
}
/**
* 获取where参数
* @param $where
* @param bool $type
* @return string
*
*/
private function GetWhere($where, $type = false)
{
$wheres = array();
$maps = array('four_map', 'log_map', 'ele_map');
$maps_np = array_merge($this->lgi_map, $this->where_map);
if (is_array($where) && $where) { #过滤查询条件
foreach ($where as $field => $val) {
$pos = strpos($field, '|');
if ($pos !== false) { #四则、正则、函数、数组、in、多重条件内嵌文档
$tep = substr($field, 0, $pos);
$key = substr($field, ($pos + 1));
if ($key !== false) {
foreach ($maps as $v) {
$arr = $this->$v;
if (in_array($tep, array_keys($arr))) {
if ($v == 'ele_map' && is_array($val))
$val = $this->GetWhere($val, true);
if ($tep == '?') { #正则
$val = new MongoRegex($val);
$wheres[$key] = $val;
} else {
$wheres[$key][$arr[$tep]] = $val;
}
}
}
}
} elseif (in_array($field, array_keys($maps_np))) {
if (in_array($field, array_keys($this->lgi_map)) && is_array($val)) { #逻辑
foreach ($val as $v) {
$val = $this->GetWhere($v, true);
$wheres[$maps_np[$field]][] = $val;
}
} else {
$wheres[$maps_np[$field]] = $val;
}
} else { #普通查询、单一条件内嵌文档
if (strpos($field, "[") !== false)
$field = str_replace("[", ".", $field);
if (is_array($val) && $type) {
$arr = $this->GetWhere($val);
foreach ($arr as $k => $v) {
$wheres[$k][] = $v;
}
} elseif (is_null($val)) {
$wheres[$field] = array('$in' => array(null), '$exists' => 1);
} else {
$wheres[$field] = $val; #支持 array('age'=>array('>'=>'18','<='=>'20'))
}
}
}
}
return $wheres;
}
/**
* 插入一行
* @param $collect
* @param $value
* @return bool
*/
public function InsOne($collect, $value)
{
if (!$this->collected || !is_array($value))
return false;
$id = array_search('id', array_keys($value)); #处理有id字段的情况
if ($id !== false && array_search('_id', array_keys($value)) === false) {
$value['_id'] = $value['id'];
unset($value['id']);
}
$value = $this->ConvertEncode($value);
if (!$value)
return false;
try {
$result = $this->GetMonCollection($collect)->insert($value);
$result = $result['err'] ? false : true;
} catch (MongoException $e) {
$this->ErrLog($e->getMessage());
return false;
}
return $result;
}
/**
* 插入多行
* @param $collect 集合
* @param $fields
* @param $values 键值对:array(0=>array(''=>''))
* @param bool $continueOnError 是否忽略插入错误,默认忽略
* @return bool
*/
public function InsMulit($collect, $fields, $values, $continueOnError = false)
{
if (!is_array($fields) || !is_array($values) || !$this->collected)
return false;
$id = array_search('id', $fields); #处理有id字段的情况
if ($id !== false && array_search('_id', $fields) === false)
$fields[$id] = '_id';
$data = array();
$values = $this->ConvertEncode($values);
if ($values) {
foreach ($values as $v) {
if (is_array($v)) {
$v = array_combine($fields, $v);
if ($v)
$data[] = $v;
}
}
}
if (!$data)
return false;
$option['continueOnError'] = $continueOnError ? true : false;
try {
$result = $this->GetMonCollection($collect)->batchinsert($data, $option);
$result = $result['err'] ? false : true;
} catch (MongoException $e) {
$this->ErrLog($e->getMessage());
return false;
}
return $result;
}
/**
* 查询一个
* @param $collect
* @param array $where
* @param $field string
* @return bool
*/
function FindOne($collect, $where = array(), $field)
{
if (!$this->collected)
return false;
$wheres = $this->GetWhere($where);
if ($where && !$wheres)
return false;
if (is_string($field) && trim($field))
$field = trim($field);
else
return false;
$result = $this->GetMonCollection($collect)->findOne($wheres, array($field));
if ($result) {
$arr = get_object_vars($result['_id']);
$result['_id'] = $arr['$id'];
return $result[$field];
}
return false;
}
/**
* 查询一条
* @param $collect
* @param array|string $where
* @param array $fields
* @param string $type
* @return array|bool
*/
function FindRow($collect, $where = array(), $fields = array(), $type = 'assoc')
{
if (!$this->collected)
return false;
$wheres = $this->GetWhere($where);
if ($where && !$wheres)
return false;
if ($fields && is_array($fields)) { #过滤fields
$val = '';
for ($i = 0; $i < count($fields); $i++) {
$val[] = 1;
}
$fields = array_combine($fields, $val);
} else {
$fields = array();
}
$result = $this->GetMonCollection($collect)->findOne($wheres, $fields);
if ($result) {
if (in_array('_id', $fields)) {
$arr = get_object_vars($result['_id']);
$result['_id'] = $arr['$id'];
} else {
unset($result['_id']);
}
if (strtolower($type) == 'array')
$result = array_values($result);
return $result;
}
return false;
}
/**
* 复合查询 todo 查询高级选项扩展 snapshot maxscan min max hint explain
* <-----------------------
* $gt:>|$gte:>=|$le:<|$lte:<=|$ne:!=
* array('x'=>array('$gt'=>'20'))
*
* $or:$|$end:&
* array('x'=>array('$in'=>array('1','2')))
*
* $where:%
* array('x'=>array('$where'=>function))
*
* 正则表达式:
* ?
*
* 数组:
* $all:*
*
* 内嵌文档:
* 单个条件:username.user username[user
* 多个条件:$elemMatch
*
* 逻辑操作符:
* not:!
* or:$
* end:&
* sort
* array('x'=>0)
*
* limit|skip
* $where = array('&|'=>array('she_hash' => '48b6c531ef2469469ede4ac21eebcf51', 'type' => 'doing'));
* $field = array('time', '_id' => 0);
* $res = $mongo->FindMix('sapilog', $where, $field,'1,2',array('time'=>'desc'));
* ------------------------->
* @param $collect |string
* @param string $where |array array('>|x'=>'10','<=|x'=>'20','^|x'=>array(),'%|x'=>'fun')
* @param array|string $fields |array|string 返回字段 默认为全部 字符串返回一个字段,键为数字则为返回该字段,键为字段,值为-1为不返回该字段
* @param string $limit |string skip,limit 0,10 如果没有 默认 skip为0 limit为当前变量
* @param string $sort |array array('x'=>'desc|asc') desc=>-1 asc=>
* @param string $type | string 返回类型 默认为关联数组 assoc|array
* @return bool
*/
public function FindMix($collect, $where = '', $fields = array(), $limit = '', $sort = '', $type = 'assoc')
{
if (!$this->collected)
return false;
$wheres = $this->GetWhere($where);
if ($where && !$wheres)
return false;
if ($fields && is_array($fields)) { #过滤fields
$val = '';
for ($i = 0; $i < count($fields); $i++) {
$val[] = 1;
}
$fields = array_combine($fields, $val);
} else {
$fields = array();
}
$limits = '';
$skip = '';
if (is_string($limit)) { #过滤limit
$limit = explode(",", trim($limit));
if ($limit && is_numeric(implode('', $limit))) {
if (count($limit) == 1) {
$limits = $limit[0];
} else {
$skip = $limit[0];
$limits = $limit[1];
}
}
} elseif (is_numeric($limit)) {
$limits = trim($limit);
}
$sorts = '';
if (is_array($sort) && $sort) { #过滤sort
foreach ($sort as $k => $v) {
$k = trim($k);
$v = strtoupper(trim($v));
if (in_array($v, array_keys($this->sort_map))) {
$sorts[$k] = $this->sort_map[$v];
}
}
}
$result = $this->GetMonCollection($collect)->find($wheres, $fields);
if ($skip)
$result = $result->skip($skip);
if ($limits)
$result = $result->limit($limits);
if ($sorts)
$result = $result->sort($sorts);
if ($result) {
$return = array();
foreach ($result as $v) {
$return[] = $v;
}
foreach ($return as &$v) {
if (in_array('_id', $fields)) {
$arr = get_object_vars($v['_id']);
$v['_id'] = $arr['$id'];
} else {
unset($v['_id']);
}
if (strtolower($type) == 'array')
$v = array_values($v);
}
return $return;
}
return false;
}
/**
* 修改记录
* $inc、$set、$unset 修改普通文档,如果有就修改,没有就条件加值创建,前者只支持数字类型,使用后者,$unset删除元素
* $push、$addToSet 添加数组元素,前者不去重,使用后者
* $pop、$pull 删除数组元素,前者删除前后,后者删除指定元素,使用后者
*
* @param $collect
* @param $where
* @param $newdata |array
* @param string $type 操作类型 upd修改 unset删除指定元素 arr修改数组元素 pull删除数组元素
* @param bool $upsert 是否创建
* @param int $return |1:返回bool,2:返回影响行数
* @return bool
*/
public function UpdMix($collect, $where, $newdata, $type = 'upd', $upsert = false, $return = 1)
{
if (!$this->collected || !is_array($newdata))
return false;
$wheres = $this->GetWhere($where);
if (!$wheres) {
$this->ErrLog('where is error');
return false;
}
$newdata = $this->ConvertEncode($newdata);
$type = strtolower(trim($type));
$types = array('upd' => '$set', 'unset' => '$unset', 'arr' => '$addToSet', 'pull' => '$pull');
if (isset($types[$type])) {
$option = array();
if ($type == 'upd')
$option = array('multiple' => true);
if (in_array($type, array('upd', 'arr')) && $upsert)
$option['upsert'] = true;
$newdata = array($types[$type] => $newdata);
} else {
return false;
}
try {
$result = $this->GetMonCollection($collect)->update($wheres, $newdata, $option);
$result = $return == 1 ? ($result['err'] ? false : true) : $result['n'];
} catch (MongoConnectionException $e) {
$this->ErrLog($e->getMessage());
return false;
}
return $result;
}
#todo EXPLAIN 函数
/**
* 对集合执行命令
* @param $data
* @return bool
*/
public function RunCommand($data)
{
if (is_array($data) && $data && $this->collected) {
$result = $this->db->command($data);
if (isset($result['values']))
return $result['values'];
else
$this->ErrLog("command error,info:" . $result['errmsg'] . ",bad_cmd:" . json_encode($result['bad cmd']));
}
return false;
}
/**
* todo 聚合
*
*/
public function Group()
{
}
public function Count($collect, $where = array())
{
if (!$this->collected)
return false;
$wheres = $this->GetWhere($where);
if ($where && !$wheres)
return false;
return $this->GetMonCollection($collect)->count($wheres);
}
/**
* 返回指定键的唯一值列表
* @param $collect
* @param $key
* @param array $where 额外查询条件
* @return bool
*/
public function Distinct($collect, $key, $where = array())
{
if (!$this->collected)
return false;
$wheres = $this->GetWhere($where);
if ($where && !$wheres)
return false;
try {
$result = $this->GetMonCollection($collect)->distinct($key, $wheres);
} catch (MongoException $e) {
$this->ErrLog($e->getMessage());
return false;
}
return $result;
}
/**
* 删除集合中的记录
* @param $collect
* @param $where
* @param int $return 1:返回bool,2:返回影响行数
* @return bool
*/
public function RemoveColl($collect, $where, $return = 1)
{
if (!$this->collected)
return false;
$wheres = $this->GetWhere($where);
if (!$wheres) {
$this->ErrLog('where is error');
return false;
}
try {
$result = $this->GetMonCollection($collect)->remove($wheres);
$result = $return == 1 ? ($result['err'] ? false : true) : $result['n'];
} catch (MongoConnectionException $e) {
$this->ErrLog($e->getMessage());
return false;
}
return $result;
}
/**
* mongo 错误日志函数
*
* @param $msg
* @param int $lever
*
*/
private function ErrLog($msg, $lever = 2)
{
global $__CFG;
$trace = debug_backtrace();
$error_log = '';
$date = date("Ymd", time());
$line = isset($trace[$lever]['line']) ? trim($trace[$lever]['line']) : '';
$file = isset($trace[$lever]['file']) ? trim($trace[$lever]['file']) : '';
$object = isset($trace[$lever]['object']) ? get_class($trace[$lever]['object']) : '';
$args = isset($trace[$lever]['args']) ? json_encode($trace[$lever]['args']) : '';
$error_log .= "line {$line} " . ($object ? 'of ' . $object : '') . "(in {$file})\n";
$error_log .= $args ? "args:{$args}\n" : '';
$error_log .= "msg:{$msg}";
if (isset($__CFG['com']['id']) && $__CFG['com']['id']) {
$com_id = $__CFG['com']['id'];
} else {
$com_id = 'common';
}
$log_dir = $this->config['logpath'] ? $this->config['logpath'] : __ROOT__ . "data/nginx_error_log/{$com_id}/{$date}/mongo.nginx_error_log";
error_log("Date:" . date("Y-m-d H:i:s", $date) . "\nstamp:{$date}\ntrace:{$error_log}\n\n", 3, $log_dir);
if (isset($__CFG['currUser']) && $__CFG['currUser'] == 'root' && isset($__CFG['daemon']['user'])) {
$paths = explode("nginx_error_log/", $log_dir);
$pathc = explode("/", $paths[1]);
$pathd = $paths[0] . "nginx_error_log";
foreach ($pathc as $v) {
$pathd .= "/" . $v;
chgrp($pathd, $__CFG['daemon']['user']);
chown($pathd, $__CFG['daemon']['user']);
}
}
}
}
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。