mongodb和python

隔壁老王
• 阅读 1602

MongoDB的安装与使用

当前学习环境的 Ubuntu 16.04 中已经安装好了MongoDB,版本为3.2.8,可以直接跳过此节.

  • 下载mongodb的版本,两点注意
  1. 根据业界规则,偶数为稳定版,如3.2.X;奇数为开发版,如3.3.X
  2. 32bit的mongodb最大只能存放2G的数据,64bit就没有限制

使用MongoDB,需要先启动服务端,再使用客户端连接数据库。

服务端

MongoDB 默认的存储数据目录为 /data/db,默认端口27017
  • 服务的命令为mongod,可以通过help查看所有参数

    python@ubuntu:~$ mongod --help

  • 相关文件存放路径:默认各个文件存放路径如下所示:

    • 可执行文件存放路径:/usr/bin/mongod 和 /usr/bin/mongo
    • 数据库文件存放路径:/data/db
    • 日志文件存放路径:/var/log/mongodb/mongod.log
    • 配置文件存放路径:/etc/mongod.conf

    启动注意事项:

  1. 首次启动:
  • 启动MongoDB服务: sudo mongod 报出如下错误,表示默认的存储数据目录 /data/db 不存在:

[initandlisten] exception in initAndListen: 29 Data directory /data/db not found., terminating

  • 创建 /data目录和 /data/db 目录,并指定 读/写/执行 权限

    python@ubuntu:$ sudo mkdir -p /data/db python@ubuntu:$ sudo chmod 777 /data/db

  1. 再次启动:
  • 再次启动MongoDB服务: sudo mongod 启动成功,但是可能会有如下警告:
    #### 此乃 Warning 1:
    [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'.
    [initandlisten] **        We suggest setting it to 'never'
    [initandlisten] 
    [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
    [initandlisten] **        We suggest setting it to 'never'
    

此乃 Warning 2:

[initandlisten] ** WARNING: soft rlimits too low. rlimits set to 1024 processes, 64000 files. Number of processes should be at least 32000 : 0.5 times number of files.

此乃 Warning 3:

[initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.

#### 注意:这里的三个Warning并非必须处理,大家了解即可:
##### Warning 1:
[initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. [initandlisten] ** We suggest setting it to 'never' [initandlisten] [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'. [initandlisten] ** We suggest setting it to 'never'

> Linux的内存分配默认由内核动态分配,而不是由程序自行管理。而MongoDB对内存占用有那么点...严重,所以为了防止MongoDB占用内存过大而被内核"管理",官方推荐关闭动态分配。

> 默认"always"表示允许动态分配,对应的"never"就是不允许,所以我们将这两个文件内容修改为"naver"后就没有warning了。

Ctrl + c 退出 MongoDB 数据库服务

然后进入 root 用户下,执行修改命令

python@ubuntu:~$ sudo su [sudo] python 的密码:

root@ubuntu:# sudo echo "never" > /sys/kernel/mm/transparent_hugepage/enabled root@ubuntu:# sudo echo "never" > /sys/kernel/mm/transparent_hugepage/defrag

> 实际上,除非网站DBA对数据库性能有极限要求,在通常情况下系统动态分配的内存页大小足够我们正常使用,而且更能优化整个系统,所以一般不必理会这个warning。而且这样只是临时修改Linux内核的设置,在Linux服务器重启后则会失效。

##### Warning 2:
[initandlisten] ** WARNING: soft rlimits too low. rlimits set to 1024 processes, 64000 files. Number of processes should be at least 32000 : 0.5 times number of files.

> 这个WARNING(如果有的话)含义为: 表示默认分配给MongoDB的进程和文件数量限制过低,需要重新分配值:

> - mongodb当前限制:1024 processes, 64000 files
> - mongodb建议要求:processes = 0.5*files=32000(至少)
> 咱们学习阶段默认用不着这么多的进程和文件,所以也可以不必理会。

打开 相关配置文件:

root@ubuntu:~# vi /etc/security/limits.conf

在打开的 文件最下方,添加,然后保存退出

mongod soft nofile 64000 mongod hard nofile 64000 mongod soft nproc 32000 mongod hard nproc 32000

##### Warning 3:
[initandlisten] ** WARNING: You are running this process as the root user, which is not recommended.

> 意思是我们在用root权限做这些事,理论上是不安全的。我们可以通过附加--auth参数,来使用用户认证来处理这个情况,这个后面会讲到。

##### 3. 再再次启动:
- 再再次启动MongoDB服务: sudo mongod
启动后查看进程,以确定是否启动成功

    python@ubuntu:~$ ps aux | grep mongod

- 如果进程中没有mongod的项则没有启动成功,可以通过查看日志来确定错误原因,默认日志文件为 /var/log/mongodb/mongod.log,最新的信息在最后面显示。

#### 客户端

- 客户端命令为 mongo,可以通过help查看所有参数。
- 这个shell即是mongodb的客户端,用来对MongoDB进行操作和管理的交互式环境。

    python@ubuntu:~$ mongo --help
- 终端退出连接

exit

(或Ctrl+C)


### MongoDB的使用

#### mongod:

> mongo 是启动MongoDB shell客户端的命令

> mongod 是启动MongoDB数据库服务的命令,主要提供了三种启动方式:

#### 1. 命令行方式直接启动

MongoDB默认的存储数据目录为/data/db(需要事先创建),默认端口27017,也可以修改成不同目录:

直接启动mongod,默认数据存储目在 /data/db

python@ubuntu:~$ sudo mongod

启动mongod,并指定数据存储目录(目录必须存在,且有读写权限)

python@ubuntu:~$ sudo mongod --dbpath=/xxxxx/xxxxx


#### 2. 配置文件方式启动

启动时加上-f参数,并指向配置文件即可,默认配置文件为/etc/mongodb.cnf,也可以自行编写配置文件并指定。

启动mongod,并按指定配置文件执行

python@ubuntu:~$ sudo mongod -f /etc/mongodb.cnf


#### 3. 守护进程方式启动

##### 启动

MongoDB提供了一种后台程序方式启动的选择,只需要加上—fork参数即可。但是注意:如果用到了--fork参数,就必须启用--logpath参数来指定log文件,这是强制的。

python@ubuntu:~$ sudo mongod --logpath=/data/db/mongodb.log --fork

about to fork child process, waiting until server is ready for connections. forked process: xxxxx child process started successfully, parent exiting


##### - 关闭

如果使用--fork在后台运行mongdb服务,那么就要通过本机admin数据库向服务器发送shutdownServer()消息来关闭。

python@ubuntu:~$ mongo MongoDB shell version: 3.2.8 connecting to: test

use admin switched to db admin

db.shutdownServer() server should be down... 2017-05-16T22:34:22.923+0800 I NETWORK [thread1] trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed 2017-05-16T22:34:22.923+0800 W NETWORK [thread1] Failed to connect to 127.0.0.1:27017, reason: errno:111 Connection refused 2017-05-16T22:34:22.923+0800 I NETWORK [thread1] reconnect 127.0.0.1:27017 (127.0.0.1) failed failed


#### 3. 启用用户认证方式启动

如果之前未定义过用户,所以mongod将允许本地直接访问操作数据库将使用本地root权限,如果使用--auth参数启动,将启用MongoDB授权认证,即启用不同的用户对不同的数据库的操作权限。

> 也可以在配置文件mongod.conf中加入auth = true按第二种启动方式启动。

启动mongod,并启用用户认证

python@ubuntu:~$ sudo mongod --auth

启动mongo shell

python@ubuntu:~$ mongo MongoDB shell version: 3.2.8 connecting to: test

1. 切换admin数据库下

use admin switched to db admin

2. 创建一个拥有root权限的超级用户,拥有所有数据库的所有权限

用户名:python,密码:chuanzhi,角色权限:root(最高权限)

db.createUser({user : "python", pwd : "chuanzhi", roles : ["root"]})

Successfully added user: { "user" : "python", "roles" : [ "root" ] }

3. 如果 MongoDB 开启了权限模式,并且某一个数据库没有任何用户时,可以不用认证权限并创建一个用户,但是当继续创建第二个用户时,会返回错误,若想继续创建用户则必须认证登录。

db.createUser({user : "bigcat", pwd : "bigcat", roles : [{role : "read", db : "db_01"}, {role : "readWrite", db : "db_02"}]}) couldn't add user: not authorized on admin to execute command{ createUser: "bigcat", pwd: "xxx", roles: [ { role: "read", db: "db_01" }, { role: "readWrite", db: "db_02" } ], digestPassword: false, writeConcern: { w: "majority", wtimeout: 30000.0 } } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 DB.prototype.createUser@src/mongo/shell/db.js:1267:15

4. 认证登录到python用户(第一次创建的用户)

db.auth("python","chuanzhi") 1

5. 查看当前认证登录的用户信息

show users { "_id" : "admin.python", "user" : "python", "db" : "admin", "roles" : [ { "role" : "root", "db" : "admin" } ] }

6. 认证登录成功,可以继续创建第二个用户

用户名:bigcat,密码:bigcat,角色权限:[对db_01 拥有读权限,对db_02拥有读/写权限]

db.createUser({user : "bigcat", pwd : "bigcat", roles : [{role : "read", db : "db_01"}, {role : "readWrite", db : "db_02"}]}) Successfully added user: { "user" : "bigcat", "roles" : [ { "role" : "read", "db" : "db_01" }, { "role" : "readWrite", "db" : "db_02" } ] }

7. 查看当前数据库下所有的用户信息.

db.system.users.find() { "_id" : "admin.python", "user" : "python", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "y/3yPLzhDKa7cJ3Zd/8DXg==", "storedKey" : "9XaUqiUteEtFAfof3k+HJjevqCA=", "serverKey" : "YjIoUPl7HTHQZuklSFXXYpZB/U4=" } }, "roles" : [ { "role" : "root", "db" : "admin" } ] } { "_id" : "admin.bigcat", "user" : "bigcat", "db" : "admin", "credentials" : { "SCRAM-SHA-1" : { "iterationCount" : 10000, "salt" : "ZcCaT057Gz0WODuSx70Ncg==", "storedKey" : "pNYyLMPisTcYuUHMdR46vndteIo=", "serverKey" : "IOzB2pyBRyCgKTNNSf1wljsVxms=" } }, "roles" : [ { "role" : "read", "db" : "db_01" }, { "role" : "readWrite", "db" : "db_02" } ] }

8. 认证登录到 bigcat 用户

db.auth("bigcat", "bigcat") 1

9. 切换到 数据库db_01,读操作没有问题

use db_01 switched to db db_01 show collections

10. 切换到 数据库db_02,读操作没有问题

use db_02 switched to db db_02 show collections

11. 切换到 数据库db_03,读操作出现错误,bigcat用户在db_03数据库下没有相关权限

use db_03 switched to db db_03 show collections 2017-05-17T00:26:56.143+0800 E QUERY [thread1] Error: listCollections failed: { "ok" : 0, "errmsg" : "not authorized on db_03 to execute command { listCollections: 1.0, filter: {} }", "code" : 13 } : _getErrorWithCode@src/mongo/shell/utils.js:25:13 DB.prototype._getCollectionInfosCommand@src/mongo/shell/db.js:773:1 DB.prototype.getCollectionInfos@src/mongo/shell/db.js:785:19 DB.prototype.getCollectionNames@src/mongo/shell/db.js:796:16 shellHelper.show@src/mongo/shell/utils.js:754:9 shellHelper@src/mongo/shell/utils.js:651:15 @(shellhelp2):1:1

12. 认证登录到python用户下

db.auth("python", "chuanzhi") 1

13. 删除bigcat用户

db.dropUser("bigcat") true

14. 尝试认证登录bigcat失败

db.auth("bigcat", "bigcat") Error: Authentication failed. 0

15. 退出mongo shell

exit bye python@ubuntu:~$


> 参考阅读:[http://www.mongoing.com/docs/tutorial/enable-authentication.html)

#### mongod部分参数说明(了解):

- dbpath:数据文件存放路径。每个数据库会在其中创建一个子目录,防止同一个实例多次运行的mongod.lock也保存在次目录中。

- logpath:错误日志文件

- auth:用户认证

- logappend:错误日志采用追加模式(默认覆写模式)

- bind_ip:对外服务的绑定ip,一般设置为空,及绑定在本机所有可用ip上。如有需要可以单独绑定。

- port:对外服务端口。Web管理端口在这个port的基础上+1000。

- fork:以后台Daemon形式运行服务。

- journal:开启日志功能,通过保存操作日志来降低单机故障的恢复时间。

- syncdelay:系统同步刷新磁盘的时间,单位为秒,默认时60秒。

- directoryperdb:每个db存放在单独的目录中,建议设置该参数。

- repairpath:执行repair时的临时目录。如果没有开启journal,异常down机后重启,必须执行repair操作。

在源代码中,mongod的参数分为一般参数,windows参数,replication参数,replica set参数以及隐含参数。上面列举的时一般参数。

> mongod的参数中,没有设置内存大小的相关参数,因为MongoDB使用os mmap机制来缓存数据文件数据,自身目前不提供缓存机制。mmap在数据量不超过内存时效率很高,但是数据超过内存后,写入的性能不太稳定。

#### MongoDB 统计信息(了解)

要获得关于MongoDB的服务器统计,需要在MongoDB客户端键入命令db.stats()。这将显示数据库名称,收集和数据库中的文档信息。输出的命令如下所示

![avatar](https://img-hello-world.oss-cn-beijing.aliyuncs.com/6b45ef2f91e6dd9f47c76c51023f8c7a.png)

了解下一下各个参数

"db" : "test" ,表示当前是针对"test"这个数据库的描述。想要查看其他数据库,可以先运行$ use datbasename "collections" : 3,表示当前数据库有多少个collections.可以通过运行show collections查看当前数据库具体有哪些collection. "objects" : 267,表示当前数据库所有collection总共有多少行数据。显示的数据是一个估计值,并不是非常精确。 "avgObjSize" : 623.2322097378277,表示每行数据是大小,也是估计值,单位是bytes "dataSize" : 16640,表示当前数据库所有数据的总大小,不是指占有磁盘大小。单位是bytes "storageSize" : 110592,表示当前数据库占有磁盘大小,单位是bytes,因为mongodb有预分配空间机制,为了防止当有大量数据插入时对磁盘的压力,因此会事先多分配磁盘空间。 "numExtents" : 0,没有什么真实意义 "indexes" : 2 ,表示system.indexes表数据行数。 "indexSize" : 53248,表示索引占有磁盘大小。单位是bytes "ok" : 1,表示服务器正常


### MongoDB快速入门

MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。

![avatar](https://img-hello-world.oss-cn-beijing.aliyuncs.com/5a7d377740d8fa6b816332107a2a4a34.png)

#### 1. 数据库

数据库是一个集合的物理容器。一个单一的MongoDB服务器通常有多个数据库。如自带的admin、test,或自行创建的数据库。

#### 2. 集合

也称为文档组,类似于关系数据库中的表格。

集合存在于数据库中,一个数据库可以包含很多个集合。集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

比如,我们可以将以下不同数据结构的文档插入到集合中:

{"site" : "www.baidu.com"} {"site" : "www.google.com", "name" : "Google"} {"site" : "www.itcast.cn", "name" : "传智播客", "num" : [100, 200, 300]}

当第一个文档数据插入时,集合就会被创建。
#### 3. 文档

MongoDB使用了BSON(Binary JSON)这种结构来存储数据,并把这种格式转化成了文档这个概念,每个文档是一组 键 : 值 的数据。

![avatar](https://img-hello-world.oss-cn-beijing.aliyuncs.com/6b1c8fc43cad8d1a4c4e1c1af3a2f6e0.png)

#### RDBMS VS MongoDB

下面给出的表显示RDBMS(关系型数据库管理系统)术语 与 MongoDB 的关系


SQL术语/概念 | MongoDB术语/概念 | 解释/说明
---|---|---
database | database | 数据库
table | collection | 数据库表/集合
row | documen | 数据记录行/文档
column | field | 数据属性/字段(域)
index | index | 索引
primary | key | primary key  | 主键,MongoDB默认自动将_id字段设置为主键,可以手动设置

- 通过下图实例,我们也可以更直观的的了解SQL和MongoDB的对比:


![avatar](https://img-hello-world.oss-cn-beijing.aliyuncs.com/2166c1aeb05d1e9f9b6d45bd583b3187.png)

### 数据库基本命令

- 连接成功后,默认使用test数据库
-  查看当前数据库名称

    > db

- 查看所有数据库名称,列出所有在物理上存在的数据库

    > show dbs

- 切换数据库,如果数据库不存在也并不创建,直到插入数据或创建集合时数据库才被创建

    > use 数据库名称

- 删除当前指向的数据库,如果数据库不存在,则什么也不做

    > db.dropDatabase()

### 集合命令

- 创建语法如下
- name是要创建的集合的名称
- options是一个文档,用于指定集合的配置,选项​​参数是可选的,所以只需要到指定的集合名称
- 可以不手动创建集合,向不存在的集合中第一次加入数据时,集合会被创建出来

    db.createCollection(name, options)

> 创建集合stu

#例1:不限制集合大小

db.createCollection("stu")

#例2:限制集合大小

  • 参数capped:默认值为false表示不设置上限,值为true表示设置上限
  • 参数size:当capped值为true时,需要指定此参数,表示上限大小,当文档达到上限时,会将之前的数据覆盖,单位为字节 语法 :

db.createCollection("stu", {capped : true, size : 6142800} ) { "ok" : 1 }

- 查看当前数据库的集合

    show collections

- 删除命令

    db.stu.drop()

### 数据类型

下表为MongoDB中常用的几种数据类型:

> - ObjectID:文档ID
> - String:字符串,最常用,必须是有效的UTF-8
> - Boolean:存储一个布尔值,true或false
> - Integer:整数可以是32位或64位,这取决于服务器
> - Double:存储浮点值
> - Arrays:数组或列表,多个值存储到一个键
> - Object:用于嵌入式的文档,即一个值为一个文档
> - Null:存储Null值
> - Timestamp:时间戳,表示从1970-1-1到现在的总秒数
> - Date:存储当前日期或时间的UNIX时间格式

创建日期语句如下,注意参数的格式为YYYY-MM-DD

    Date('2017-12-20')

#### ObjectID

_id是一个12字节的十六进制数,保证每一份文件的唯一性。你可以自己去设置_id插入文档。如果没有提供,那么MongoDB的每个文档提供了一个独特的ID,这12个字节:

- 前4个字节为当前时间戳;

- 之后的3个字节的机器ID;

- 接下来的2个字节的MongoDB的服务进程id;

- 剩余3个字节是简单的增量值

- 一个字节等于2位十六进制(一位十六进制的数等于四位二进制的数。一个字节等于8位二进制数)

#### 插入

语法

db.集合名称.insert(document)

- 插入文档时,如果不指定_id参数,MongoDB会为文档分配一个唯一的ObjectId
- 例1

db.stu.insert({name:'gj',gender:1})

- 例2

s1={_id:'20160101',name:'hr'} s1.gender=0 db.stu.insert(s1)


#### 简单查询

- 语法

    db.集合名称.find()

#### 更新

- 语法

db.集合名称.update( , , {multi: } )

> - 参数query:查询条件,类似sql语句update中where部分
> - 参数update:更新操作符,类似sql语句update中set部分
> - 参数multi:可选,默认是false,表示只更新找到的第一条记录,值为true表示把满足条件的文档全部更新

- 例3:全文档更新

    db.stu.update({name:'hr'},{name:'mnc'})

- 例4:指定属性更新,通过操作符$set

    db.stu.insert({name:'hr',gender:0})
    db.stu.update({name:'hr'},{$set:{name:'hys'}})

- 例5:修改多条匹配到的数据

    db.stu.update({},{$set:{gender:0}},{multi:true})

#### 保存

- 语法

    db.集合名称.save(document)

- 如果文档的_id已经存在则修改,如果文档的_id不存在则添加

- 例6

    db.stu.save({_id:'20160102','name':'yk',gender:1})

- 例7

    db.stu.save({_id:'20160102','name':'wyk'})

#### 删除

- 语法

db.集合名称.remove( , { justOne: } )

> - 参数query:可选,删除的文档的条件
> - 参数justOne:可选,如果设为true或1,则只删除一条,默认false,表示删除多条

- 例8:只删除匹配到的第一条

    db.stu.remove({gender:0},{justOne:true})

- 例9:全部删除

    db.stu.remove({})

#### 创建示例数据

- 创建集合stu,文档的属性包含:_id、name、hometown、age、gender
- 示例数据如下:

db.stu.drop() db.stu.insert({_id : 1, name:'郭靖',hometown:'蒙古',age:20,gender:true}) db.stu.insert({_id : 2, name:'黄蓉',hometown:'桃花岛',age:18,gender:false}) db.stu.insert({_id : 3, name:'华筝',hometown:'蒙古',age:18,gender:false}) db.stu.insert({_id : 4, name:'黄药师',hometown:'桃花岛',age:40,gender:true}) db.stu.insert({_id : 5, name:'段誉',hometown:'大理',age:16,gender:true}) db.stu.insert({_id : 6, name:'段王爷',hometown:'大理',age:45,gender:true})


#### 数据查询

##### 1. 基本查询

- 方法find():查询全部符合条件数据

    db.集合名称.find({条件文档})

db.stu.find({age:18}) { "_id" : ObjectId("591b49c29f8ac621f995c911"), "name" : "黄蓉", "hometown" : "桃花岛", "age" : 18, "gender" : false } { "_id" : ObjectId("591b49c29f8ac621f995c912"), "name" : "华筝", "hometown" : "蒙古", "age" : 18, "gender" : false }


- 方法findOne():查询,只返回第一个

    db.集合名称.findOne({条件文档})

db.stu.findOne({age:18}) { "_id" : ObjectId("591b49c29f8ac621f995c911"), "name" : "黄蓉", "hometown" : "桃花岛", "age" : 18, "gender" : false }


- 方法pretty():将结果格式化

    db.集合名称.find({条件文档}).pretty()

db.stu.find({age:18}).pretty() { "_id" : ObjectId("591b49c29f8ac621f995c911"), "name" : "黄蓉", "hometown" : "桃花岛", "age" : 18, "gender" : false } { "_id" : ObjectId("591b49c29f8ac621f995c912"), "name" : "华筝", "hometown" : "蒙古", "age" : 18, "gender" : false }


#### 2. 比较运算符

> - 等于,默认是等于判断,没有运算符
> - 小于 $lt
> - 小于或等于 $lte
> - 大于 $gt
> - 大于或等于 $gte
> - 不等于 $ne

- 例1:查询名称等于'郭靖'的学生

    db.stu.find({name:'郭靖'})

- 例2:查询年龄大于或等于18的学生

    db.stu.find({age:{$gte:18}})

#### 3. 逻辑运算符
- 查询时可以有多个条件,多个条件之间需要通过逻辑运算符连接
- 逻辑与:默认是逻辑与的关系
- 例3:查询年龄大于或等于18,并且性别为1的学生

    db.stu.find({age:{$gte:18},gender:true})

- 逻辑或:使用$or
- 例4:查询年龄大于18,或性别为0的学生

    db.stu.find({$or:[{age:{$gt:18}},{gender:true}]})

- and和or一起使用
- 例5:查询年龄大于18或性别为0的学生,并且学生的姓名为gj

    db.stu.find({$or:[{age:{$gte:18}},{gender:true}],name:'郭靖'})

#### 4. 范围运算符

- 使用$in,$nin 判断是否在某个范围内
- 例6:查询年龄为18、28的学生

    db.stu.find({age:{$in:[18,28]}})

#### 5. 支持正则表达式

- 使用/ /或$regex编写正则表达式
- 例7:查询姓黄的学生

db.stu.find({name:/^黄/}) db.stu.find({name:{$regex:'^黄'}}})


#### 6. 自定义查询

- 使用$where后面写一个函数,返回满足条件的数据
- 例7:查询年龄大于30的学生

    db.stu.find({$where : function(){return this.age>20}})

#### Limit

方法limit():用于读取指定数量的文档

- 语法:

    db.集合名称.find().limit(NUMBER)

> - 参数NUMBER表示要获取文档的条数
> - 如果没有指定参数则显示集合中的所有文档

- 例1:查询2条学生信息

    db.stu.find().limit(2)

#### skip

方法skip():用于跳过指定数量的文档

- 语法:

    db.集合名称.find().skip(NUMBER)

- 参数NUMBER表示跳过的记录条数,默认值为0
- 例2:查询从第3条开始的学生信息

    db.stu.find().skip(2)

#### 一起使用

- 方法limit()和skip()可以一起使用,不分先后顺序

- 创建数据集

for(i=0;i<15;i++){db.nums.insert({_id:i})}

- 查询第5至8条数据

db.nums.find().limit(4).skip(5) 或 db.nums.find().skip(5).limit(4)


#### 投影

- 在查询到的返回结果中,只选择必要的字段,而不是选择一个文档的整个字段
- 如:一个文档有5个字段,需要显示只有3个,投影其中3个字段即可
- 语法:
- 参数为字段与值,值为1表示显示,值为0不显示

    db.集合名称.find({},{字段名称:1,...})

- 对于需要显示的字段,设置为1即可,不设置即为不显示
- 特殊:对于_id列默认是显示的,如果不显示需要明确设置为0
- 例1

    db.stu.find({},{name:1, gender:true})

- 例2

    db.stu.find({},{_id:0,name:1, gender:true})

#### 排序

- 方法sort(),用于对结果集进行排序
- 语法

    db.集合名称.find().sort({字段:1,...})

- 参数1为升序排列
- 参数-1为降序排列
- 例1:根据性别降序,再根据年龄升序

    db.stu.find().sort({gender:-1,age:1})

#### 统计个数

- 方法count()用于统计结果集中文档条数
- 语法

    db.集合名称.find({条件}).count()

- 也可以与为

    db.集合名称.count({条件})

- 例1:统计男生人数

    db.stu.find({gender:1}).count()

- 例2:统计年龄大于20的男性人数

    db.stu.count({age:{$gt:20},gender:true})

#### 消除重复

- 方法distinct()对数据进行去重
- 语法

    db.集合名称.distinct('去重字段',{条件})

- 例:查找年龄大于18的学生,来自哪些省份

    db.stu.distinct('hometown',{age:{$gt:18}})

### 聚合 aggregate
- 聚合(aggregate)主要用于计算数据,类似sql中的sum()、avg()
- 语法

    db.集合名称.aggregate([ {管道 : {表达式}} ])

#### 管道
- 管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的输入

    ps aux | grep mongod

- 在mongodb中,管道具有同样的作用,文档处理完毕后,通过管道进行下一次处理

- 常用管道
    - $group:将集合中的文档分组,可用于统计结果
    - $match:过滤数据,只输出符合条件的文档
    - $project:修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
    - $sort:将输入文档排序后输出
    - $limit:限制聚合管道返回的文档数
    - $skip:跳过指定数量的文档,并返回余下的文档
    - $unwind:将数组类型的字段进行拆分

#### 表达式
- 处理输入文档并输出
- 语法

    表达式:'$列名'

- 常用表达式

    - $sum:计算总和,$sum:1同count表示计数
    - $avg:计算平均值
    - $min:获取最小值
    - $max:获取最大值
    - $push:在结果文档中插入值到一个数组中
    - $first:根据资源文档的排序获取第一个文档数据
    - $last:根据资源文档的排序获取最后一个文档数据

db.stu.aggregate([{$group: {_id : "$age", _sum : {$sum : 1}}}]) { "_id" : 45, "_sum" : 1 } { "_id" : 16, "_sum" : 1 } { "_id" : 40, "_sum" : 1 } { "_id" : 18, "_sum" : 2 } { "_id" : 20, "_sum" : 1 }


#### $group
- 将集合中的文档分组,可用于统计结果
- _id表示分组的依据,使用某个字段的格式为'$字段'
- 例1:统计男生、女生的总人数

db.stu.aggregate([ {$group: { _id:'$gender', counter:{$sum:1} } } ])

##### Group by null
- 将集合中所有文档分为一组
- 例2:求学生总人数、平均年龄

db.stu.aggregate([ {$group: { _id:null, counter:{$sum:1}, avgAge:{$avg:'$age'} } } ])

##### 透视数据
- 例3:统计学生性别及学生姓名

db.stu.aggregate([ {$group: { _id:'$gender', name:{$push:'$name'} } } ])

- 使用$$ROOT可以将文档内容加入到结果集的数组中,代码如下

db.stu.aggregate([ {$group: { _id:'$gender', name:{$push:'$$ROOT'} } } ])


#### $match
- 用于过滤数据,只输出符合条件的文档
- 使用MongoDB的标准查询操作
- 例1:查询年龄大于20的学生

db.stu.aggregate([ {$match:{age:{$gt:20}}} ])

- 例2:查询年龄大于20的男生、女生人数

db.stu.aggregate([ {$match:{age:{$gt:20}}}, {$group:{_id:'$gender',counter:{$sum:1}}} ])


#### $project
- 修改输入文档的结构,如重命名、增加、删除字段、创建计算结果
- 例1:查询学生的姓名、年龄

db.stu.aggregate([ {$project:{_id:0,name:1,age:1}} ])

- 例2:查询男生、女生人数,输出人数

db.stu.aggregate([ {$group:{_id:'$gender',counter:{$sum:1}}}, {$project:{_id:0,counter:1}} ])


#### $sort
- 将输入文档排序后输出
- 例1:查询学生信息,按年龄升序

    b.stu.aggregate([{$sort:{age:1}}])

- 例2:查询男生、女生人数,按人数降序

db.stu.aggregate([ {$group:{_id:'$gender',counter:{$sum:1}}}, {$sort:{counter:-1}} ])


#### $limit
- 限制聚合管道返回的文档数
- 例1:查询2条学生信息

    db.stu.aggregate([{$limit:2}])

#### $skip
- 跳过指定数量的文档,并返回余下的文档
- 例2:查询从第3条开始的学生信息

    db.stu.aggregate([{$skip:2}])

- 例3:统计男生、女生人数,按人数升序,取第二条数据

db.stu.aggregate([ {$group:{_id:'$gender',counter:{$sum:1}}}, {$sort:{counter:1}}, {$skip:1}, {$limit:1} ])

- 注意顺序:先写skip,再写limit

#### $unwind
- 将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值

##### 语法1
- 对某字段值进行拆分

    db.集合名称.aggregate([{$unwind:'$字段名称'}])

- 构造数据

    db.t2.insert({_id:1,item:'t-shirt',size:['S','M','L']})

- 查询

    db.t2.aggregate([{$unwind:'$size'}])

##### 语法2

- 对某字段值进行拆分
- 处理空数组、非数组、无字段、null情况

db.inventory.aggregate([{ $unwind:{ path:'$字段名称', preserveNullAndEmptyArrays:#防止数据丢失 } }])

- 构造数据

db.t3.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 } ])

- 使用语法1查询

    db.t3.aggregate([{$unwind:'$size'}])

- 查看查询结果,发现对于空数组、无字段、null的文档,都被丢弃了
- 问:如何能不丢弃呢?
- 答:使用语法2查询

    db.t3.aggregate([{$unwind:{path:'$sizes',preserveNullAndEmptyArrays:true}}])

#### 索引
- 在mysql中已经学习了索引,并知道索引对于查询速度的提升
- mongodb也支持索引,以提升查询速度

##### 步骤一:创建大量数据
- 执行如下代码,向集合中插入10万条文档

for(i=0;i<100000;i++){ db.t1.insert({name:'test'+i,age:i}) }

##### 步骤二:数据查找性能分析
- 查找姓名为'test10000'的文档

    db.t1.find({name:'test10000'})

- 使用explain()命令进行查询性能分析

    db.t1.find({name:'test10000'}).explain('executionStats')

- 其中的executionStats下的executionTimeMillis表示整体查询时间,单位是毫秒
- 性能分析结果如下图

![avatar](https://img-hello-world.oss-cn-beijing.aliyuncs.com/91fadaddf2a5ed75ddf47c24a5111dfd.png)

##### 步骤三:建立索引
- 创建索引
- 1表示升序,-1表示降序

db.集合.ensureIndex({属性:1}) 如 db.t1.ensureIndex({name:1})


##### 步骤四:对索引属性查询
- 执行上面的同样的查询,并进行查询性能分析

    db.t1.find({name:'test10000'}).explain('executionStats')

- 性能分析结果如下图

![avatar](https://img-hello-world.oss-cn-beijing.aliyuncs.com/c6f03c66601319d6825009cddb3f7921.png)

##### 索引的命令
- 建立唯一索引,实现唯一约束的功能

    db.t1.ensureIndex({"name":1},{"unique":true})

- 联合索引,对多个属性建立一个索引,按照find()出现的顺序

    db.t1.ensureIndex({name:1,age:1})

- 查看文档所有索引

    db.t1.getIndexes()

- 删除索引

    db.t1.dropIndexes('索引名称')

#### 备份
- 语法

    mongodump -h dbhost -d dbname -o dbdirectory
- -h:服务器地址,也可以指定端口号
- -d:需要备份的数据库名称
- -o:备份的数据存放位置,此目录中存放着备份出来的数据
- 例1

    sudo mkdir test1bak
    sudo mongodump -h 192.168.196.128:27017 -d test1 -o ~/Desktop/test1bak

#### 恢复
- 语法

    mongorestore -h dbhost -d dbname --dir dbdirectory

- -h:服务器地址
- -d:需要恢复的数据库实例
- --dir:备份数据所在位置
- 例2

    mongorestore -h 192.168.196.128:27017 -d test2 --dir ~/Desktop/test1bak/test1

### 与python交互
- 点击查看官方文档
- 安装python包

    sudo pip install pymongo

- 引入包pymongo

    from pymongo import *

##### MongoClient对象
- 使用init方法创建连接对象

    client = MongoClient('主机ip',端口)

##### Database对象
- 通过client对象获取获得数据库对象

    db = client.数据库名称
##### Collections对象
- 通过db对象获取集合对象

    collections = db.集合名称

- 主要方法如下
    - insert_one:加入一条文档对象
    - insert_many:加入多条文档对象
    - find_one:查找一条文档对象
    - find:查找多条文档对象
    - update_one:更新一条文档对象
    - update_many:更新多条文档对象
    - delete_one:删除一条文档对象
    - delete_many:删除多条文档对象

##### Cursor对象
- 当调用集合对象的find()方法时,会返回Cursor对象
- 结合for...in...遍历cursor对象

#### 增加
- 创建mongodb_insert1.py文件,增加一条文档对象

#coding=utf-8

from pymongo import *

try: # 接收输入 name=raw_input('请输入姓名:') home=raw_input('请输入家乡:') # 构造json对象 doc={'name':name,'home':home} #调用mongo对象,完成insert client=MongoClient('localhost',27017) db=client.py3 db.stu.insert_one(doc) print 'ok' except Exception,e: print e

- 创建mongodb_insert2.py文件,增加多条文档对象

#coding=utf-8

from pymongo import *

try: # 构造json对象 doc1={'name':'hr','home':'thd'} doc2={'name':'mnc','home':'njc'} doc=[doc1,doc2] #调用mongo对象,完成insert client=MongoClient('localhost',27017) db=client.py3 db.stu.insert_many(doc) print 'ok' except Exception,e: print e


#### 查询
- 创建mongodb_find1.py文件,查询一条文档对象

#coding=utf-8

from pymongo import *

try: client=MongoClient('localhost',27017) db=client.py3 doc=db.stu.find_one() print '%s--%s'%(doc['name'],doc['hometown']) except Exception,e: print e

- 创建mongodb_find2.py文件,查询多条文档对象

#coding=utf-8

from pymongo import *

try: client=MongoClient('localhost',27017) db=client.py3 cursor=db.stu.find({'hometown':'大理'}) for doc in cursor: print '%s--%s'%(doc['name'],doc['hometown']) except Exception,e: print e

#### 修改
- 创建mongodb_update1.py文件,修改一条文档对象

#coding=utf-8

from pymongo import *

try: client=MongoClient('localhost',27017) db=client.py3 db.stu.update_one({'gender':False},{'$set':{'name':'hehe'}}) print 'ok' except Exception,e: print e

- 创建mongodb_update2.py文件,修改多条文档对象

#coding=utf-8

from pymongo import *

try: client=MongoClient('localhost',27017) db=client.py3 db.stu.update_many({'gender':True},{'$set':{'name':'haha'}}) print 'ok' except Exception,e: print e

#### 删除
- 创建mongodb_delete1.py文件,删除一条文档对象

#coding=utf-8

from pymongo import *

try: client=MongoClient('localhost',27017) db=client.py3 db.stu.delete_one({'gender':True}) print 'ok' except Exception,e: print e

- 创建mongodb_delete2.py文件,删除多条文档对象

#coding=utf-8

from pymongo import *

try: client=MongoClient('localhost',27017) db=client.py3 db.stu.delete_many({'gender':False}) print 'ok' except Exception,e: print e

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
Python3:sqlalchemy对mysql数据库操作,非sql语句
Python3:sqlalchemy对mysql数据库操作,非sql语句python3authorlizmdatetime2018020110:00:00coding:utf8'''
Wesley13 Wesley13
3年前
4cast
4castpackageloadcsv.KumarAwanish发布:2020122117:43:04.501348作者:KumarAwanish作者邮箱:awanish00@gmail.com首页:
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
11个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
隔壁老王
隔壁老王
Lv1
千万程序员队伍中的一员。我住隔壁我姓王,同事们亲切得称呼我隔壁老王
文章
20
粉丝
2
获赞
7