MongoDB学习笔记

启动mongo命令:

sudo /usr/local/opt/mongodb/bin/mongod --dbpath /data/db/ --logpath=/var/log/mongodb.log --logappend &

假定mongo有一个表结构如下:

_id             MongoId             id
sub_id          int                 原来sql表中的信息id
title           string              信息内容
img_path        string              图片路径
img_width       int                 图片宽
img_height      int                 图片高
love            int                 被喜欢的次数
review          int                 评论数
src_status      int                 魔信来源,主平台、手机等
nat_status      int                 魔信分类,图片、视频等
status          int                 是否删除
access_status   int                 访问权限
access_data     array               当access_status=4时,指定谁能访问
    can_fans    array               谁能查看
    cant_fans   array               谁不能查看
t_sub_id        int                 转发id
t_sub_id2       int                 转发id
mx_id           int                 谁发的
browse          int                 已看过总数
cby             int                 创建时间
love_data       array               喜欢人的id数组
click           int                 查看人的数量
click_data      array               查看人的id数组
review_data     array               评论数组
    id          MongoId             评论id,在评论表中的MongoId
    v_mx_id     int                 谁评论的
    content     string              内容
    cby         int                 时间
    yo          array               所有yo过的人的id数组
browse_data     array               所有已看的人的id数组
forward_data    array               所有转发人的id数组
shield_data     array               所有屏蔽该魔信的用户id数组,将id作为key值

如果access_status可能值为0-3,现在我要查access_status为0-2时mx_id指定范围,为3时mx_id不能为指定值(不能为自己):

{
    '$or':[
        {'$and':[{'$in':{'t_type':[0,1,2]}}, {'$in':{'mx_id':[34,52]}}]},
        {'$or':[{'t_type':3}, {'mx_id':{'$ne':34}}]}
    ]
}

转化成php数组形式就为:

array(
'$or'=>array(
	array( '$and'=>array( array( '$in'=>array('t_type'=>array(0,1,2))) ,array( '$in'=>array('mx_id'=>array(34,52))))),
	array( '$or'=> array( array('t_type'=>3) ,array('mx_id'=>array('$ne'=>34))))
	)
)

我要查t_sub_id值类型为MongoId的:

{
	't_sub_id':{'$type':7}
}

常用$type值见官网地址:http://www.mongodb.org/display/DOCS/Advanced+Queries

查询:

{'love':6}  //love=6
{'love':{'$ne':6}}   //love!=6
{'love':{'$gt':6}}   //love数大于6 $lt:小于 $gte,$lte >= , <=
{'access_status':{'$in':[0,1,2]}}   //access_status=0或1或2
{'browse_data':{'$all':[1,2,3]}}   //browse_data中必须要有1,2,3三个值
{'shield_data.1642':{'$exists':true}}  //shield_data数据下面存在1642的key
{'review_data':{'$size':5}}   //review_data数组长度为5
{'$where':'this.review_data.length > 6'}  //review_data数组长度大于6,这样会比较慢,一般设置一个字段review_len表示review长度,然后查review_len>6
{'title':/my god/}  //正则匹配

修改:

db.info.update({'_id':ObjectId('xxxxx')}, {'$inc':{'love':1}} );   //将love数+1
db.info.update({'mx_id':1002, 'status':0}, {'$set':{'status':1}});   //将mx_id=1002且status=0的记录,修改status=1
db.info.update({'_id':ObjectId('xxxxx')}, {'$push':{'shield_data':{1088:1}}});  //将array(1088=>1)的值添加到数组shield_data最后
db.info.update({'_id':ObjectId('xxxxx')}, {'$pop':{'review_data':-1}};   //删除到review_data数组的第一个元素,1为删除最后一个元素
db.info.update({'_id':ObjectId('xxxxx')}, {'$pull':{'forward_data':1321}}); //删除forward_data数组中所有值为1321的元素
db.info.update({'movie_info':{'$exists':true}}, {'$rename': {'movie_info':'mov_info'}}) //将movie_info字段改名为mov_info

shell命令:

show dbs;  #显示所有数据库
use info;  #切换到info库
show collections;  #显示info库所有聚集
db.info.findOne();  #info库下面sub_info表找一条
db.getPrevError();	 #返回之前操作的错误
db.info.find().forEach( function(x) { print(tojson(x));});  #forEach用法
var cursor = db.info.find();  print (tojson(cursor[4]));  #cursor当数组用
db.currentOp();    #获取当前正在执行的操作。
db.serverStatus();  #获取服务器的状态
db.stat();  #获取当前数据库的信息,比如Obj总数、数据库总大小、平均Obj大小等
/usr/local/opt/mongodb/bin/mongodump --db sys_info --collection info   #导出sys_info库下面的info集合
/usr/local/opt/mongodb/bin/mongorestore -h 127.0.0.1    #从刚的备份中恢复

将click_log表里面的id0字段为string类型的,转换成int:

db.click_log.find( { 'id0' : { $type : 2 } } ).forEach( function (x) {
  x.id0 = new NumberInt(x.id0);
  db.click_log.save(x);
});

mongostat是mongdb自带的状态检测工具,在命令行下使用。它会间隔固定时间获取mongodb的当前运行状态,并输出。如果你发现数据库突然变慢或者有其他问题的话,你第一手的操作就考虑采用mongostat来查看mongo的状态。
它的输出有以下几列:

inserts/s             每秒插入次数
query/s               每秒查询次数
update/s              每秒更新次数
delete/s              每秒删除次数
getmore/s             每秒执行getmore次数
command/s             每秒的命令数,比以上插入、查找、更新、删除的综合还多,还统计了别的命令
flushs/s              每秒执行fsync将数据写入硬盘的次数。
mapped/s              所有的被mmap的数据量,单位是MB,
vsize                 虚拟内存使用量,单位MB
res                   物理内存使用量,单位MB
faults/s              每秒访问失败数(只有Linux有),数据被交换出物理内存,放到swap。不要超过100,否则就是机器内存太小,造成频繁swap写入。此时要升级内存或者扩展
locked %              被锁的时间百分比,尽量控制在50%以下吧
idx miss %            索引不命中所占百分比。如果太高的话就要考虑索引是不是少了
q t|r|w               当Mongodb接收到太多的命令而数据库被锁住无法执行完成,它会将命令加入队列。这一栏显示了总共、读、写3个队列的长度,都为0的话表示mongo毫无压力。高并发时,一般队列值会升高。
conn                  当前连接数
time                  时间戳

类似于mysql的phpmyadmin一样的管理工具,mongo有一个叫rockmongo。配置rockmongo有时候,注意如果是配置mongo群集,则要加如下参数:

$MONGO["servers"][$i]["mongo_options"] = array('replicaSet' => 'REPLICA_NAME');//mongo server name
$MONGO["servers"][$i]["mongo_host"] = "mongodb://192.168.0.2,192.168.0.3";//mongo host
$MONGO["servers"][$i]["mongo_port"] = false;//mongo port

SQL:

select sum(app_num) as cot,mx_id from table group by mx_id;

用mongo实现为:

db.table.group( {key: { mx_id:true },
            reduce: function(obj,prev) { prev.cot += obj.app_num; },
            initial: { cot: 0 }
            });

update 时,如果要修改多条记录,则要设置multiple选项,如:

odb::mongodb()->insert(otable::DB_MSG, otable::TABLE_MSG_USER_SYSTEM, $new_system_msg);
$mongo_id = $new_system_msg['_id'];
$options = array('multiple'=>true);
odb::mongodb()->update(otable::DB_MSG,otable::TABLE_MSG_USER_FEEDS,array(),array('$push'=>array('msg_system'=>$mongo_id)),$options);

update时,如果该记录不存在,则插入一条记录,用upsert=true选项。如修改 info的值为3,如果info字段不存在,则插入。

db.info.update({'_id':ObjectId('xxxxx')}, {'$addToSet':{'shield_data':1088}}}); //向shield_data中插入唯一记录,如果不存在,才插入此记录

使用 db.collection.find() 返回数据,每一条占一整行,一点格式都没有,好难阅读,不知道有没有方法格式化返回的值,比如每个key-value占一行这种的。
db.collection.find().pretty();
补充个一劳永逸的方法,在 shell 里执行下列代码
echo “DBQuery.prototype._prettyShell = true” >> ~/.mongorc.js
这样随时随地都是 pretty() 了

现在我要从TABLE_INFO_CLICK表中查询所有click记录,对指定mx_id,只取时间字段cby最大的一条,可以用mongo函数来实现:

$keys = array("mx_id"=>true);
// 要显示的栏位
$initial = array("mx_id" => 0);
// 使用js將要显示栏位的值塞入 prev.mx_id = obj.mx_id;
$reduce = 'function (obj, prev) {
	if(!prev[obj.mx_id] || prev[obj.mx_id] < obj.cby)
	{
		prev[obj.mx_id] = obj.cby;
	}
	delete(prev.mx_id);
}';
$click_mx_ids = odb::mongodb()->group(otable::DB_INFO, otable::TABLE_INFO_CLICK,$keys,$initial,$reduce,array('sub_id'=>new MongoId($sub_id)));

但是这样效率非常低下,建议还是用mongo直接全部查出来,然后再用php处理。

_id             MongoId             id
review_data     array               评论数组
id          MongoId             评论id,在评论表中的MongoId
v_mx_id     int                 谁评论的
content     string              内容
cby         int                 时间
yo          array               所有yo过的人的id数组

上述结构中,向指定记录的review_data数组字段,字段中指定id值的一个元素,在其下面插入一个yo值:

db.info.update({'_id':ObjectId('xxxxx'), 'review_data.id':ObjectId('xxxxx')}, {'$push':{'review_data.$.yo':1088}});

来一个复杂查询:

		$access_query = array();
 		$access_query['access_status'] = array('$in'=>array(0,2));
 		$access_query['mx_id'] = $mx_id; //是自己
 		$access_query['$and'] = array(
 				array('access_status'=>4),
 				array('access_data.cant_fans'=>array('$nin'=>array($mx_id))), //一定得不在禁止列表里面
 				array('$or'=>array(array('access_data.can_fans'=>array()),array('access_data.can_fans'=>array('$in'=>array($mx_id)))))
 				);
		$query = array( //访问权限 0公开 1仅魔友 2仅魔友和魔粉 3仅自己 4自定义
				'status'=>0,  //状态正常
				'mx_id'=>array('$in'=>$arr),  //是我关注的人
				"shield_data.$mx_id"=>array('$exists'=>false),    //我没有屏蔽这条魔信
				'$or'=>array(  //权限设置
						array('access_status'=>$access_query['access_status']),  //公开与魔友和魔粉
						array('mx_id'=>$access_query['mx_id']), //仅自己,如果是自己的魔信,肯定可以见
						array('$and'=> $access_query['$and']), //access_status=4 指定可见条件时
						array('$and'=>array(array('access_status'=>1),array('mx_id'=>array('$in'=>$all_moyou)))), //仅魔友可见
						)
				);

换成js:

{
    "$or": [
        {
            "access_status": {
                "$in": [
                    0,
                    2
                ]
            }
        },
        {
            "mx_id": 648
        },
        {
            "$and": [
                {
                    "access_status": 4
                },
                {
                    "access_data.cant_fans": {
                        "$nin": [
                            648
                        ]
                    }
                },
                {
                    "$or": [
                        {
                            "access_data.can_fans": []
                        },
                        {
                            "access_data.can_fans": {
                                "$in": [
                                    648
                                ]
                            }
                        }
                    ]
                }
            ]
        },
        {
            "$and": [
                {
                    "access_status": 1
                },
                {
                    "mx_id": {
                        "$in": [
                            743,
                            887845
                        ]
                    }
                }
            ]
        }
    ],
    "mx_id": {
        "$in": [
            1,
            64,
            78,
            113,
            121,
            124,
            157,
            249,
            743,
            754,
            1000,
            457,
            79,
            1131,
            790,
            887845,
            602,
            22,
            887896,
            648
        ]
    },
    "shield_data.648": {
        "$exists": false
    },
    "status": 0
}

php的mongo采用正则表达式:

$regexObj = new MongoRegex("/^86-*/");
$find_condition = array('mx_tel'=>$regexObj);
#http://php.net/manual/en/class.mongoregex.php

执行mongo类的时候,报如下notice:

( ! ) Notice: Mongo::__construct(): parsing servers

这个notice是由mongo的驱动输出的,可由下面代码关掉:

MongoLog::setLevel(MongoLog::NONE)

详见:http://stackoverflow.com/questions/11380822/php-mongo-notice-mongo-construct-parsing-servers