安装MongoDB

以ubuntu为例,安装MongoDB

1 导入mongodb的共有gpg key
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10

2 添加/etc/apt/sources.list.d/mongodb-org-3.0.list
echo "deb http://repo.mongodb.org/apt/ubuntu trusty/mongodb-org/3.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-3.0.list

3 升级并安装
sudo apt-get update
sudo apt-get install -y mongodb-org

运行MongoDB

sudo service mongodb start

mongodb默认将数据存放于/var/lib/mongodb目录下,日志文件存放在/var/log/mongodb目录下,以mongodb用户运行mongodb,可以设置/etc/mongod.conf来设置

卸载MongoDB

1 关闭mongodb
sudo service mongodb stop

2 删除软件包
sudo apt-get purge mongodb-org-*

3 删除数据和日志
sudo rm -r /var/log/mongodb
sudo rm -r /var/lib/mongodb

MongoDB的CRUD操作

mongo使用一种叫[BSON]的数据结构

做实验前先插入点数据

ligulfzhouMac-mini:~ ligulfzhou$ ~/py3venv/bin/python3
>>> import pymongo
>>> import random
>>> db = pymongo.MongoClient("localhost:27017")
>>> for i in range(100):
...   db.testdb.test.insert({'id': i, 'name': 'name%s' % i, 'age': random.randint(10, 50)})
...
>>>
ligulfzhouMac-mini:~ ligulfzhou$ mongo     ;;进入mongo终端  
MongoDB shell version: 3.0.7
connecting to: test
Welcome to the MongoDB shell.
For interactive help, type "help".
For more comprehensive documentation, see
	http://docs.mongodb.org/
Questions? Try the support group
	http://groups.google.com/group/mongodb-user
Server has startup warnings: 

> use testdb     ;;选择testdb
switched to db testdb

> db.test.find()    ;;查看所有的testdb的test表的数据
{ "_id" : ObjectId("565c29ddfcce6632cf922b83"), "id" : 0, "name" : "name0", "age" : 49 }
{ "_id" : ObjectId("565c29defcce6632cf922b84"), "id" : 1, "name" : "name1", "age" : 23 }
{ "_id" : ObjectId("565c29defcce6632cf922b85"), "id" : 2, "name" : "name2", "age" : 11 }
{ "_id" : ObjectId("565c29defcce6632cf922b86"), "id" : 3, "name" : "name3", "age" : 32 }
{ "_id" : ObjectId("565c29defcce6632cf922b87"), "id" : 4, "name" : "name4", "age" : 15 }
{ "_id" : ObjectId("565c29defcce6632cf922b88"), "id" : 5, "name" : "name5", "age" : 28 }
{ "_id" : ObjectId("565c29defcce6632cf922b89"), "id" : 6, "name" : "name6", "age" : 30 }
{ "_id" : ObjectId("565c29defcce6632cf922b8a"), "id" : 7, "name" : "name7", "age" : 15 }
{ "_id" : ObjectId("565c29defcce6632cf922b8b"), "id" : 8, "name" : "name8", "age" : 22 }
{ "_id" : ObjectId("565c29defcce6632cf922b8c"), "id" : 9, "name" : "name9", "age" : 32 }
{ "_id" : ObjectId("565c29defcce6632cf922b8d"), "id" : 10, "name" : "name10", "age" : 30 }
{ "_id" : ObjectId("565c29defcce6632cf922b8e"), "id" : 11, "name" : "name11", "age" : 26 }
{ "_id" : ObjectId("565c29defcce6632cf922b8f"), "id" : 12, "name" : "name12", "age" : 11 }
{ "_id" : ObjectId("565c29defcce6632cf922b90"), "id" : 13, "name" : "name13", "age" : 10 }
{ "_id" : ObjectId("565c29defcce6632cf922b91"), "id" : 14, "name" : "name14", "age" : 36 }
{ "_id" : ObjectId("565c29defcce6632cf922b92"), "id" : 15, "name" : "name15", "age" : 40 }
{ "_id" : ObjectId("565c29defcce6632cf922b93"), "id" : 16, "name" : "name16", "age" : 22 }
{ "_id" : ObjectId("565c29defcce6632cf922b94"), "id" : 17, "name" : "name17", "age" : 42 }
{ "_id" : ObjectId("565c29defcce6632cf922b95"), "id" : 18, "name" : "name18", "age" : 30 }
{ "_id" : ObjectId("565c29defcce6632cf922b96"), "id" : 19, "name" : "name19", "age" : 18 }
Type "it" for more

> db.test.find().pretty()      ;;让显示更加漂亮
{
	"_id" : ObjectId("565c29ddfcce6632cf922b83"),
	"id" : 0,
	"name" : "name0",
	"age" : 49
}
{
	"_id" : ObjectId("565c29defcce6632cf922b84"),
	"id" : 1,
	"name" : "name1",
	"age" : 23
}
{
	"_id" : ObjectId("565c29defcce6632cf922b85"),
	"id" : 2,
	"name" : "name2",
	"age" : 11
}

> db.test.insert({"name" : "name101", "id" : 101, "age" : 50})   ;;添加
WriteResult({ "nInserted" : 1 })

> db.test.update({id: 101}, {$set: {age: 100}})					;;修改
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

> db.test.remove({id: 101})										;;删除
WriteResult({ "nRemoved" : 1 })

Aggregate

这里介绍一下mongo的聚集操作,这应该是在开发者角度,优于关系型数据库的地方,方便做一些稍微复杂点操作,mapreduce会另提出来讲。

首先当然还是先生成一些随机数据 接下来看例子 先随机产生100条数据: collection名:col 数据格式: grade(年级), class(班级),sex(性别), age(年龄), point(分数)

  ~ ~/py35env/bin/python
Python 3.5.1 (default, Dec 30 2015, 23:37:24)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pymongo
>>> import random
>>> db = pymongo.MongoClient('127.0.0.1:27017').youcai
>>> for i in range(100):
...   col = {'grade': random.randint(1, 6), 'class': random.randint(1,3), 'age': random.randint(10, 20), 'point': random.randint(60, 100), 'sex': random.randint(0,1)}
...   db.col.insert(col)
这里存了100条数据,年级1-6 班级1-3 年龄10-20 份数60-100 sex0/1

首先,先测试简单功能的聚集(Single Purpose Aggregation Operations)

  ~ mongo
MongoDB shell version: 3.2.0
connecting to: test
> use youcai
switched to db youcai
> db.col.count()      # count: 总人数 
100
> db.col.count({'class': 2})    # count:  二班的人数
41

> db.col.distinct('point')    # distinct: 有哪些分值
db.col.distinct('point')
[ 63, 75, 97, 64, 87, 84, 82, 77, 96, 62, 71, 66, 83, 68, 74, 91, 69, 80, 78, 61, 60, 81, 94, 73, 65, 89, 95, 90, 67, 100, 79, 86, 99, 72, 85, 88, 76, 92, 98 ]

> # 注:其他命令待加

聚集管道(aggregate pipeline) 注:暂时就先记录自己常用的,其他命令待加 聚集管道,用我的话来说,就是一层一层的过滤和操作

比如,我要知道年级是1,2的最高的10个分数的各班总分(虽然比较绕口,也没什么意思)

先做筛选:	
年级是1 2  {'$match': {'grade': {'$in': [1, 2]}}}
按分数倒序:	{'$sort': {'point': -1}}
如果想让同分年级大的放前面:  {'$sort': {'point': -1, 'grade': -1}}
只要前10个: 	{'$limit': 10}
所以总的筛选条件是: db.col.aggregate([{'$match': {'grade': {'$in': [1, 2]}}}, {'$sort': {'point': -1, 'grade': -1}}, {'$limit': 10}])

然后是计算总分,对grade进行聚集,然后求point的总和
{'$group': {'_id': '$grade', 'total': {'$sum': '$point'}}}

汇总:db.col.aggregate([
	{'$match': {'grade': {'$in': [1, 2]}}}, 
	{'$sort': {'point': -1, 'grade': -1}}, 
	{'$limit': 10}, 
	{'$group': {'_id': '$grade', 'total': {'$sum': '$point'}}}]) 

结果:

> db.col.aggregate([{'$match': {'grade': {'$in': [1, 2]}}}, {'$sort': {'point': -1, 'grade': -1}}, {'$limit': 10}, {'$group': {'_id': '$grade', 'total': {'$sum': '$point'}}}])
{ "_id" : 2, "total" : 186 }
{ "_id" : 1, "total" : 770 }
# 所以答案就是1年级的总分770 2年级的总分186

MapReduce

mongo的MapReduce主要用于做统计使用,在对某表做MapReduce时, 不能再查询其他表不能进行更新操作

//MapReduce的语法如下
db.collection.mapReduce(
                         <map>,
                         <reduce>,
                         {
                           out: <collection>,
                           query: <document>,
                           sort: <document>,
                           limit: <number>,
                           finalize: <function>,
                           scope: <document>,
                           jsMode: <boolean>,
                           verbose: <boolean>,
                           bypassDocumentValidation: <boolean>
                         }
                       )

参数说明:

map和reduce是主要的函数, 第三个参数是可选的, out指向mapreduce执行完之后, 将数据存放的collection, query, sort, limit是执行map函数之前,对数据的一次过滤, finalize是最后执行的函数,scope中用于指定可以在map, reduce和finalize中可以共享的参数值, jsmode,默认false, 制定是否存储中间产生的数据,如执行完map后和reduce后,verbose是指定是否要调试信息, 默认是true, 会有代码运行时时间的信息。

类似于,前面先进行了一次查询(db.collection.find(query, sort).limit()),侯然查询后的数据做map,reduce操作 (详情请看官方文档

接下来看例子 先随机产生100条数据: collection名:col 数据格式: grade(年级), class(班级),sex(性别), age(年龄), point(分数)

  ~ ~/py35env/bin/python
Python 3.5.1 (default, Dec 30 2015, 23:37:24)
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import pymongo
>>> import random
>>> db = pymongo.MongoClient('127.0.0.1:27017').youcai
>>> for i in range(100):
...   col = {'grade': random.randint(1, 6), 'class': random.randint(1,3), 'age': random.randint(10, 20), 'point': random.randint(60, 100), 'sex': random.randint(0,1)}
...   db.col.insert(col)
这里存了100条数据,年级1-6 班级1-3 年龄10-20 份数60-100 sex0/1

第一个: 统计各年级平均分

//map函数
var mapFunction = function(){
	//直接将一条数据中的grade和point键的值'发射'给reduce函数
	emit(this.grade, this.point)
}
//reduce函数
var reduceFunction = function(grade, points){
	//这里收到的是年级数和一个分数的数组
	return Array.avg(points);
}
db.col.mapReduce(
	mapFunction, 
	reduceFunction, 
	{
		query: {},
		//对所有数据进行统计
		sort: {'grade': 1, 'point': 1},
		//因为只需要grade和point,加上sort,可以在map和reduce函数运行时,少传点数据
		out: 'grade_avg_output_col'
		//统计之后的数据, 会存到grade_avg_output_col的文档中
	})

第二个: 统计各年级各班级的平均分 这里有一个值得说的事: map函数,不能emit出三个参数, 只能emit两个参数,否则,就会报错了,报错提示类似于这样: “errmsg” : “Error: fast_emit takes 2 args :\[email protected]:2:3\n”, 所以,就不能这样去实现

var mapFunction = function(){
	emit(this.grade, this.class, this.point)
}

正确的姿势是这样的:

var mapFunction = function(){
	emit({grade: this.grade, class: this.class}, this.point)
}

也是这样的:

var mapFunction = function(){
	emit(this.grade, {class: this.class, point: this.point})
}

然后下面是全部的代码: (就挑了这种简单的方式来实现)

//map函数
var mapFunction = function(){
	//直接将一条数据中的grade和point键的值'发射'给reduce函数
	emit({this.grade, class: this.class}, point: this.point)
}
//reduce函数
var reduceFunction = function(gradeWithClass, points){
	return Array.avg(points)
}
db.col.mapReduce(
	mapFunction, 
	reduceFunction, 
	{
		query: {},
		//对所有数据进行统计
		sort: {'grade': 1, 'class': 1, 'point': 1},
		//因为只需要grade和point,加上sort,可以在map和reduce函数运行时,少传点数据
		out: 'grade_class_avg_output_col'
		//统计之后的数据, 会存到该collection中
	})

下面是结果

> db.grade_avg_output_col.find()
{ "_id" : { "grade" : 1, "class" : 1 }, "value" : 77.8 }
{ "_id" : { "grade" : 1, "class" : 2 }, "value" : 77.66666666666667 }
{ "_id" : { "grade" : 1, "class" : 3 }, "value" : 76.55555555555556 }
{ "_id" : { "grade" : 2, "class" : 1 }, "value" : 73.4 }
{ "_id" : { "grade" : 2, "class" : 2 }, "value" : 80.42857142857143 }
{ "_id" : { "grade" : 2, "class" : 3 }, "value" : 79.8 }
{ "_id" : { "grade" : 3, "class" : 1 }, "value" : 83 }
{ "_id" : { "grade" : 3, "class" : 2 }, "value" : 78.25 }
{ "_id" : { "grade" : 3, "class" : 3 }, "value" : 79.83333333333333 }
{ "_id" : { "grade" : 4, "class" : 1 }, "value" : 87.75 }
{ "_id" : { "grade" : 4, "class" : 2 }, "value" : 76.4 }
{ "_id" : { "grade" : 4, "class" : 3 }, "value" : 86 }
{ "_id" : { "grade" : 5, "class" : 1 }, "value" : 86.33333333333333 }
{ "_id" : { "grade" : 5, "class" : 2 }, "value" : 79 }
{ "_id" : { "grade" : 5, "class" : 3 }, "value" : 84.25 }
{ "_id" : { "grade" : 6, "class" : 2 }, "value" : 86 }
{ "_id" : { "grade" : 6, "class" : 3 }, "value" : 84.75 }

mongo控制台保存js函数留以后使用

mongo能将js函数存储起来, 以后进入mongo控制台时,直接调用就可以。

db.system.js.save({
	'_id': 'echoFunc',
	'value': function(p){
		print(p);
	}
})

//下次进入之后
db.loadServerScripts()
//然后就可以调用以前存的函数了
echoFunc('test')