MongoDB
一、Cnetos7安装mongodb
1.1 安装
cd /opt/ wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel70-4.0.7.tgz tar xzvf mongodb-linux-x86_64-rhel70-4.0.7.tgz mv mongodb-linux-x86_64-rhel70-4.0.7 mongodb # 创建数据库目录 mkdir -p /opt/mongodb/data/db mkdir -p /opt/mongodb/data/log # 创建配置文件 cd /opt/mongodb vim mongod.conf # 加入如下内容 """ bind_ip=0.0.0.0 # mongodb所绑定的ip地址 port=27017 # 默认端口 dbpath=/opt/mongodb/data/db # 数据库文件存放路径 logpath=/opt/mongodb/data/log/mongod.log # 日志文件路径 logappend=true # 以追加的方式记录日志 fork=true # 以后台方式运行进程 """ # 添加环境变量 vim /etc/profile # 加入 export PATH=$PATH:/opt/mongodb/bin # 让环境变量立即生效 source /etc/profile
1.2 启动与连接
# 启动服务端 mongod -f mongod.conf # 连接 mongo # 更多用法 mongod/mongo --help
二、简介
MongoDB是用C++语言编写的非关系型数据库。特点是高性能、易部署、易使用,存储数据十分方便,主要特性有:
- 面向集合存储,易于存储对象类型的数据
- 模式自由
- 支持动态查询
- 支持完全索引,包含内部对象
- 支持复制和故障恢复
- 使用高效的二进制数据存储,包括大型对象
- 文件存储格式为BSON (一种JSON的扩展)
2.1 MongoDB基本概念
- 文档 (document) 是MongoDB中数据的基本单元,类似于关系型数据库系统中的行。
- 集合 (collection) 就是一组文档,如果说 MongoDB 中的文档类似于关系型数据库中的行,那么集合就如同表。
- MongoDB 的单个计算机可以容纳多个独立的数据库,每一个数据库都有自己的集合和权限。
- MongoDB 自带简洁但功能强大的 JavaScript shell,这个工具对于管理MongoDB实例和操作数据作用非常大。
- 每一个文档都有一个特殊的键 "_id",它在文档所处的集合中是唯一的,相当于关系数据库中的表的主键。
2.2 文档(Document)
文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。
一个简单的文档例子如下:
{"name":"pd","age":18}
需要注意的是:
- 文档中的键/值对是有序的
- 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型 (甚至可以是整个嵌入的文档)
- MongoDB区分类型和大小写
- MongoDB的文档不能有重复的键
- 文档的键是字符串,除了少数例外情况,键可以使用任意UTF-8字符
文档键命名规范:
- 键不能含有\0 (空字符),这个字符用来表示键的结尾
- . 和 $ 有特别的意义,只有在特定环境下才能使用
- 以下划线 "_" 开头的键是保留的 (不是严格要求的)
2.3 集合(Collection)
集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System) 中的表。
集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。
比如,我们可以将以下不同数据结构的文档插入到集合中:
{"site":"www.baidu.com"} {"site":"www.google.com","name":"Google"} {"site":"www.runoob.com","name":"菜鸟教程","num":5}
当第一个文档插入时,集合就会被创建。
合法的集合名:
- 集合名不能是空字符串""
- 集合名不能含有\0 (空字符),这个字符表示集合名的结尾
- 集合名不能以 "system." 开头,这是为系统集合保留的前缀
- 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$
2.4 MongoDB数据类型
String {"xx": "boo"} Integer {"xx": 10} Boolean {"xx": true} Double {"xx": 1.11} Array {"xx": ["a", "b"]} Object {"id", Objectid()} null {"xx": null} Date {"xx": new Date()} Code {"xx": function() {}} Regular expression {"foo": /foobar/i}
几种重要的数据类型:
ObjectId:
ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:
- 前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
- 接下来的 3 个字节是机器标识码
- 紧接的两个字节由进程 id 组成 PID
- 最后三个字节是随机数
MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象。
由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:
> var newObject = ObjectId() > newObject.getTimestamp() ISODate("2017-11-25T07:21:10Z")
ObjectId 转为字符串
> newObject.str
5a1919e63df83ce79df8b38f
字符串:
BSON 字符串都是 UTF-8 编码。
时间戳:
BSON 有一个特殊的时间戳类型用于 MongoDB 内部使用,与普通的日期类型不相关。 时间戳值是一个 64 位的值。其中:
- 前32位是一个 time_t 值 (与Unix新纪元相差的秒数)
- 后32位是在某秒中操作的一个递增的
序数
在单个 mongod 实例中,时间戳值通常是唯一的。
在复制集中,oplog 有一个 ts 字段。这个字段中的值使用BSON时间戳表示了操作时间。
BSON 时间戳类型主要用于 MongoDB 内部使用。在大多数情况下的应用开发中,你可以使用 BSON 日期类型。
日期:
表示当前距离 Unix新纪元 (1970年1月1日) 的毫秒数。日期类型是有符号的,负数表示 1970 年之前的日期。
> var mydate1 = new Date() # 格林尼治时间 > mydate1 ISODate("2018-03-04T14:58:51.233Z") > typeof mydate1 object > var mydate2 = ISODate() # 格林尼治时间 > mydate2 ISODate("2018-03-04T15:00:45.479Z") > typeof mydate2 object
这样创建的时间是日期类型,可以使用 JS 中的 Date 类型的方法。
返回一个时间类型的字符串:
> var mydate1str = mydate1.toString() > mydate1str Sun Mar 04 2018 14:58:51 GMT+0000 (UTC) > typeof mydate1str string
或者
> Date()
Sun Mar 04 2018 15:02:59 GMT+0000 (UTC)
三、mongodb操作
3.1 账号管理
#################### 创建用户 #################### use admin db.createUser({ user: "root", # 这个root可以随便写 pwd: "123", roles: [{role: "root", db: "admin"}] # 权限,role是root说明是管理员 }) use test db.createUser({ user: "pd", pwd: "123456", roles: [ {role: "readWrite", db: "db1"}, # 针对db1库有读写权限,操作自己的库有读写权限 {role: "read", db: "db2"} # 针对db2库读权限,操作其他库有读权限 ] }) #################### 登录认证 #################### # 连接时认证 mongo --host 127.0.0.1 --port 27017 --authenticationDatabase "数据库名" -u "账号" -p 输入密码 # 登录后认证 mongo --host 127.0.0.1 --port 27017 use 数据库名 db.auth("账号", "密码") #################### 查看所有账号 #################### use admin db.system.users.find().pretty()
# MongoDB 内建角色介绍 https://docs.mongodb.org/manual/reference/built-in-roles/ # MongoDB 权限操作列表 https://docs.mongodb.org/manual/reference/privilege-actions/#security-user-actions # MongoDB 角色管理方法 https://docs.mongodb.org/manual/reference/method/js-role-management/ # MongoDB 用户管理方法 https://docs.mongodb.org/manual/reference/method/js-user-management/
3.2 数据库的增删查
# 增 use db1 # 切换/新增数据库 # 查 show dbs # 查看所有数据库 db # 查看当前数据库 # 删 db.dropDatabase() # 删除当前数据库
3.3 集合(表)的增删查
# 增 use test 当第一个文档插入时,集合就会被创建 db.table1.insert({name: "pd", age: 18}) # 删 db.集合名称.drop() # 查 show collections show tables
3.4 文档(数据记录)的增删改查
新增
user0 = {"name": "xx", "age": 10} user1 = {"name": "oo", "age": 20} db.user.insert(user0) db.user.insertMany([user0, user1]) db.集合名称.insert() # 插入数据,_id存在就报错 db.集合名称.save() # 插入数据,_id存在就更新数据
删除
db.collection.deleteOne(<filter>, {writeConcern: <document>, collation: <document>}) db.collection.deleteMany(<filter>, {writeConcern: <document>, collation: <document>}) db.collection.remove( <query>, { justOne: <boolean>, writeConcern:<document>, collation:<document> } ) # 参数 query:使用查询运算符指定删除条件;要删除集合中的所有文档,传递空文档 {} 即可 justOne:可选,如果设为true或1,则只删除一条,默认为false,表示删除多条 writeConcern:可选 collation:可选,指定要用于操作的排序规则 # 示例 db.collection.deleteOne({"name": "pd"}) db.collection.deleteMany({"name": "pd"}) db.user.remove({"name": "pd"}, {"justOne": true})
更改
语法介绍
db.collection.updateOne( <filter>, <update>, { upsert: <boolean>, writeConcern: <document>, collation: <document>, arrayFilters: [<filterdocument1>, ...] } ) db.collection.updateMany(<filter>, <update>, <options>) # 同上 db.collection.update( <query>, <update>, { upsert: <boolean>, multi: <boolean>, writeConcern: <document>, collation: <document>, arrayFilters: [<filterdocument1>, ...] } ) # 参数说明: query:查询条件 update:更新操作符 upsert:可选,如果设置为true,则在没有文档与查询条件匹配时创建新文档。默认值为false,未找到匹配项时不插入新文档 multi:可选,默认为false,代表只更新找到的第一条记录;设为true,代表更新找到的全部记录 writeConcern:可选,抛出异常的级别 collation:可选,指定要用于操作的排序规则 arrayFilters:可选,一组过滤器文档,用于确定要为阵列字段上的更新操作修改哪些数组元素 db.user.update({name: "pd"}, {name: "pink"}) # 更新一条,更新会覆盖原来全部内容 db.user.update({name: "pd"}, {$set: {name: "pink"}}) # 更新一条,更新指定对应键值对 db.user.update({name: "pd"}, {$set: {name: "pink"}}, {upsert: true}) # 没有匹配成功,则新增一条;匹配成功,同上 db.user.update({name: "pd"}, {$set: {name: "pink"}, {multi: true}}) # 更新全部,multi只有和 $ 一起操作才有效 # 数据结构 { name: "pd", age: 18, gender: "male", hobby: ["basketball", "swimming"], addr: {"province": "GuangDong", "city": "GuangZhou"} } # 修改内嵌文档,把名字为pd的人所在的城市改成ShenZhen db.user.update({name: "pd"}, {"$set": {"addr.city": "ShenZhen"}}) # 把名字为pd的人的第2个爱好改成ping pong db.user.update({name: "pd"}, {"$set": {"hobby.1": "ping pong"}}) # 删除pd的地址,$unset db.user.update({name: "pd"}, {"$unset": {"addr": ""}})
增加与减少:$inc
# 把名字为pd的年龄增加2岁 db.user.update({name: "pd"}, {$inc: {"age": 2}}, {multi: true}) # 把名字为pd的年龄减少10岁 db.user.update({name: "pd"}, {$inc: {"age": -10}}, {multi: true})
添加删除数组内元素:$push、$pop、$pull
# 往数组内添加元素($push) # 为名字为pd的人添加一个爱好 db.user.update({name: "pd"}, {$push: {hobby: "swimming"}}) # 为名字为pd的人添加多个爱好 db.user.update({name: "pd"}, {$push: {hobby: {$each: ["reading", "writting"]}}}) # 按照位置且只能从开头或结尾删除元素($pop) # -1 从数组头部删除一个元素 db.user.update({name: "pd"}, {$pop: {hobby: -1}}) # 1 从数组末尾删除一个元素 db.user.update({name: "pd"}, {$pop: {hobby: 1}}) # 按照条件删除元素,"$pull" 把符合条件的统统删掉,而$pop只能从两端删 db.user.update({name: "pd"}, {$pull: {hobby: "reading"}}, {multi: true})
避免重复添加:$addToSet
db.urls.insert({_id: 1, urls: []}) db.urls.update({_id: 1}, {$addToSet: {urls:http://www.baidu.com}}) db.urls.update({_id: 1}, {$addToSet: {urls:http://www.baidu.com}}) db.urls.update({_id: 1}, {$addToSet: {urls:http://www.baidu.com}}) db.urls.update({_id: 1}, { $addToSet: { urls: { $each: [ "http://www.baidu.com", "http://www.baidu.com", "http://www.xxxxx.com", ] } } })
查询
基本查询
db.collection.find().pretty() db.collection.find() db.collection.findOne() db.collection.find({"name": "pd"})
比较运算符
等于:{key: value} 本身就代表什么等于什么 不等于:$ne 小于:$lt 小于等于:$lte 大于:$gt 大于等于:$gte # 查找年龄小于18(其他类似) db.user.find({"age": {$lt: 18}})
成员运算符
在:$in 不在:$nin db.user.find({"age": {$in: [10, 20]}}) db.user.find({"name": {$nin: ["xx", "oo"]}})
逻辑运算符
# and:写多个条件即可 查询姓名为pd且年龄大于等于18的用户 db.user.find({"name": "pd", "age": {$gte:10}}) # or:使用 $or,值为数组 查询姓名为pd 或 年龄大于等于18的用户 db.user.find({$or: [{"name": "pd"}, {"age": {$gte: 18}}]}) # nor:使用 $nor,值为数组,对一个或多个查询表达式的数组执行逻辑运算,并选择 使数组中的所有查询表达式失败的文档 db.user.find({$nor: [{"name": "pd"}, {"age": 18}]}) # not:使用 $nor,语法:{field: {$not: {<operator-expression>}}} 查询姓名不在数组中的值 db.user.find({"name": {$not: {$in: ["pd"]}}})
正则匹配
# {<field>: /pattern/<options>} 查询姓名以p开头的数据 db.user.find({"name": /^p/}) # 使用 $regex {<field>: {$regex: /pattern/, $options: "<options>"}} {<field>: {$regex: "pattern", $options: "<options>"}} {<field>: {$regex: /pattern/<options>}} 查询姓名以a结尾的数据 db.user.find({"name": {$regex: /A$/, $options: "i"}}) db.user.find({"name": {$regex: "A$", $options: "i"}}) db.user.find({"name": {$regex: /A$/i}}) # $options参数说明 i:不区分大小写。 m:对于包含锚点的模式(即^,对于开始,$结束),在每行的开头或结尾处匹配具有多行值的字符串。如果没有此选项,这些锚点将在字符串的开头或结尾处匹配。 x:$regex除非转义或包含在字符类中,否则"扩展"功能可忽略模式中的所有空格字符。 s:允许点字符(即.)匹配包括换行符在内的所有字符。
limit和skip(分页)
# 方法limit():用于读取指定数量的文档 db.user.find().limit(2) # 方法skip():用于跳过指定数量的文档 db.user.find().skip(2) # 同时使用 db.user.find().limit(2).skip(2) 或 db.user.find().skip(2).limit(2)
自定义查询
# 使用:$where后面写一个函数,返回满足条件的数据 查询年龄大于10的用户 db.user.find({ $where: function() { return this.age>10; } }) # {"_id" :ObjectId("5c9e25b8e58eba05afa55e9f"), "name" :"pink", "age": 10, "gender": "女"} # 想要哪个字段就写1;非"_d"字段,不要不用写;不要"_d"字段,写0 db.user.find({ $where: function() { return this.age<20; }}, {name: 1, age: 1} ) # {"_id" :ObjectId("5c9e25b8e58eba05afa55e9f"), "name" :"pink", "age": 10} db.user.find({ $where: function() { return this.age<20; }}, {_id: 0, name: 1, age: 1} ) # {"name": "pink", "age": 10}
投影
# 在查询到的返回结果中,只选择必要的字段 # 参数为字段与值,值1表示显示,不显示不用写 # 特殊:对于 _id 字段默认是显示的,不显示写0 db.user.find({}, {_id: 0, name: 1, age: 1})
排序
# 1代表升序排序,-1代表降序排序 db.user.find().sort({age: 1}) db.user.find({age: {$gt: 20}}).sort({age: -1}) db.user.find({age: {$gt: 20}}, {_id: 0, name: 1}).sort({age: -1}) db.user.find().sort({age: -1, gender: 1})
统计个数
# 方法count()用于统计结果集中文档的条数 # db.集合名称.find({条件}).count() # db.集合名称.count({条件}) db.user.find({age: 20}).count() db.user.find({age: {$gt: 20}}).count() db.user.count({age: 20})
去重
# 方法distinct()对数据进行去重 # db.集合名称.distinct("去重字段") # db.集合名称.distinct("去重字段", {条件}) db.user.distinct("name") db.user.distinct("name", {age: {$gt: 10}})
四、聚合操作
https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/
聚合 (aggregate) 是基于数据处理的聚合管道,每个文档通过一个由多个阶段 (stage) 组成的管道,可以对每个阶段的管道进行分组、过滤等功能,然后经过一系列的处理,输出相应的结果。
db.集合名称.aggregate({管道: {表达式}})
常用管道
# 在mongodb中,文档处理完毕后,通过管道进行下一次处理 # 常用管道如下: $group # 将集合中的文档分组,可用于统计结果 $match # 过滤数据,只输出符合条件的文档 $project # 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果 $sort # 将输入文档排序后输出 $limit # 限制聚合管道返回的文档数 $skip # 跳过指定数量的文档,并返回余下的文档 $unwind # 将数组类型的字段进行拆分
表达式
# 处理输入文档并输出 # 语法:表达式:"$列名" # 常用表达式: $sum # 计算总和,$sum:1表示以一倍计数 $avg # 计算平均值 $min # 获取最小值 $max # 获取最大值 $push # 在结果文档中插入值到一个数组中 $first # 根据资源文档的排序获取第一个文档数据 $last # 根据资源文档的排序获取最后一个文档数据
聚合操作
准备数据
import datetime from pymongo import MongoClient client = MongoClient("mongodb://root:123456@localhost:27017") table = client["test"]["emp"] l = [ # 教学部 ("凯隐", "male", 18, "20170301", "teacher", 7300, 1), ("纳尔", "male", 25, "20150302", "teacher", 1000, 1), ("艾克", "male", 36, "20130305", "teacher", 8300, 1), ("瑞文", "male", 42, "20140701", "teacher", 3500, 1), ("提莫", "female", 18, "20110211", "teacher", 9000, 1), ("露露", "female", 27, "20160211", "teacher", 3000, 1), # 销售部 ("金克丝", "female", 48, "20150311", "sale", 3000, 2), ("戴安娜", "female", 38, "20101101", "sale", 2000, 2), ("丽桑卓", "female", 18, "20110312", "sale", 1000, 2), ("辛德拉", "female", 18, "20160513", "sale", 3000, 2), ("拉克丝", "female", 28, "20170127", "sale", 4000, 2), # 运营部 ("德玛", "male", 28, "20160311", "operation", 10000, 3), ("狗头", "male", 18, "20170512", "operation", 20000, 3), ("盲僧", "male", 18, "20130311", "operation", 19000, 3), ("妖姬", "female", 38, "20150411", "operation", 18000, 3), ("寒冰", "female", 18, "20140512", "operation", 17000, 3) ] for n, item in enumerate(l, 1): d = { "_id": n, "name": item[0], "gender": item[1], "age": item[2], "hire_date": datetime.datetime.strptime(item[3], "%Y%m%d"), "post": item[4], "salary": item[5], "emp_id": item[6] } table.save(d)
$group
# $group注意点 分组依据需要放到"_id"后面 取不同的字段的值需要使用$,"$age" 取字典嵌套的字典中的值的时候,"$_id.xx" 能够同时按照多个键进行分组 # 按照gender分组 db.emp.aggregate( {$group: {_id: "$gender"}} ) # 按照emp_id分组,获取不同emp_id个数及其平均年龄 db.emp.aggregate( {$group: {_id: "$emp_id", count: {$sum: 1}, avg_age: {$avg: "$age"}}} ) # group by null 将集合中所有文档分为一组 db.emp.aggregate( {$group: {_id: null, count: {$sum: 1}, avg_age: {$avg: "$age"}}} ) # 数据 {"province": "广东", "city": "广州", "user_id": 1} {"province": "广东", "city": "广州", "user_id": 1} {"province": "广东", "city": "广州", "user_id": 2} {"province": "四川", "city": "成都", "user_id": 3} {"province": "广东", "city": "深圳", "user_id": 4} {"province": "广东", "city": "深圳", "user_id": 5} # 需求 # 统计出每个province/city下的user_id的数量(同一个user_id只统计一次) db.user.aggregate( {"$group": {"_id": {"province": "$province", "city": "$city", "user_id": "$user_id"}}} ) """ { "_id" : { "province" : "广东", "city" : "深圳", "user_id" : 5 } } { "_id" : { "province" : "广东", "city" : "广州", "user_id" : 1 } } { "_id" : { "province" : "广东", "city" : "广州", "user_id" : 2 } } { "_id" : { "province" : "四川", "city" : "成都", "user_id" : 3 } } { "_id" : { "province" : "广东", "city" : "深圳", "user_id" : 4 } } """ db.user.aggregate( {"$group": {"_id": {"province": "$province", "city": "$city", "user_id": "$user_id"}}}, {"$group": {"_id": {"province": "$_id.province", "city": "$_id.city"}, "count": {"$sum": 1}}} ) """ { "_id" : { "province" : "四川", "city" : "成都" }, "count" : 1 } { "_id" : { "province" : "广东", "city" : "广州" }, "count" : 2 } { "_id" : { "province" : "广东", "city" : "深圳" }, "count" : 2 } """ db.user.aggregate( {"$group": {"_id": {"province": "$province", "city": "$city", "user_id": "$user_id"}}}, {"$group": {"_id": {"province": "$_id.province", "city": "$_id.city"}, "count": {"$sum": 1}}}, {"$project": {"_id": 0, "province": "$_id.province", "city": "$_id.city", "count": 1}} ) """ { "count" : 1, "province" : "四川", "city" : "成都" } { "count" : 2, "province" : "广东", "city" : "广州" } { "count" : 2, "province" : "广东", "city" : "深圳" } """
$project
# 格式:{"$project":{"保留字段名": 1, "_id": 0, "新增字段名": "表达式"}} # 查看每个员工的名字和年龄+1 db.emp.aggregate( {"$project": { "_id": 0, "name": 1, "new_age": {"$add": ["$age", 1]} } } ) # 表达式之数学表达式 {"$add": [expr1, expr2, ..., exprN]} # 相加 {"$subtract": [expr1, expr2]} # 第一个减第二个 {"$multiply": [expr1, expr2, ..., exprN]} # 相乘 {"$divide": [expr1, expr2]} # 第一个表达式除以第二个表达式的商作为结果 {"$mod": [expr1, expr2]} # 第一个表达式除以第二个表达式得到的余数作为结果 # 查看每个员工的工作多长时间 db.emp.aggregate( {"$project": { "_id": 0, "name": 1, "hire_period": { "$subtract": [ {"$year": new Date()}, {"$year": "$hire_date"} ] } } } ) # 字符串表达式 {"$substr": [字符串/$值为字符串的字段名, 起始位置, 截取几个字节]} {"$concat": [expr1, expr2, ..., exprN]} # 指定的表达式或字符串连接在一起返回,只支持字符串拼接 {"$toLower": expr} {"$toUpper": expr} # 查看每个员工的姓和以大写形式显示的职位 db.emp.aggregate( {"$project": { "_id": 0, "name": 1, "post": {"$toUpper": "$post"}}, } ) # 在group基础上进行project db.emp.aggregate( {$group: {_id: "$gender", count: {$sum: 1}, avg_age: {$avg: "$age"}}}, {$project: {gender: "$_id", count: 1, avg_age: "$avg_age", _id: 0}} )
$match
# 查询年龄大于18的员工,观察男性和女性各有多少人 db.emp.aggregate( {"$match": {"age": {"$gt": 18}}}, {"$group": {"_id": "$gender", "count": {"$sum": 1}}}, {"$project": {"_id": 0, "gender": "$_id", "count": 1}} ) # 查询年龄大于18或部门id为1的员工,观察男性和女性各有多少人 db.emp.aggregate( {"$match": {"$or": [{"age": {"$gt": 18}}, {"emp_id": {"$in": [1]}}]}}, {"$group": {"_id": "$gender", "count": {"$sum": 1}}}, {"$project": {"_id": 0, "gender": "$_id", "count": 1}} )
$sort、$limit、$skip
# {"$sort": {"字段名": 1, "字段名": -1}} # 1升序,-1降序 # {"$limit": n} # {"$skip": n} # 查询男性、女性人数,按人数降序排序 db.emp.aggregate( {"$group": {"_id": "$gender", "count": {"$sum": 1}}}, {"$sort": {"count": -1}} ) db.emp.aggregate( {"$limit": 2}, {"$project": {"name": 1}} ) """ { "_id" : 1, "name" : "凯隐" } { "_id" : 2, "name" : "纳尔" } """ db.emp.aggregate( {"$skip": 14}, {"$project": {"name": 1}} ) """ { "_id" : 15, "name" : "妖姬" } { "_id" : 16, "name" : "寒冰" } """ db.emp.aggregate( {"$skip": 2}, {"$limit": 2}, {"$project": {"name": 1}} ) db.emp.aggregate( {"$limit": 4}, {"$skip": 2}, {"$project": {"name": 1}} ) # 这两个输出结果一样,但是建议先使用skip,再使用limit,因为这样效率会更高 """ { "_id" : 3, "name" : "艾克" } { "_id" : 4, "name" : "瑞文" } """
$unwind
# 将文档中的某一个数组类型字段拆分成多条,每条包含数组的一个值 # 语法:{"$unwind": "$字段名"} db.t1.insert({"_id": 1, "item": "t-shirt", "size": ["S", "M", "L"]}) db.t1.aggregate({"$unwind": "$size"}) """ { "_id" : 1, "item" : "t-shirt", "size" : "S" } { "_id" : 1, "item" : "t-shirt", "size" : "M" } { "_id" : 1, "item" : "t-shirt", "size" : "L" } """ # 统计size的个数 db.t1.aggregate( {"$match": {"item": "t-shirt"}}, {"$unwind": "$size"}, {"$group": {"_id": null, "count": {"$sum": 1}}}, {"$project": {"_id": 0, "count": 1}} ) """ { "count" : 3 } """ # 使用$unwind注意点 db.t2.insert([ {"_id": 1, "item": "a", "size": ["S", "M", "L"]}, {"_id": 2, "item": "b", "size": []}, {"_id": 3, "item": "c", "size": "M"}, {"_id": 4, "item": "d"}, {"_id": 5, "item": "e", "size": null}, ]) db.t2.aggregate({"$unwind": "$size"}) """ { "_id" : 1, "item" : "a", "size" : "S" } { "_id" : 1, "item" : "a", "size" : "M" } { "_id" : 1, "item" : "a", "size" : "L" } { "_id" : 3, "item" : "c", "size" : "M" } """ # 可以看到_id为2、5的数据没了,如果我们希望保留这种数据,则: # 属性值为false表示丢弃属性值为空的文档 # 设置preserveNullAndEmptyArrays值为true,可以保留属性值为空的文档 db.t2.aggregate({ "$unwind":{ "path": "$size", "preserveNullAndEmptyArrays": true } }) """ { "_id" : 1, "item" : "a", "size" : "S" } { "_id" : 1, "item" : "a", "size" : "M" } { "_id" : 1, "item" : "a", "size" : "L" } { "_id" : 2, "item" : "b" } { "_id" : 3, "item" : "c", "size" : "M" } { "_id" : 4, "item" : "d" } { "_id" : 5, "item" : "e", "size" : null } """
五、数据的备份与恢复
########### 备份语法 ########### mongodump -h dbhost -d dbname -o dbdirectory # 参数解析 -h 服务器地址,也可以指定端口 -d 需要备份的数据库名称 -o 备份的数据存放位置,此目录中存放着备份出来的数据 mongodump -d test -o /home # 本机不需要-h mongodump -h 47.107.109.75:27017 -d test -o /home ########### 恢复语法 ###########
# 恢复时可以指定恢复到哪个数据库(没有强制要求与备份时的名称一样)
mongorestore -h dbhost -d dbname --dir dbdirectory mongorestore -d test1 --dir /home/test
六、索引
# 测试,插入10万条数据 for(i=0; i<100000; i++){db.test.insert({"name": "test"+i, "age": i})} db.test.find({"name": "test10000"}) db.test.find({"name": "test10000"}).explain("executionStats") # 建立索引,语法:db.集合名称.ensureIndex({"属性": 1}),1表示升序,-1表示降序 # 具体操作:db.test.ensureIndex({"name": 1})
建立索引之后对比:
# 创建唯一索引(索引的值是唯一的) db.collection.ensureIndex({"name": 1}, {"unique": true}) # 创建联合索引 db.collection.ensureIndex({"name": 1, "age": 1}) # 查看当前集合的所有索引 db.collection.getIndexes() # 删除索引 db.collection.dropIndex({"索引名称": 1})
七、pymongo
官方文档:http://api.mongodb.com/python/current/tutorial.html
from pymongo import MongoClient client = MongoClient("mongodb://root:123456@localhost:27017") collection = client["test"]["t1"] ######## 插入一条数据 ######## ret1 = collection.insert({"name": "pd", "age": 18}) print(ret1) # 打印的是 _id 的值 ######## 插入多条数据 ######## data_list = [{"name": "pd{}".format(i), "age": "{}".format(i)} for i in range(10)] collection.insert_many(data_list) ######## 查新一条记录 ######## ret2 = collection.find_one({"name": "pd"}) print(ret2) ######## 查新多条记录 ######## ret3 = collection.find({"name": "pd"}) print(ret3) # <pymongo.cursor.Cursor object at 0x0000000002F4DF28> # 方式1 for i in ret3: print(i) # 方式2 print(list(ret3)) ######## 更新一条数据 ######## collection.update_one({"name": "pd"}, {"$set": {"name": "pink"}}) ######## 更新多条数据 ######## collection.update_many({"name": "pd"}, {"$set": {"name": "pink"}}) ######## 删除一条数据 ######## collection.delete_one({"name": "pd"}) ######## 删除多条数据 ######## collection.delete_many({"name": "pd"})