MongoDb简介

知识点名
"什么是MongoDB ?
MongoDB 是由C++语言编写的,是一个基于分布式文件存储的开源数据库系统。

分布式系统
分布式系统(distributed system)由多台计算机和通信的软件组件通过计算机网络连接(本地网络或广域网)组成。
分布式系统是建立在网络之上的软件系统。正是因为软件的特性,所以分布式系统具有高度的内聚性和透明性。
因此,网络和分布式系统之间的区别更多的在于高层软件(特别是操作系统),而不是硬件。
分布式系统可以应用在不同的平台上如:Pc、工作站、局域网和广域网上等。

分布式计算的优点
可靠性(容错) :
分布式计算系统中的一个重要的优点是可靠性。一台服务器的系统崩溃并不影响到其余的服务器。
可扩展性:
在分布式计算系统可以根据需要增加更多的机器。
资源共享:
共享数据是必不可少的应用,如银行,预订系统。
灵活性:
由于该系统是非常灵活的,它很容易安装,实施和调试新的服务。
更快的速度:
分布式计算系统可以有多台计算机的计算能力,使得它比其他系统有更快的处理速度。
开放系统:
由于它是开放的系统,本地或者远程都可以访问到该服务。
更高的性能:
相较于集中式计算机网络集群可以提供更高的性能(及更好的性价比)。

分布式计算的缺点
故障排除:
故障排除和诊断问题。
软件:
更少的软件支持是分布式计算系统的主要缺点。
网络:
网络基础设施的问题,包括:传输问题,高负载,信息丢失等。
安全性:
开放系统的特性让分布式计算系统存在着数据的安全性和共享的风险等问题。

什么是NoSQL?
NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。
NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。
为什么使用NoSQL ?
今天我们可以通过第三方平台(如:Google,Facebook等)可以很容易的访问和抓取数据。用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了, NoSQL数据库的发展也却能很好的处理这些大的数据。

RDBMS vs NoSQL
RDBMS
- 高度组织化结构化数据
- 结构化查询语言(SQL) (SQL)
- 数据和关系都存储在单独的表中。
- 数据操纵语言,数据定义语言
- 严格的一致性
- 基础事务

NoSQL
- 代表着不仅仅是SQL
- 没有声明性查询语言
- 没有预定义的模式
-键 - 值对存储,列存储,文档存储,图形数据库
- 最终一致性,而非ACID属性
- 非结构化和不可预知的数据
- CAP定理
- 高性能,高可用性和可伸缩性

ACID vs BASE
ACID BASE
原子性(Atomicity) 基本可用(Basically Available)
一致性(Consistency) 软状态/柔性事务(Soft state)
隔离性(Isolation) 最终一致性 (Eventual consistency)
持久性 (Durable)

NoSQL 数据库分类
列存储式:
Hbase、Cassandra、Hypertable
特点:
是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。

文档存储:
MongoDB、CouchDB
特点:
文档存储一般用类似json的格式存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。

key-value存储
Tokyo Cabinet / Tyrant、Berkeley DB、MemcacheDB、Redis
特点:
可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。(Redis包含了其他功能)

图存储:
Neo4J、FlockDB
特点:
图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。

对象存储:
db4o、Versant
特点:
通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。

xml数据库:
Berkeley DB XML、BaseX
特点
高效的存储XML数据,并支持XML的内部查询语法,比如XQuery,Xpath。" 1



"主要特点
MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
你可以在MongoDB记录中设置任何属性的索引 (如:FirstName=""Sameer"",Address=""8 Gandhi Road"")来实现更快的排序。
你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。
如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。
Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
MongoDB安装简单。"
"历史
2007年10月,MongoDB由10gen团队所发展。2009年2月首度推出。
2012年05月23日,MongoDB2.1 开发分支发布了! 该版本采用全新架构,包含诸多增强。
2012年06月06日,MongoDB 2.0.6 发布,分布式文档数据库。
2013年04月23日,MongoDB 2.4.3 发布,此版本包括了一些性能优化,功能增强以及bug修复。
2013年08月20日,MongoDB 2.4.6 发布。
2013年11月01日,MongoDB 2.4.8 发布。"
"MongoDB 下载
你可以在mongodb官网下载该安装包,地址为:https://www.mongodb.com/download-center#community。MonggoDB支持以下平台:

OS X 32-bit
OS X 64-bit
Linux 32-bit
Linux 64-bit
Windows 32-bit
Windows 64-bit
Solaris i86pc
Solaris 64"
"MongoDB 工具
有几种可用于MongoDB的管理工具。

监控
MongoDB提供了网络和系统监控工具Munin,它作为一个插件应用于MongoDB中。
Gangila是MongoDB高性能的系统监视的工具,它作为一个插件应用于MongoDB中。
基于图形界面的开源工具 Cacti, 用于查看CPU负载, 网络带宽利用率,它也提供了一个应用于监控 MongoDB 的插件。

GUI
Fang of Mongo – 网页式,由Django和jQuery所构成。
Futon4Mongo – 一个CouchDB Futon web的mongodb山寨版。
Mongo3 – Ruby写成。
MongoHub – 适用于OSX的应用程序。
Opricot – 一个基于浏览器的MongoDB控制台, 由PHP撰写而成。
Database Master — Windows的mongodb管理工具
RockMongo — 最好的PHP语言的MongoDB管理工具,轻量级, 支持多国语言.
MongoDB 应用案例
下面列举一些公司MongoDB的实际应用:
Craiglist上使用MongoDB的存档数十亿条记录。
FourSquare,基于位置的社交网站,在Amazon EC2的服务器上使用MongoDB分享数据。
Shutterfly,以互联网为基础的社会和个人出版服务,使用MongoDB的各种持久性数据存储的要求。
bit.ly, 一个基于Web的网址缩短服务,使用MongoDB的存储自己的数据。
spike.com,一个MTV网络的联营公司, spike.com使用MongoDB的。
Intuit公司,一个为小企业和个人的软件和服务提供商,为小型企业使用MongoDB的跟踪用户的数据。
sourceforge.net,资源网站查找,创建和发布开源软件免费,使用MongoDB的后端存储。
etsy.com ,一个购买和出售手工制作物品网站,使用MongoDB。
纽约时报,领先的在线新闻门户网站之一,使用MongoDB。
CERN,著名的粒子物理研究所,欧洲核子研究中心大型强子对撞机的数据使用MongoDB。"
"Windows 平台安装 MongoDB:
1.MongoDB 下载
MongoDB 预编译二进制包下载地址:https://www.mongodb.com/download-center#community

2.根据你的系统下载 32 位或 64 位的 .msi 文件,下载后双击该文件,按操作提示安装即可。
安装过程中,你可以通过点击 ""Custom(自定义)"" 按钮来设置你的安装目录。
"
"MongoDB启动服务并连接:命令行方式启动
MongoDB将数据目录存储在 db 目录下。但是这个数据目录不会主动创建,我们在安装完成后需要创建它。请注意,数据目录应该放在根目录下((如: C:\ 或者 D:\ 等 )。
在根盘符下创建一个 data 的目录,然后在 data 目录里创建 db 目录。
命令行下运行 MongoDB 服务器
为了从命令提示符下运行 MongoDB 服务器,你必须从 MongoDB 目录的 bin 目录中执行 mongod.exe 文件。
C:\mongodb\bin\mongod --dbpath c:\data\db

连接MongoDB
我们可以在命令窗口中运行 mongo.exe 命令即可连接上 MongoDB,执行如下命令:
C:\mongodb\bin\mongo.exe"
"MongoDB启动服务并连接:配置服务方式启动
管理员模式打开命令行窗口
创建目录,执行下面的语句来创建数据库和日志文件的目录
mkdir D:\mongoDatas\db
mkdir D:\mongoDatas\log

创建配置文件
创建一个配置文件。
例如,创建一个配置文件位于 D:\mongoDatas\conf\mongodb.conf,具体配置内容如下:
dbpath=D:\mongoDatas\db
logpath=D:\mongoDatas\log\mongodb.log
logappend=true
bind_ip=0.0.0.0
port=27019
#fork=true
#master=true
auth=true

在命令行窗口运行mongodb服务器
输入命令:mongod -f D:/mongoDatas/conf/mongodb.conf

在命令行窗口运行shell界面配置用户名密码
注意:直接输入mongo连接数据库默认ip是127.0.0.1,端口是270717
输入命令:mongo.exe mongodb://localhost:27019
在shell界面配置账号管理员root,利用此账号可以增加其他账号
注意:账号管理员没有读写的权限,只能增加其他账号
输入命令:use admin
输入命令:
db.createUser
({user:""root"",pwd:""root"",roles:[{""role"":""userAdminAnyDatabase"",""db"":""admin""}]})?
输入命令登陆账号管理员:
db.auth(""root"", ""root"")
权限说明:
userAdminAnyDatabase 有分配角色和用户的权限

重新运行mongodb服务器
将mongodb.conf中的auth设置为true
输入命令:
mongod -f D:/mongoDatas/conf/mongodb.conf重新运行shell

输入命令:mongo localhost:27019/admin -u root -p root
测试账号是否成功
测试账号:
> show dbs;

安装 MongoDB服务
通过执行mongod.exe,使用--install选项来安装服务,使用--config选项来指定之前创建的配置文件。
C:\mongodb\bin\mongod.exe --config ""D:/mongoDatas/conf/mongodb.conf"" --install
要使用备用 dbpath,可以在配置文件(例如:D:/mongoDatas/conf/mongodb.conf)或命令行中通过 --dbpath 选项指定。
如果需要,您可以安装 mongod.exe 或 mongos.exe 的多个实例的服务。只需要通过使用 --serviceName 和 --serviceDisplayName 指定不同的实例名。只有当存在足够的系统资源和系统的设计需要这么做。

启动MongoDB服务
net start MongoDB

关闭MongoDB服务
net stop MongoDB

移除 MongoDB 服务
C:\mongodb\bin\mongod.exe --remove

连接MongoDB
我们可以在命令窗口中运行 mongo.exe 命令即可连接上 MongoDB,执行如下命令:
C:\mongodb\bin\mongo.exe"


"MongoDB 后台管理 Shell
打开mongodb装目录的下的bin目录,然后执行mongo.exe文件,MongoDB Shell是MongoDB自带的交互式Javascript shell,用来对MongoDB进行操作和管理的交互式环境
当你进入mongoDB后台后,它默认会链接到 test 文档(数据库):
db 命令用于查看当前操作的文档(数据库):
> db
test
>

由于它是一个JavaScript shell,您可以运行一些简单的算术运算:
> 2 + 2
4
>

插入一些简单的记录并查找它:
> db.runoob.insert({x:10})
WriteResult({ ""nInserted"" : 1 })
> db.runoob.find()
{ ""_id"" : ObjectId(""5604ff74a274a611b0c990aa""), ""x"" : 10 }
>
第一个命令将数字 10 插入到 runoob 集合的 x 字段中。"
"MongoDB 基础解析
传统数据库和MongoDB术语的区别
SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持
primary key primary key 主键,MongoDB自动将_id字段设置为主键

数据库
一个mongodb中可以建立多个数据库。
MongoDB的默认数据库为""db"",该数据库存储在data目录中。
MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
""show dbs"" 命令可以显示所有数据的列表。
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
test 0.000GB
>
执行 ""db"" 命令可以显示当前数据库对象或集合。
> db
test
>
运行""use""命令,可以连接到一个指定的数据库。
> use local
switched to db local
> db
local
>

数据库命名规则:
数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串。
不能是空字符串("""")。
不得含有' '(空格)、.、$、/、\和\0 (空字符)。
应全部小写。
最多64字节。
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库:
admin: 从权限的角度来看,这是""root""数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。"

"文档
文档是一组键值(key-value)对(即BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。

一个简单的文档例子如下:

{""site"":""www.runoob.com"", ""name"":""菜鸟教程""}
下表列出了 RDBMS 与 MongoDB 对应的术语:

RDBMS MongoDB
数据库 数据库
表格 集合
行 文档
列 字段
表联合 嵌入文档
主键 主键 (MongoDB 提供了 key 为 _id )

数据库服务和客户端
Mysqld/Oracle mongod
mysql/sqlplus mongo

需要注意的是:
文档中的键/值对是有序的。
文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
MongoDB区分类型和大小写。
MongoDB的文档不能有重复的键。
文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。
文档键命名规范:

键不能含有\0 (空字符)。这个字符用来表示键的结尾。
.和$有特别的意义,只有在特定环境下才能使用。
以下划线""_""开头的键是保留的(不是严格要求的)。"

"集合
集合就是 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.""开头,这是为系统集合保留的前缀。
用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。 
如下实例:

db.col.findOne()
capped collections
Capped collections 就是固定大小的collection。

它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 ""RRD"" 概念类似。

Capped collections 是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能和标准的 collection 不同,你必须要显式的创建一个capped collection,指定一个 collection 的大小,单位是字节。collection 的数据存储空间值提前分配的。

Capped collections 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当我们更新Capped collections 中文档的时候,更新后的文档不可以超过之前文档的大小,这样话就可以确保所有文档在磁盘上的位置一直保持不变。

由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。

要注意的是指定的存储大小包含了数据库的头信息。

db.createCollection(""mycoll"", {capped:true, size:100000})
在 capped collection 中,你能添加新的对象。
能进行更新,然而,对象不会增加存储空间。如果增加,更新就会失败 。
使用 Capped Collection 不能删除一个文档,可以使用 drop() 方法删除 collection 所有的行。
删除之后,你必须显式的重新创建这个 collection。
在32bit机器中,capped collection 最大存储为 1e9( 1X109)个字节。"

"元数据
数据库的信息是存储在集合中。它们使用了系统的命名空间:

dbname.system.*
在MongoDB数据库中名字空间 <dbname>.system.* 是包含多种系统信息的特殊集合(Collection),如下:

集合命名空间 描述
dbname.system.namespaces 列出所有名字空间。
dbname.system.indexes 列出所有索引。
dbname.system.profile 包含数据库概要(profile)信息。
dbname.system.users 列出所有可访问数据库的用户。
dbname.local.sources 包含复制对端(slave)的服务器信息和状态。
对于修改系统集合中的对象有如下限制。
在{{system.indexes}}插入数据,可以创建索引。但除此之外该表信息是不可变的(特殊的drop index命令将自动更新相关信息)
{{system.users}}是可修改的。 {{system.profile}}是可删除的。"
"MongoDB 数据类型
下表为MongoDB中常用的几种数据类型。

数据类型 描述
String 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean 布尔值。用于存储布尔值(真/假)。
Double 双精度浮点值。用于存储浮点值。
Min/Max keys 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array 用于将数组或列表或多个值存储为一个键。
Timestamp 时间戳。记录文档修改或添加的具体时间。
Object 用于内嵌文档。
Null 用于创建空值。
Symbol 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID 对象 ID。用于创建文档的 ID。
Binary Data 二进制数据。用于存储二进制数据。
Code 代码类型。用于在文档中存储 JavaScript 代码。
Regular expression 正则表达式类型。用于存储正则表达式。"

"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的基本操作
Mongodb中关键字种类:
db(数据库实例级别)
db本身
db.connection 数据库下的集合信息
db.collection.xxx()
rs(复制集级别)
sh(分片级别)

查询操作
查看当前数据库版本
> db.version()
4.0.4

切换数据库
> use test;
switched to db test

显示当前数据库
> db
test
> db.getName()
test

查询所有数据库
> show dbs;
clsn 0.000GB
local 0.000GB
test 0.000GB
> show databases;
clsn 0.000GB
local 0.000GB
test 0.000GB

查看clsn数据库当前状态
> use clsn;
> db.stats()
{
""db"" : ""clsn"",
""collections"" : 1,
""objects"" : 10000,
""avgObjSize"" : 80,
""dataSize"" : 800000,
""storageSize"" : 258048,
""numExtents"" : 0,
""indexes"" : 1,
""indexSize"" : 94208,
""ok"" : 1
}

查看当前数据库的连接机器地址
> db.getMongo()
connection to 127.0.0.1

数据管理
创建数据库
> use clsn;
说明:
创建数据库:
当use的时候,系统就会自动创建一个数据库。
如果use之后没有创建任何集合。系统就会删除这个数据库。

删除数据库
> use clsn
switched to db clsn
> db.dropDatabase()
{ ""dropped"" : ""clsn"", ""ok"" : 1 }
说明:删除数据库:如果没有选择任何数据库,会删除默认的test数据库

创建集合
方法一:
> db.createCollection('a')
{ ""ok"" : 1 }
查看当前数据下的所有集合
> show collections;
a
> db.getCollectionNames()
[ ""a"" ]
方法二:
当插入一个文档的时候,一个集合就会自动创建。
> db.c.insert({name:'clsn'});
WriteResult({ ""nInserted"" : 1 })
查看创建的合集
> db.getCollectionNames()
[ ""a"",""c"" ]
查看合集里的内容
> db.c.find()
{ ""_id"" : ObjectId(""5a4cbcea83ec78b7bea904f8""), ""name"" : ""clsn"" }
重命名集合
> db.c.renameCollection(""clsn"")
{ ""ok"" : 1 }
> db.getCollectionNames()
[ ""a"",""clsn"" ]
删除合集
> db.a.drop()
true
> db.getCollectionNames()
[""clsn"" ]
插入1w行数据
> for(i=0;i<10000;i++){ db.log.insert({""uid"":i,""name"":""mongodb"",""age"":6,""date"":new Date()}); }
WriteResult({ ""nInserted"" : 1 })
查询集合中的查询所有记录
> db.log.find()
注:默认每页显示20条记录,当显示不下的的情况下,可以用it迭代命令查询下一页数据。
查询限定:
> DBQuery.shellBatchSize=50; # 每页显示50条记录
50
> db.log.findOne() # 查看第1条记录
> db.log.count() # 查询总的记录数
> db.log.find({uid:1000}); # 查询UUID为1000的数据
删除集合中的记录数
> db.log.distinct(""name"") # 查询去掉当前集合中某列的重复数据
[ ""mongodb"" ]
> db.log.remove({}) # 删除集合中所有记录
WriteResult({ ""nRemoved"" : 10000 })
> db.log.distinct(""name"")
[ ]
查看集合存储信息
> db.log.stats() # 查看数据状态
> db.log.dataSize() # 集合中数据的原始大小
> db.log.totalIndexSize() # 集合中索引数据的原始大小
> db.log.totalSize() # 集合中索引+数据压缩存储之后的大小
> db.log.storageSize() # 集合中数据压缩存储的大小
pretty()使用:格式化查询数据
> db.log.find({uid:1000}).pretty()
显示效果如下:
{
""_id"" : ObjectId(""5a4c5c0bdf067ab57602f7c2""),
""uid"" : 1000,
""name"" : ""mongodb"",
""age"" : 6,
""date"" : ISODate(""2018-01-03T04:28:59.343Z"")
}
。。。"









"MongoDB 创建集合
语法格式:
db.createCollection(name, options)
参数说明:
name: 要创建的集合名称
options: 可选参数, 指定有关内存大小及索引的选项
options 可以是如下参数:
capped 布尔 (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。当该值为 true 时,必须指定 size 参数。
autoIndexId 布尔 (可选)如为 true,自动在 _id 字段创建索引。默认为 false。
size 数值 (可选)为固定集合指定一个最大值(以字节计)。如果 capped 为 true,也需要指定该字段。
max 数值 (可选)指定固定集合中包含文档的最大数量。在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。

实例
创建 runoob 集合:
> db.createCollection(""runoob"")
{ ""ok"" : 1 }
>
如果要查看已有集合,可以使用 show collections 命令:
> show collections
runoob
system.indexes
下面是带有几个关键参数的 createCollection() 的用法:
创建固定集合 mycol,整个集合空间大小 6142800 KB, 文档最大个数为 10000 个。
> db.createCollection(""mycol"", { capped : true, autoIndexId : true, size :
6142800, max : 10000 } )
{ ""ok"" : 1 }"
"MongoDB 删除集合
语法格式:
db.collection.drop()
返回值
如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。
例:
删除集合 mycol2 :
>db.mycol2.drop()
true"
"MongoDB 插入文档
文档的数据结构和JSON基本一样,都是BSON格式。
BSON:类似json的二进制形式存储格式,简称Binary JSON
MongoDB 使用 insert() 或 save() 方法向集合中插入文档,语法如下:
db.COLLECTION_NAME.insert(document)
或者
db.col.save(document) 命令。
如果不指定 _id 字段 save() 方法类似于 insert() 方法。
如果指定 _id 字段,则会更新该 _id 的数据。
实例
>db.col.insert
({
title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})
以上实例中如果该集合不存在,会自动创建该集合并插入文档。
可以将数据定义为一个变量,如下所示:
> document=({title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
});
执行插入操作:
> db.col.insert(document)
WriteResult({ ""nInserted"" : 1 })"
"MongoDB 更新文档
update() 和 save() 方法来更新集合中的文档
update() 方法
update() 方法用于更新已存在的文档。语法格式如下:
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
参数说明:
query : update的查询条件,类似where后面的条件
update : update的对象和一些更新的操作符(如$,$inc...)等,类似set后面的
upsert : 可选,如果不存在,是否插入objNew,true为插入,默认是false,不插入。
multi : 可选,默认是false,只更新找到的第一条记录,为true,就把按条件查出来多条记录全部更新
writeConcern :可选,抛出异常的级别。
实例
我们在集合 col 中插入如下数据:
>db.col.insert({
title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})
接着我们通过 update() 方法来更新标题(title):
>db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}})
WriteResult({ ""nMatched"" : 1, ""nUpserted"" : 0, ""nModified"" : 1 }) # 输出信息
> db.col.find().pretty()
{
""_id"" : ObjectId(""56064f89ade2f21f36b03136""),
""title"" : ""MongoDB"",
......
}
>
可以 ""MongoDB 教程"" 更新为了 ""MongoDB""。

以上语句只会修改第一条,要修改多条相同的文档,则 multi 参数为 true。
>db.col.update({'title':'MongoDB 教程'},{$set:{'title':'MongoDB'}},{multi:true})

save() 方法
save() 方法通过传入的文档来替换已有文档。语法格式如下:
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
参数说明:
document : 文档数据。
writeConcern :可选,抛出异常的级别。
实例
替换 _id 为 56064f89ade2f21f36b03136 的文档数据:
>db.col.save({
""_id"" : ObjectId(""56064f89ade2f21f36b03136""),
""title"" : ""MongoDB_qiku"",
})
替换成功后,我们可以通过 find() 命令来查看替换后的数据

>db.col.find().pretty()
{
""_id"" : ObjectId(""56064f89ade2f21f36b03136""),
""title"" : ""MongoDB_qiku"",
}
>
更多实例
只更新第一条记录:
db.col.update( { ""count"" : { $gt : 1 } } , { $set : { ""test2"" : ""OK""} } );
全部更新:
db.col.update( { ""count"" : { $gt : 3 } } , { $set : { ""test2"" : ""OK""} },false,true );
只添加第一条:
db.col.update( { ""count"" : { $gt : 4 } } , { $set : { ""test5"" : ""OK""} },true,false );
全部添加进去:
db.col.update( { ""count"" : { $gt : 5 } } , { $set : { ""test5"" : ""OK""} },true,true );
全部更新:
db.col.update( { ""count"" : { $gt : 15 } } , { $inc : { ""count"" : 1} },false,true );
只更新第一条记录:
db.col.update( { ""count"" : { $gt : 10 } } , { $inc : { ""count"" : 1} },false,false );"


"MongoDB 删除文档
remove()函数是用来移除集合中的数据。
在执行remove()函数前先执行find()命令来判断执行的条件是否正确,这是一个比较好的习惯。
语法
remove() 方法的基本语法格式如下所示:
db.collection.remove(
<query>,
<justOne>
)
若MongoDB 是 2.6 版本以后的,语法格式如下:
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
参数说明:
query :(可选)删除的文档的条件。
justOne : (可选)如果为 true 或 1,只删除一个文档,如果不设置该参数或使用默认值 false,则删除所有匹配条件的文档
writeConcern :(可选)抛出异常的级别。
实例
以下文档我们执行两次插入操作:
>db.col.insert({title: 'MongoDB 教程',
description: 'MongoDB 是一个 Nosql 数据库',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
})
使用 find() 函数查询数据:
> db.col.find()
接下来我们移除 title 为 'MongoDB 教程' 的文档:
>db.col.remove({'title':'MongoDB 教程'})
WriteResult({ ""nRemoved"" : 2 }) # 删除了两条数据
>db.col.find()
…… # 没有数据
只想删除第一条找到的记录可以设置 justOne 为 1:
>db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)
如果你想删除所有数据,可以使用以下方式(类似常规 SQL 的 truncate 命令):
>db.col.remove({})"

"MongoDB 查询文档
find() 方法以非结构化的方式来显示所有文档。
语法
db.collection.find(query, projection)
query :可选,使用查询操作符指定查询条件
projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
易读的方式来读取数据,可以使用 pretty() 方法:
>db.col.find().pretty()#以格式化的方式来显示所有文档。
实例
> db.col.find().pretty()
{
""_id"" : ObjectId(""56063f17ade2f21f36b03133""),
""title"" : ""MongoDB 教程"",
""likes"" : 100
}
findOne() 方法,它只返回一个文档。
MongoDB 与 RDBMS Where 语句比较
操作 格式 范例 RDBMS中的类似语句
= {<key>:<value>} db.col.find({""by"":""菜鸟教程""}) where by = '教程'
< {<key>:{$lt:<value>}} db.col.find({""likes"":{$lt:50}}) where likes < 50
<= {<key>:{$lte:<value>}} db.col.find({""likes"":{$lte:50}}) where likes <= 50
> {<key>:{$gt:<value>}} db.col.find({""likes"":{$gt:50}}) where likes > 50
>= {<key>:{$gte:<value>}} db.col.find({""likes"":{$gte:50}}) where likes >= 50
!= {<key>:{$ne:<value>}} db.col.find({""likes"":{$ne:50}}) where likes != 50
AND 条件
语法:
>db.col.find({key1:value1, key2:value2}).pretty()
实例
以下实例通过 by 和 title 键来查询数据
> db.col.find({""by"":""教程"", ""title"":""MongoDB 教程""}).pretty()
以上实例中类似于 WHERE 语句:WHERE by='教程' AND title='MongoDB 教程'

OR 条件
MongoDB OR 条件语句使用了关键字 $or,语法格式如下:
>db.col.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
实例
查询键 by 值为 教程 或键 title 值为 MongoDB 教程 的文档。
>db.col.find({$or:[{""by"":""教程""},{""title"": ""MongoDB 教程""}]}).pretty()

AND 和 OR 联合使用
以下实例演示类似 SQL 语句为:
where likes>50 AND (by = '教程' OR title = 'MongoDB 教程')
>db.col.find({""likes"": {$gt:50}, $or: [{""by"": ""菜鸟教程""},{""title"": ""MongoDB 教程""}]}).pretty()

补充一下 projection 参数的使用方法
db.collection.find(query, projection)
若不指定 projection,则默认返回所有键,指定 projection 格式如下,有两种模式
db.collection.find(query, {title: 1, by: 1}) // inclusion模式 指定返回的键,不返回其他键
db.collection.find(query, {title: 0, by: 0}) // exclusion模式 指定不返回的键,返回其他键
_id 键默认返回,需要主动指定 _id:0 才会隐藏
两种模式不可混用(因为这样的话无法推断其他键是否应返回)
db.collection.find(query, {title: 1, by: 0}) // 错误
只能全1或全0,除了在inclusion模式时可以指定_id为0
db.collection.find(query, {_id:0, title: 1, by: 1}) // 正确

若不想指定查询条件参数 query 可以 用 {} 代替,但是需要指定 projection 参数:
querydb.collection.find({}, {title: 1})

如果是 qty 大于 50 小于 80 不能这样写:
db.posts.find( { qty: { $gt: 50 }, qty: { $lt: 80 } } )
应该这样:
db.posts.find( { qty: { $gt: 50 ,$lt: 80}} )"


"MongoDB 条件操作符
条件操作符用于比较两个表达式并从mongoDB集合中获取数据。
条件操作符有:
(>) 大于 - $gt
(<) 小于 - $lt
(>=) 大于等于 - $gte
(<= ) 小于等于 - $lte
为了方便测试,我们可以先使用以下命令清空集合 ""col"" 的数据:
db.col.remove({})
插入以下数据
>db.col.insert({
title: 'PHP 教程',
tags: ['php'],
likes: 200
})
>db.col.insert({
title: 'Java 教程',
tags: ['java'],
likes: 150
})
>db.col.insert({
title: 'MongoDB 教程',
tags: ['mongodb'],
likes: 100
})
使用find()命令查看数据:
> db.col.find()

MongoDB (>) 大于操作符 - $gt
获取 ""col"" 集合中 ""likes"" 大于 100 的数据:
db.col.find({likes : {$gt : 100}})
类似于SQL语句:Select * from col where likes > 100;

MongoDB(>=)大于等于操作符 - $gte
获取""col""集合中 ""likes"" 大于等于 100 的数据:
db.col.find({likes : {$gte : 100}})
类似于SQL语句:Select * from col where likes >=100;

MongoDB (<) 小于操作符 - $lt
获取""col""集合中 ""likes"" 小于 150 的数据:
db.col.find({likes : {$lt : 150}})
类似于SQL语句:Select * from col where likes < 150;

MongoDB (<=) 小于操作符 - $lte
获取""col""集合中 ""likes"" 小于等于 150 的数据:
db.col.find({likes : {$lte : 150}})
类似于SQL语句:Select * from col where likes <= 150;

MongoDB 使用 (<) 和 (>) 查询 - $lt 和 $gt
获取""col""集合中 ""likes"" 大于100,小于 200 的数据:
db.col.find({likes : {$lt :200, $gt : 100}})
类似于SQL语句:Select * from col where likes>100 AND likes<200;

模糊查询
查询 title 包含""教""字的文档:
db.col.find({title:/教/})
查询 title 字段以""教""字开头的文档:
db.col.find({title:/^教/})
查询 titl e字段以""教""字结尾的文档:
db.col.find({title:/教$/})"


"MongoDB $type 操作符
$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。
MongoDB 中可以使用的类型如下表所示:
类型 数字 备注
Double 1
String 2
Object 3
Array 4
Binary data 5
Undefined 6 已废弃。
Object id 7
Boolean 8
Date 9
Null 10
Regular Expression 11
JavaScript 13
Symbol 14
JavaScript (with scope) 15
32-bit integer 16
Timestamp 17
64-bit integer 18
Min key 255 Query with -1.
Max key 127

MongoDB 操作符 - $type 实例
获取 ""col"" 集合中 title 为 String 的数据:
db.col.find({""title"" : {$type : 2}})

db.col.find({""title"" : {$type : 'string'}})"

"MongoDB Limit与Skip方法
MongoDB Limit() 方法
读取指定数量的数据记录,使用Limit方法,limit()方法接受一个数字参数,该参数指定读取的记录条数

语法
limit()方法基本语法如下所示:
>db.COLLECTION_NAME.find().limit(NUMBER)
实例
以下实例为显示查询文档中的两条记录:
> db.col.find({},{""title"":1,_id:0}).limit(2)
{ ""title"" : ""PHP 教程"" }
{ ""title"" : ""Java 教程"" }
>
注:如果你们没有指定limit()方法中的参数则显示集合中的所有数据。

MongoDB Skip() 方法
使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。

语法
>db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)
实例
以下实例只会显示第二条文档数据
>db.col.find({},{""title"":1,_id:0}).limit(1).skip(1)
{ ""title"" : ""Java 教程"" }
>
注:skip()方法默认参数为 0

db.col.find({},{""title"":1,_id:0}).limit(2)
补充说明:
第一个 {} 放 where 条件,为空表示返回集合中所有文档。
第二个 {} 指定那些列显示和不显示 (0表示不显示 1表示显示)。

读取从 10 条记录后 100 条记录,相当于 sql 中limit (10,100)。
> db.COLLECTION_NAME.find().skip(10).limit(100)
以上实例在集合中跳过前面 10 条返回 100 条数据。

skip 和 limit 结合就能实现分页。
补充说明skip和limit方法只适合小数据量分页,如果是百万级效率就会非常低,因为skip方法是一条条数据数过去的,建议使用where_limit:
db.test.sort({""amount"":1}).skip(100000).limit(10) //183ms
db.test.find({amount:{$gt:2399927}}).sort({""amount"":1}).limit(10) //53ms

limit(n) 是用来规定显示的条数,而 skip(n) 是用来在符合条件的记录中从第一个记录跳过的条数,这两个函数可以交换使用

比如:find({},{age:1,_id:0}).limit(2).skip(1),在符合条件的文档中,要显示两条文档,显示的位置从跳过第一条记录开始。这样不是很好理解。

如果写成 find({},{age:1,_id:0}).skip(1).limit(2),在符合条件的文档中,先跳过第一条文档,然后显示两条文档,这样比较好理解。

需要注意的是,此处的skip,sort,和limit三者执行顺序和位置无关,但是在聚合aggregate中使用的时候,具有管道流的特质,执行顺序是按照位置关系顺序执行的。"

"MongoDB 排序
MongoDB sort() 方法
使用 sort() 方法对数据进行排序,通过参数指定排序的字段,使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列
语法
sort()方法基本语法如下所示:
>db.COLLECTION_NAME.find().sort({KEY:1})
实例
col 集合中的数据如下:
{ ""_id"" : ObjectId(""56066542ade2f21f36b0313a""), ""title"" : ""PHP 教程"", ""tags"" : [ ""php"" ], ""likes"" : 200 }
{ ""_id"" : ObjectId(""56066549ade2f21f36b0313b""), ""title"" : ""Java 教程"", ""tags"" : [ ""java"" ], ""likes"" : 150 }
{ ""_id"" : ObjectId(""5606654fade2f21f36b0313c""), ""title"" : ""MongoDB 教程"", ""tags"" : [ ""mongodb"" ], ""likes"" : 100 }
按字段 likes 的降序排列:
>db.col.find({},{""title"":1,_id:0}).sort({""likes"":-1})
{ ""title"" : ""PHP 教程"" }
{ ""title"" : ""Java 教程"" }
{ ""title"" : ""MongoDB 教程"" }
skip(), limilt(), sort()三个放在一起执行的时候,执行的顺序是 sort()-> skip()-> limit()"
"MongoDB 索引
索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

createIndex() 方法
使用 createIndex() 方法来创建索引。

语法
>db.collection.createIndex(keys, options)
语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,按降序来创建索引指定为 -1 即可。

实例
>db.col.createIndex({""title"":1})
createIndex() 方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)。
>db.col.createIndex({""title"":1,""description"":-1})

createIndex() 接收可选参数,可选参数列表如下:
background:Boolean类型,建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 ""background"" 可选参数。 ""background"" 默认值为false。
unique:Boolean类型,建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
name:string类型,索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
sparse:Boolean类型,对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds:integer类型,指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间
v:index version类型,索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。
weights:document类型,索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
default_language:string 类型,对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
language_override:string类型,对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.
实例
在后台创建索引:
db.values.createIndex({open: 1, close: 1}, {background: true})
通过在创建索引时加 background:true 的选项,让创建工作在后台执行
补充:
1、查看集合索引:db.col.getIndexes()
2、查看集合索引大小:db.col.totalIndexSize()
3、删除集合所有索引:db.col.dropIndexes()
4、删除集合指定索引:db.col.dropIndex(""索引名称"")"

"MongoDB 聚合
聚合(aggregate)主要用于处理数据(统计平均值,求和等),并返回数据结果。类似sql语句中的 count(*)

语法
>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
实例
计算每个作者所写的文章数,使用aggregate()计算结果如下:
> db.mycol.aggregate([{$group : {_id : ""$by_user"", num_tutorial : {$sum : 1}}}])
{
""result"" : [
{
""_id"" : ""qikux.com"",
""num_tutorial"" : 2
},
{
""_id"" : ""Neo4j"",
""num_tutorial"" : 1
}
],
""ok"" : 1
}
以上实例类似sql语句:
select by_user, count(*) from mycol group by by_user
在上面的例子中,我们通过字段 by_user 字段对数据进行分组,并计算 by_user 字段相同值的总和。

下表展示了一些聚合的表达式:
$sum:计算总和:
db.mycol.aggregate([{$group : {_id : ""$by_user"", num_t : {$sum : ""$likes""}}}])
$avg:计算平均值
db.mycol.aggregate([{$group : {_id : ""$by_user"", num_tutorial : {$avg : ""$likes""}}}])
$min:获取集合中所有文档对应值得最小值。
db.mycol.aggregate([{$group : {_id : ""$by_user"", num_tutorial : {$min : ""$likes""}}}])
$max:获取集合中所有文档对应值得最大值。
db.mycol.aggregate([{$group : {_id : ""$by_user"", num_tutorial : {$max : ""$likes""}}}])
$push:在结果文档中插入值到一个数组中。
db.mycol.aggregate([{$group : {_id : ""$by_user"", url : {$push: ""$url""}}}])
$addToSet:在结果文档中插入值到一个数组中,但不创建副本。
db.mycol.aggregate([{$group : {_id : ""$by_user"", url : {$addToSet : ""$url""}}}])
$first:根据资源文档的排序获取第一个文档数据。
db.mycol.aggregate([{$group : {_id : ""$by_user"", first_url : {$first : ""$url""}}}])
$last:根据资源文档的排序获取最后一个文档数据
db.mycol.aggregate([{$group : {_id : ""$by_user"", last_url : {$last : ""$url""}}}])"

"MongoDB 复制(副本集)
MongoDB复制是将数据同步在多个服务器的过程。
复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并可以保证数据的安全性。
复制还允许您从硬件故障和服务中断中恢复数据。

什么是复制?
保障数据的安全性
数据高可用性 (24*7)
灾难恢复
无需停机维护(如备份,重建索引,压缩)
分布式读取数据
MongoDB复制原理
mongodb的复制至少需要两个节点。其中一个是主节点,负责处理客户端请求,其余的都是从节点,负责复制主节点上的数据。

mongodb各个节点常见的搭配方式为:一主一从、一主多从。

主节点记录在其上的所有操作oplog,从节点定期轮询主节点获取这些操作,然后对自己的数据副本执行这些操作,从而保证从节点的数据与主节点一致。

MongoDB复制结构图如下所示:
ClientApplication
Driver
Writes Reads
↓ ↓
Primary
(Replication)↓ ↓(Replication)
Secondary Secondary
以上结构图中,客户端从主节点读取数据,在客户端写入数据到主节点时, 主节点与从节点进行数据交互保障数据的一致性。
副本集特征:
N个节点的集群
任何节点可作为主节点
所有写入操作都在主节点上
自动故障转移
自动恢复
MongoDB副本集设置
使用同一个MongoDB来做MongoDB主从的实验, 操作步骤如下:
关闭正在运行的MongoDB服务器。
通过指定 --replSet 选项来启动mongoDB。--replSet 基本语法格式如下:
mongod --port ""PORT"" --dbpath ""YOUR_DB_DATA_PATH"" --replSet ""REPLICA_SET_INSTANCE_NAME""
实例
mongod --port 27017 --dbpath ""D:\set up\mongodb\data"" --replSet rs0
以上实例会启动一个名为rs0的MongoDB实例,其端口号为27017。

启动后打开命令提示框并连接上mongoDB服务。
在Mongo客户端使用命令rs.initiate()来启动一个新的副本集。
可以使用rs.conf()来查看副本集的配置
查看副本集状态使用 rs.status() 命令

副本集添加成员
添加副本集的成员,我们需要使用多台服务器来启动mongo服务。进入Mongo客户端,并使用rs.add()方法来添加副本集的成员。
语法
rs.add() 命令基本语法格式如下:
>rs.add(HOST_NAME:PORT)
实例
假设你已经启动了一个名为mongod1.net,端口号为27017的Mongo服务。在客户端命令窗口使用rs.add() 命令将其添加到副本集中,命令如下所示:
>rs.add(""mongod1.net:27017"")
MongoDB中你只能通过主节点将Mongo服务添加到副本集中,判断当前运行的Mongo服务是否为主节点可以使用命令db.isMaster() 。
MongoDB的副本集与我们常见的主从有所不同,主从在主机宕机后所有服务将停止,而副本集在主机宕机后,副本会接管主节点成为主节点,不会出现宕机的情况。"


"MongoDB 分片
分片
在Mongodb里面存在另一种集群,就是分片技术,可以满足MongoDB数据量大量增长的需求。

当MongoDB存储海量的数据时,一台机器可能不足以存储数据,也可能不足以提供可接受的读写吞吐量。这时,我们就可以通过在多台机器上分割数据,使得数据库系统能存储和处理更多的数据。

为什么使用分片
复制所有的写入操作到主节点
延迟的敏感数据会在主节点查询
单个副本集限制在12个节点
当请求量巨大时会出现内存不足。
本地磁盘不足
垂直扩展价格昂贵

MongoDB分片
下图展示了在MongoDB中使用分片集群结构分布:
App Server-Router(mongos) 2 or more Routers ←→
↓↑ 3 Config Servers
Shard(replica set)(2 or more Shards) ←→
上图中主要有如下所述三个主要组件:
Shard:
用于存储实际的数据块,实际生产环境中一个shard server角色可由几台机器组个一个replica set承担,防止主机单点故障
Config Server:
mongod实例,存储了整个 ClusterMetadata,其中包括 chunk信息。
Query Routers:
前端路由,客户端由此接入,且让整个集群看上去像单一数据库,前端应用可以透明使用。

分片实例
分片结构端口分布如下:
Shard Server 1:27020
Shard Server 2:27021
Shard Server 3:27022
Shard Server 4:27023
Config Server :27100
Route Process:40000
步骤一:启动Shard Server
[root@100 /]# mkdir -p /www/mongoDB/shard/s0
[root@100 /]# mkdir -p /www/mongoDB/shard/s1
[root@100 /]# mkdir -p /www/mongoDB/shard/s2
[root@100 /]# mkdir -p /www/mongoDB/shard/s3
[root@100 /]# mkdir -p /www/mongoDB/shard/log
[root@100 /]# mongoDB路径/bin/mongod --port 27020 --dbpath=/www/mongoDB/shard/s0 --logpath=/www/mongoDB/shard/log/s0.log --logappend --fork
....
[root@100 /]# mongoDB路径/bin/mongod --port 27023 --dbpath=/www/mongoDB/shard/s3 --logpath=/www/mongoDB/shard/log/s3.log --logappend --fork
步骤二: 启动Config Server
[root@100 /]# mkdir -p /www/mongoDB/shard/config
[root@100 /]# mongoDB路径/bin/mongod --port 27100 --dbpath=/www/mongoDB/shard/config --logpath=/www/mongoDB/shard/log/config.log --logappend --fork
注意:这里我们完全可以像启动普通mongodb服务一样启动,不需要添加—shardsvr和configsvr参数。因为这两个参数的作用就是改变启动端口的,所以我们自行指定了端口就可以。

步骤三: 启动Route Process
mongoDB路径/bin/mongos --port 40000 --configdb localhost:27100 --fork --logpath=/www/mongoDB/shard/log/route.log --chunkSize 500
mongos启动参数中,chunkSize这一项是用来指定chunk的大小的,单位是MB,默认大小为200MB.

步骤四: 配置Sharding
接下来,我们使用MongoDB Shell登录到mongos,添加Shard节点
[root@100 shard]# mongoDB路径/bin/mongo admin --port 40000
MongoDB shell version: 2.0.7
connecting to: 127.0.0.1:40000/admin
mongos> db.runCommand({ addshard:""localhost:27020"" })
{ ""shardAdded"" : ""shard0000"", ""ok"" : 1 }
......
mongos> db.runCommand({ addshard:""localhost:27029"" })
{ ""shardAdded"" : ""shard0009"", ""ok"" : 1 }
mongos> db.runCommand({ enablesharding:""test"" }) #设置分片存储的数据库
{ ""ok"" : 1 }
mongos> db.runCommand({ shardcollection: ""test.log"", key: { id:1,time:1}})
{ ""collectionsharded"" : ""test.log"", ""ok"" : 1 }
步骤五: 程序代码内无需太大更改,直接按照连接普通的mongo数据库那样,将数据库连接接入接口40000
"


"综合使用复制、分片
1. 创建Sharding复制集 rs0

# mkdir /data/log
# mkdir /data/db1
# nohup mongod --port 27020 --dbpath=/data/db1 --logpath=/data/log/rs0-1.log --logappend --fork --shardsvr --replSet=rs0 &

# mkdir /data/db2
# nohup mongod --port 27021 --dbpath=/data/db2 --logpath=/data/log/rs0-2.log --logappend --fork --shardsvr --replSet=rs0 &
1.1 复制集rs0配置

# mongo localhost:27020 > rs.initiate({_id: 'rs0', members: [{_id: 0, host: 'localhost:27020'}, {_id: 1, host: 'localhost:27021'}]}) > rs.isMaster() #查看主从关系
2. 创建Sharding复制集 rs1

# mkdir /data/db3
# nohup mongod --port 27030 --dbpath=/data/db3 --logpath=/data/log/rs1-1.log --logappend --fork --shardsvr --replSet=rs1 &
# mkdir /data/db4
# nohup mongod --port 27031 --dbpath=/data/db4 --logpath=/data/log/rs1-2.log --logappend --fork --shardsvr --replSet=rs1 &
2.1 复制集rs1配置

# mongo localhost:27030
> rs.initiate({_id: 'rs1', members: [{_id: 0, host: 'localhost:27030'}, {_id: 1, host: 'localhost:27031'}]})
> rs.isMaster() #查看主从关系
3. 创建Config复制集 conf

# mkdir /data/conf1
# nohup mongod --port 27100 --dbpath=/data/conf1 --logpath=/data/log/conf-1.log --logappend --fork --configsvr --replSet=conf &
# mkdir /data/conf2
# nohup mongod --port 27101 --dbpath=/data/conf2 --logpath=/data/log/conf-2.log --logappend --fork --configsvr --replSet=conf &
3.1 复制集conf配置

# mongo localhost:27100
> rs.initiate({_id: 'conf', members: [{_id: 0, host: 'localhost:27100'}, {_id: 1, host: 'localhost:27101'}]})
> rs.isMaster() #查看主从关系
4. 创建Route

# nohup mongos --port 40000 --configdb conf/localhost:27100,localhost:27101 --fork --logpath=/data/log/route.log --logappend &
4.1 设置分片

# mongo localhost:40000
> use admin
> db.runCommand({ addshard: 'rs0/localhost:27020,localhost:27021'})
> db.runCommand({ addshard: 'rs1/localhost:27030,localhost:27031'})
> db.runCommand({ enablesharding: 'test'})
> db.runCommand({ shardcollection: 'test.user', key: {name: 1}})"

"MongoDB 备份(mongodump)与恢复(mongorestore)
MongoDB数据备份
mongodump命令来备份MongoDB数据。该命令可以导出所有数据到指定目录中。
mongodump命令可以通过参数指定导出的数据量级转存的服务器。
语法
mongodump命令脚本语法如下:
>mongodump -h dbhost -d dbname -o dbdirectory
-h:
MongDB所在服务器地址,例如:127.0.0.1,当然也可以指定端口号:127.0.0.1:27017
-d:
需要备份的数据库实例,例如:test
-o:
备份的数据存放位置,例如:c:\data\dump,当然该目录需要提前建立,在备份完成后,系统自动在dump目录下建立一个test目录,这个目录里面存放该数据库实例的备份数据。
实例
在本地使用 27017 启动你的mongod服务。打开命令提示符窗口,进入MongoDB安装目录的bin目录输入命令mongodump:
>mongodump
执行以上命令后,客户端会连接到ip为 127.0.0.1 端口号为 27017 的MongoDB服务上,并备份所有数据到 bin/dump/ 目录中。

mongodump 命令可选参数列表如下所示:
mongodump --host HOST_NAME --port PORT_NUMBER:该命令将备份所有MongoDB数据
mongodump --host runoob.com --port 27017

mongodump --dbpath DB_PATH --out BACKUP_DIRECTORY
mongodump --dbpath /data/db/ --out /data/backup/

mongodump --collection COLLECTION --db DB_NAME:该命令将备份指定数据库的集合。
mongodump --collection mycol --db test

MongoDB数据恢复
使用 mongorestore 命令来恢复备份的数据。
语法
mongorestore命令脚本语法如下:
>mongorestore -h <hostname><:port> -d dbname <path>
--host <:port>, -h <:port>:
MongoDB所在服务器地址,默认为: localhost:27017

--db , -d :
需要恢复的数据库实例,例如:test,当然这个名称也可以和备份时候的不一样,比如test2

--drop:
恢复的时候,先删除当前数据,然后恢复备份的数据。就是说,恢复后,备份后添加修改的数据都会被删除,慎用哦!

<path>:
mongorestore 最后的一个参数,设置备份数据所在位置,例如:c:\data\dump\test。
不能同时指定 <path> 和 --dir 选项,--dir也可以设置备份目录。

--dir:
指定备份的目录
不能同时指定 <path> 和 --dir 选项。

接下来执行以下命令:
>mongorestore"

"MongoDB 监控
已经安装部署并允许MongoDB服务后,你必须要了解MongoDB的运行情况,并查看MongoDB的性能。这样在大流量得情况下可以很好的应对并保证MongoDB正常运作。

MongoDB中提供了mongostat 和 mongotop 两个命令来监控MongoDB的运行情况。

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

启动你的Mongod服务,进入到你安装的MongoDB目录下的bin目录, 然后输入mongostat命令,如下所示:
mongodb所在目录\bin>mongostat

mongotop 命令
mongotop也是mongodb下的一个内置工具,mongotop提供了一个方法,用来跟踪一个MongoDB的实例,查看哪些大量的时间花费在读取和写入数据。 mongotop提供每个集合的水平的统计数据。默认情况下,mongotop返回值的每一秒。

启动你的Mongod服务,进入到你安装的MongoDB目录下的bin目录, 然后输入mongotop命令,如下所示:

mongodb所在目录\bin>mongotop

带参数实例
mongodb所在目录\bin>mongotop 10
后面的10是<sleeptime>参数 ,可以不使用,等待的时间长度,以秒为单位,mongotop等待调用之间。通过的默认mongotop返回数据的每一秒。

mongodb所在目录\bin>mongotop --locks
报告每个数据库的锁的使用中,使用mongotop - 锁。

输出结果字段说明:
ns:
包含数据库命名空间,后者结合了数据库名称和集合。
db:
包含数据库的名称。名为 . 的数据库针对全局锁定,而非特定数据库。
total:
mongod花费的时间工作在这个命名空间提供总额。
read:
提供了大量的时间,这mongod花费在执行读操作,在此命名空间。
write:
提供这个命名空间进行写操作,这mongod花了大量的时间。"

"MongoDB 关系
MongoDB 的关系表示多个文档之间在逻辑上的相互联系。

文档间可以通过嵌入和引用来建立联系。
MongoDB 中的关系可以是:
1:1 (1对1)
1: N (1对多)
N: 1 (多对1)
N: N (多对多)

例:
一个用户可以有多个地址,所以是一对多的关系。
嵌入式关系:
{
""_id"":ObjectId(""52ffc33cd85242f436000001""),
""contact"": ""987654321"",
""dob"": ""01-01-1991"",
""name"": ""Tom Benzamin"",
""address"": [
{
""building"": ""22 A, Indiana Apt"",
""pincode"": 123456,
""city"": ""Los Angeles"",
""state"": ""California""
},
{
""building"": ""170 A, Acropolis Apt"",
""pincode"": 456789,
""city"": ""Chicago"",
""state"": ""Illinois""
}]
}
以上数据保存在单一的文档中,可以比较容易的获取和维护数据。可以这样查询用户的地址:
>db.users.findOne({""name"":""Tom Benzamin""},{""address"":1})
注意:以上查询中 db 和 users 表示数据库和集合。
缺点是,如果用户和用户地址在不断增加,数据量不断变大,会影响读写性能。

引用式关系
引用式关系是设计数据库时经常用到的方法,这种方法把用户数据文档和用户地址数据文档分开,通过引用文档的 id 字段来建立关系:
address 文档的简单结构:
{
""_id"":ObjectId(""52ffc4a5d85242602e000000""),
""building"": ""22 A, Indiana Apt"",
""pincode"": 123456,
""city"": ""Los Angeles"",
""state"": ""California""
}
{
""_id"":ObjectId(""52ffc33cd85242f436000001""),
""contact"": ""987654321"",
""dob"": ""01-01-1991"",
""name"": ""Tom Benzamin"",
""address_ids"": [
ObjectId(""52ffc4a5d85242602e000000""),
ObjectId(""52ffc4a5d85242602e000001"")
]
}
以上实例中,用户文档的 address_ids 字段包含用户地址的对象id(ObjectId)数组。

可以读取这些用户地址的对象id(ObjectId)来获取用户的详细地址信息。
这种方法需要两次查询,第一次查询用户地址的对象id(ObjectId),第二次通过查询的id获取用户的详细地址信息。
>var result = db.users.findOne({""name"":""Tom Benzamin""},{""address_ids"":1})
>var addresses = db.address.find({""_id"":{""$in"":result[""address_ids""]}})"


"MongoDB 数据库引用
引用用来规范数据结构文档。
MongoDB 引用有两种:
手动引用(Manual References)
DBRefs
DBRefs vs 手动引用
不同的集合中 (address_home, address_office, address_mailing, 等)存储不同的地址(住址,办公室地址,邮件地址等)。
调用不同地址时,也需要指定集合,一个文档从多个集合引用文档,我们应该使用 DBRefs。

使用 DBRefs
DBRef的形式:
{ $ref : , $id : , $db : }
三个字段表示的意义为:
$ref:集合名称
$id:引用的id
$db:数据库名称,可选参数
以下实例中用户数据文档使用了 DBRef, 字段 address:
{
""_id"":ObjectId(""53402597d852426020000002""),
""address"": {
""$ref"": ""address_home"",
""$id"": ObjectId(""534009e4d852427820000002""),
""$db"": ""qiku""},
""contact"": ""987654321"",
""dob"": ""01-01-1991"",
""name"": ""Tom Benzamin""
}
address DBRef 字段指定了引用的地址文档是在 qiku 数据库下的 address_home 集合,id 为 534009e4d852427820000002。

以下代码中,我们通过指定 $ref 参数(address_home 集合)来查找集合中指定id的用户地址信息:
>var user = db.users.findOne({""name"":""Tom Benzamin""})
>var dbRef = user.address
>db[dbRef.$ref].findOne({""_id"":ObjectId(dbRef.$id)})
以上实例返回了 address_home 集合中的地址数据:
{
""_id"" : ObjectId(""534009e4d852427820000002""),
""building"" : ""22 A, Indiana Apt"",
""pincode"" : 123456,
""city"" : ""Los Angeles"",
""state"" : ""California""
}"

"MongoDB 覆盖索引查询
覆盖查询是以下的查询:
所有的查询字段是索引的一部分
所有的查询返回字段在同一个索引中
由于所有出现在查询中的字段是索引的一部分, MongoDB 无需在整个数据文档中检索匹配查询条件和返回使用相同索引的查询结果。

因为索引存在于RAM中,从索引中获取数据比通过扫描文档读取数据要快得多。

使用覆盖索引查询
为了测试覆盖索引查询,使用以下 users 集合:
{
""_id"": ObjectId(""53402597d852426020000002""),
""contact"": ""987654321"",
""dob"": ""01-01-1991"",
""gender"": ""M"",
""name"": ""Tom Benzamin"",
""user_name"": ""tombenzamin""
}
在 users 集合中创建联合索引,字段为 gender 和 user_name :
>db.users.ensureIndex({gender:1,user_name:1})
该索引会覆盖以下查询:
>db.users.find({gender:""M""},{user_name:1,_id:0})
也就是说,对于上述查询,MongoDB的不会去数据库文件中查找。相反,它会从索引中提取数据,这是非常快速的数据查询。
注意:
由于我们的索引中不包括 _id 字段,_id在查询中会默认返回,可以在MongoDB的查询结果集中排除它。
下面的实例没有排除_id,查询就不会被覆盖:
>db.users.find({gender:""M""},{user_name:1})

如果是以下的查询,不能使用覆盖索引查询:
所有索引字段是一个数组
所有索引字段是一个子文档"

"MongoDB 查询分析
MongoDB 查询分析可以确保我们建议的索引是否有效,是查询语句性能分析的重要工具。

MongoDB 查询分析常用函数有:explain() 和 hint()。

使用 explain()
explain 操作提供了查询信息,使用索引及查询统计等。有利于我们对索引的优化。

接下来我们在 users 集合中创建 gender 和 user_name 的索引:

>db.users.ensureIndex({gender:1,user_name:1})
现在在查询语句中使用 explain :

>db.users.find({gender:""M""},{user_name:1,_id:0}).explain()
以上的 explain() 查询返回如下结果:

{
""cursor"" : ""BtreeCursor gender_1_user_name_1"",
""isMultiKey"" : false,
""n"" : 1,
""nscannedObjects"" : 0,
""nscanned"" : 1,
""nscannedObjectsAllPlans"" : 0,
""nscannedAllPlans"" : 1,
""scanAndOrder"" : false,
""indexOnly"" : true,
""nYields"" : 0,
""nChunkSkips"" : 0,
""millis"" : 0,
""indexBounds"" : {
""gender"" : [
[
""M"",
""M""
]
],
""user_name"" : [
[
{
""$minElement"" : 1
},
{
""$maxElement"" : 1
}
]
]
}
}
现在,我们看看这个结果集的字段:

indexOnly: 字段为 true ,表示我们使用了索引。
cursor:因为这个查询使用了索引,MongoDB 中索引存储在B树结构中,所以这是也使用了 BtreeCursor 类型的游标。如果没有使用索引,游标的类型是 BasicCursor。这个键还会给出你所使用的索引的名称,你通过这个名称可以查看当前数据库下的system.indexes集合(系统自动创建,由于存储索引信息,这个稍微会提到)来得到索引的详细信息。
n:当前查询返回的文档数量。
nscanned/nscannedObjects:表明当前这次查询一共扫描了集合中多少个文档,我们的目的是,让这个数值和返回文档的数量越接近越好。
millis:当前查询所需时间,毫秒数。
indexBounds:当前查询具体使用的索引。
使用 hint()
虽然MongoDB查询优化器一般工作的很不错,但是也可以使用 hint 来强制 MongoDB 使用一个指定的索引。

这种方法某些情形下会提升性能。 一个有索引的 collection 并且执行一个多字段的查询(一些字段已经索引了)。

如下查询实例指定了使用 gender 和 user_name 索引字段来查询:

>db.users.find({gender:""M""},{user_name:1,_id:0}).hint({gender:1,user_name:1})
可以使用 explain() 函数来分析以上查询:

>db.users.find({gender:""M""},{user_name:1,_id:0}).hint({gender:1,user_name:1}).explain()"


"MongoDB 原子操作
mongodb不支持事务,所以,在你的项目中应用时,要注意这点。无论什么设计,都不要要求mongodb保证数据的完整性。

但是mongodb提供了许多原子操作,比如文档的保存,修改,删除等,都是原子操作。

所谓原子操作就是要么这个文档保存到Mongodb,要么没有保存到Mongodb,不会出现查询到的文档没有保存完整的情况。

原子操作数据模型
考虑下面的例子,图书馆的书籍及结账信息。

实例说明了在一个相同的文档中如何确保嵌入字段关联原子操作(update:更新)的字段是同步的。

book = {
_id: 123456789,
title: ""MongoDB: The Definitive Guide"",
author: [ ""Kristina Chodorow"", ""Mike Dirolf"" ],
published_date: ISODate(""2010-09-24""),
pages: 216,
language: ""English"",
publisher_id: ""oreilly"",
available: 3,
checkout: [ { by: ""joe"", date: ISODate(""2012-10-15"") } ]
}
你可以使用 db.collection.findAndModify() 方法来判断书籍是否可结算并更新新的结算信息。

在同一个文档中嵌入的 available 和 checkout 字段来确保这些字段是同步更新的:

db.books.findAndModify ( {
query: {
_id: 123456789,
available: { $gt: 0 }
},
update: {
$inc: { available: -1 },
$push: { checkout: { by: ""abc"", date: new Date() } }
}
} )
原子操作常用命令
$set
用来指定一个键并更新键值,若键不存在并创建。

{ $set : { field : value } }
$unset
用来删除一个键。

{ $unset : { field : 1} }
$inc
$inc可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作。

{ $inc : { field : value } }
$push
用法:

{ $push : { field : value } }
把value追加到field里面去,field一定要是数组类型才行,如果field不存在,会新增一个数组类型加进去。

$pushAll
同$push,只是一次可以追加多个值到一个数组字段内。

{ $pushAll : { field : value_array } }
$pull
从数组field内删除一个等于value值。

{ $pull : { field : _value } }
$addToSet
增加一个值到数组内,而且只有当这个值不在数组内才增加。

$pop
删除数组的第一个或最后一个元素

{ $pop : { field : 1 } }
$rename
修改字段名称

{ $rename : { old_field_name : new_field_name } }
$bit
位操作,integer类型

{$bit : { field : {and : 5}}}
偏移操作符
> t.find() { ""_id"" : ObjectId(""4b97e62bf1d8c7152c9ccb74""), ""title"" : ""ABC"", ""comments"" : [ { ""by"" : ""joe"", ""votes"" : 3 }, { ""by"" : ""jane"", ""votes"" : 7 } ] }

> t.update( {'comments.by':'joe'}, {$inc:{'comments.$.votes':1}}, false, true )

> t.find() { ""_id"" : ObjectId(""4b97e62bf1d8c7152c9ccb74""), ""title"" : ""ABC"", ""comments"" : [ { ""by"" : ""joe"", ""votes"" : 4 }, { ""by"" : ""jane"", ""votes"" : 7 } ] }"



"MongoDB 高级索引
考虑以下文档集合(users ):

{
""address"": {
""city"": ""Los Angeles"",
""state"": ""California"",
""pincode"": ""123""
},
""tags"": [
""music"",
""cricket"",
""blogs""
],
""name"": ""Tom Benzamin""
}
以上文档包含了 address 子文档和 tags 数组。

索引数组字段
假设我们基于标签来检索用户,为此我们需要对集合中的数组 tags 建立索引。

在数组中创建索引,需要对数组中的每个字段依次建立索引。所以在我们为数组 tags 创建索引时,会为 music、cricket、blogs三个值建立单独的索引。

使用以下命令创建数组索引:

>db.users.ensureIndex({""tags"":1})
创建索引后,我们可以这样检索集合的 tags 字段:

>db.users.find({tags:""cricket""})
为了验证我们使用使用了索引,可以使用 explain 命令:

>db.users.find({tags:""cricket""}).explain()
以上命令执行结果中会显示 ""cursor"" : ""BtreeCursor tags_1"" ,则表示已经使用了索引。

索引子文档字段
假设我们需要通过city、state、pincode字段来检索文档,由于这些字段是子文档的字段,所以我们需要对子文档建立索引。

为子文档的三个字段创建索引,命令如下:

>db.users.ensureIndex({""address.city"":1,""address.state"":1,""address.pincode"":1})
一旦创建索引,我们可以使用子文档的字段来检索数据:

>db.users.find({""address.city"":""Los Angeles""})
查询表达不一定遵循指定的索引的顺序,mongodb 会自动优化。所以上面创建的索引将支持以下查询:

>db.users.find({""address.state"":""California"",""address.city"":""Los Angeles""})
同样支持以下查询:

>db.users.find({""address.city"":""Los Angeles"",""address.state"":""California"",""address.pincode"":""123""})"

"MongoDB 索引限制
额外开销
每个索引占据一定的存储空间,在进行插入,更新和删除操作时也需要对索引进行操作。所以,如果你很少对集合进行读取操作,建议不使用索引。

内存(RAM)使用
由于索引是存储在内存(RAM)中,你应该确保该索引的大小不超过内存的限制。

如果索引的大小大于内存的限制,MongoDB会删除一些索引,这将导致性能下降。

查询限制
索引不能被以下的查询使用:
正则表达式及非操作符,如 $nin, $not, 等。
算术运算符,如 $mod, 等。
$where 子句
所以,检测你的语句是否使用索引是一个好的习惯,可以用explain来查看。

索引键限制
从2.6版本开始,如果现有的索引字段的值超过索引键的限制,MongoDB中不会创建索引。

插入文档超过索引键限制
如果文档的索引字段值超过了索引键的限制,MongoDB不会将任何文档转换成索引的集合。与mongorestore和mongoimport工具类似。

最大范围
集合中索引不能超过64个
索引名的长度不能超过128个字符
一个复合索引最多可以有31个字段"
"MongoDB ObjectId
ObjectId 是一个12字节 BSON 类型数据,有以下格式:
前4个字节表示时间戳
接下来的3个字节是机器标识码
紧接的两个字节由进程id组成(PID)
最后三个字节是随机数。
MongoDB中存储的文档必须有一个""_id""键。这个键的值可以是任何类型的,默认是个ObjectId对象。

在一个集合里面,每个文档都有唯一的""_id""值,来确保集合里面每个文档都能被唯一标识。

MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个 服务器上同步自动增加主键值既费力还费时。

创建新的ObjectId
使用以下代码生成新的ObjectId:
>newObjectId = ObjectId()
上面的语句返回以下唯一生成的id:
ObjectId(""5349b4ddd2781d08c09890f3"")

可以使用生成的id来取代MongoDB自动生成的ObjectId:
>myObjectId = ObjectId(""5349b4ddd2781d08c09890f4"")

创建文档的时间戳
由于 ObjectId 中存储了 4 个字节的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:
>ObjectId(""5349b4ddd2781d08c09890f4"").getTimestamp()
以上代码将返回 ISO 格式的文档创建时间:
ISODate(""2014-04-12T21:49:17Z"")

ObjectId 转换为字符串
在某些情况下,您可能需要将ObjectId转换为字符串格式。你可以使用下面的代码:
>new ObjectId().str
以上代码将返回Guid格式的字符串::
5349b4ddd2781d08c09890f3"

"MongoDB Map Reduce
Map-Reduce是一种计算模型,简单的说就是将大批量的工作(数据)分解(MAP)执行,然后再将结果合并成最终结果(REDUCE)。
MongoDB提供的Map-Reduce非常灵活,对于大规模数据分析也相当实用。

MapReduce 命令
以下是MapReduce的基本语法:
>db.collection.mapReduce(
function() {emit(key,value);}, //map 函数
function(key,values) {return reduceFunction}, //reduce 函数
{
out: collection,
query: document,
sort: document,
limit: number
}
)
使用 MapReduce 要实现两个函数 Map 函数和 Reduce 函数,Map 函数调用 emit(key, value), 遍历 collection 中所有的记录, 将 key 与 value 传递给 Reduce 函数进行处理。
Map 函数必须调用 emit(key, value) 返回键值对。

参数说明:
map :映射函数 (生成键值对序列,作为 reduce 函数参数)。
reduce 统计函数,reduce函数的任务就是将key-values变成key-value,也就是把values数组变成一个单一的值value。。
out 统计结果存放集合 (不指定则使用临时集合,在客户端断开后自动删除)。
query 一个筛选条件,只有满足条件的文档才会调用map函数。(query。limit,sort可以随意组合)
sort 和limit结合的sort排序参数(也是在发往map函数前给文档排序),可以优化分组机制
limit 发往map函数的文档数量的上限(要是没有limit,单独使用sort的用处不大)

使用 MapReduce
考虑以下文档结构存储用户的文章,文档存储了用户的 user_name 和文章的 status 字段:
>db.posts.insert({
""post_text"": ""最全的技术文档。"",
""user_name"": ""mark"",
""status"":""active""
})
WriteResult({ ""nInserted"" : 1 })
>db.posts.insert({
""post_text"": ""最全的技术文档。"",
""user_name"": ""mark"",
""status"":""active""
})
WriteResult({ ""nInserted"" : 1 })
>db.posts.insert({
""post_text"": ""最全的技术文档。"",
""user_name"": ""mark"",
""status"":""active""
})
WriteResult({ ""nInserted"" : 1 })
>db.posts.insert({
""post_text"": ""最全的技术文档。"",
""user_name"": ""mark"",
""status"":""active""
})
WriteResult({ ""nInserted"" : 1 })
>db.posts.insert({
""post_text"": ""最全的技术文档。"",
""user_name"": ""mark"",
""status"":""disabled""
})
WriteResult({ ""nInserted"" : 1 })
>db.posts.insert({
""post_text"": ""最全的技术文档。"",
""user_name"": ""qiku"",
""status"":""disabled""
})
WriteResult({ ""nInserted"" : 1 })
>db.posts.insert({
""post_text"": ""最全的技术文档。"",
""user_name"": ""qiku"",
""status"":""disabled""
})
WriteResult({ ""nInserted"" : 1 })
>db.posts.insert({
""post_text"": ""最全的技术文档。"",
""user_name"": ""qiku"",
""status"":""active""
})
WriteResult({ ""nInserted"" : 1 })
将在 posts 集合中使用 mapReduce 函数来选取已发布的文章(status:""active""),并通过user_name分组,计算每个用户的文章数:
>db.posts.mapReduce(
function() { emit(this.user_name,1); },
function(key, values) {return Array.sum(values)},
{
query:{status:""active""},
out:""post_total""
}
)
以上 mapReduce 输出结果为:
{
""result"" : ""post_total"",
""timeMillis"" : 23,
""counts"" : {
""input"" : 5,
""emit"" : 5,
""reduce"" : 1,
""output"" : 2
},
""ok"" : 1
}
结果表明,共有 5 个符合查询条件(status:""active"")的文档, 在map函数中生成了 5 个键值对文档,最后使用reduce函数将相同的键值分为 2 组。

具体参数说明:
result:储存结果的collection的名字,这是个临时集合,MapReduce的连接关闭后自动就被删除了。
timeMillis:执行花费的时间,毫秒为单位
input:满足条件被发送到map函数的文档个数
emit:在map函数中emit被调用的次数,也就是所有集合中的数据总量
ouput:结果集合中的文档个数(count对调试非常有帮助)
ok:是否成功,成功为1
err:如果失败,这里可以有失败原因,不过从经验上来看,原因比较模糊,作用不大
使用 find 操作符来查看 mapReduce 的查询结果:
>db.posts.mapReduce(
function() { emit(this.user_name,1); },
function(key, values) {return Array.sum(values)},
{
query:{status:""active""},
out:""post_total""
}
).find()
以上查询显示如下结果,两个用户 tom 和 mark 有两个发布的文章:

{ ""_id"" : ""mark"", ""value"" : 4 }
{ ""_id"" : ""qiku"", ""value"" : 1 }
用类似的方式,MapReduce可以被用来构建大型复杂的聚合查询。

Map函数和Reduce函数可以使用 JavaScript 来实现,使得MapReduce的使用非常灵活和强大。
补充:
临时集合参数是这样写的
out: { inline: 1 }
设置了 {inline:1} 将不会创建集合,整个 Map/Reduce 的操作将会在内存中进行。
注意,这个选项只有在结果集单个文档大小在16MB限制范围内时才有效。
db.users.mapReduce(map,reduce,{out:{inline:1}});"




"MongoDB 全文检索
全文检索对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。
这个过程类似于通过字典中的检索字表查字的过程。
MongoDB 从 2.4 版本开始支持全文检索,目前支持15种语言的全文索引。

创建全文索引
考虑以下 posts 集合的文档数据,包含了文章内容(post_text)及标签(tags):
{
""post_text"": ""enjoy the mongodb articles on qiku"",
""tags"": [
""mongodb"",
""qiku""
]
}
我们可以对 post_text 字段建立全文索引,这样我们可以搜索文章内的内容:
>db.posts.ensureIndex({post_text:""text""})

使用全文索引
现在已经对 post_text 建立了全文索引,可以搜索文章中的关键词 qiku:
>db.posts.find({$text:{$search:""qiku""}})
以下命令返回了如下包含 qiku 关键词的文档数据:
{
""_id"" : ObjectId(""53493d14d852429c10000002""),
""post_text"" : ""enjoy the mongodb articles on qiku"",
""tags"" : [ ""mongodb"", ""qiku"" ]
}
如果你使用的是旧版本的 MongoDB,你可以使用以下命令:
>db.posts.runCommand(""text"",{search:""qiku""})
使用全文索引可以提高搜索效率。

删除全文索引
删除已存在的全文索引,可以使用 find 命令查找索引名:
>db.posts.getIndexes()
通过以上命令获取索引名,本例的索引名为post_text_text,执行以下命令来删除索引:
>db.posts.dropIndex(""post_text_text"")"

"MongoDB 正则表达式
正则表达式是使用单个字符串来描述、匹配一系列符合某个句法规则的字符串。
MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式。
MongoDB使用PCRE (Perl Compatible Regular Expression) 作为正则表达式语言。
使用正则表达式不需要做任何配置。
考虑以下 posts 集合的文档结构,该文档包含了文章内容和标签:
{
""post_text"": ""enjoy the mongodb articles on qiku"",
""tags"": [
""mongodb"",
""qiku""
]
}
使用正则表达式
以下命令使用正则表达式查找包含 qiku 字符串的文章:
>db.posts.find({post_text:{$regex:""qiku""}})
以上查询也可以写为:
>db.posts.find({post_text:/qiku/})

不区分大小写的正则表达式
如果检索需要不区分大小写,可以设置 $options 为 $i。
以下命令将查找不区分大小写的字符串 qiku:
>db.posts.find({post_text:{$regex:""qiku"",$options:""$i""}})
集合中会返回所有包含字符串 qiku 的数据,且不区分大小写:
{
""_id"" : ObjectId(""53493d37d852429c10000004""),
""post_text"" : ""hey! this is my post on qiku"",
""tags"" : [ ""qiku"" ]
}

数组元素使用正则表达式
可以在数组字段中使用正则表达式来查找内容。 这在标签的实现上非常有用,如果要查找包含以 run 开头的标签数据(qi 或 qik 或 qiku), 你可以使用以下代码:
>db.posts.find({tags:{$regex:""qik""}})

优化正则表达式查询
如果文档中字段设置了索引,那么使用索引相比于正则表达式匹配查找所有的数据查询速度更快。
如果正则表达式是前缀表达式,所有匹配的数据将以指定的前缀字符串为开始。
例如: 如果正则表达式为 ^tut ,查询语句将查找以 tut 为开头的字符串。

这里面使用正则表达式有两点需要注意:
正则表达式中使用变量。一定要使用eval将组合的字符串进行转换,不能直接将字符串拼接后传入给表达式。否则没有报错信息,只是结果为空!实例如下:
var name=eval(""/"" + 变量值key +""/i"");
以下是模糊查询包含title关键词, 且不区分大小写:
title:eval(""/""+title+""/i"") // 等同于 title:{$regex:title,$Option:""$i""}

regex操作符的介绍
regex操作符
{<field>:{$regex:/pattern/,$options:’<options>’}}
{<field>:{$regex:’pattern’,$options:’<options>’}}
{<field>:{$regex:/pattern/<options>}}
正则表达式对象
{<field>: /pattern/<options>}
$regex与正则表达式对象的区别:
在$in操作符中只能使用正则表达式对象,例如:{name:{$in:[/^joe/i,/^jack/}}
在使用隐式的$and操作符中,只能使用$regex,例如:{name:{$regex:/^jo/i, $nin:['john']}}
当option选项中包含X或S选项时,只能使用$regex,例如:{name:{$regex:/m.*line/,$options:""si""}}

$regex操作符的使用
$regex操作符中的option选项可以改变正则匹配的默认行为,它包括i, m, x以及S四个选项,其含义如下
i 忽略大小写,{<field>{$regex/pattern/i}},设置i选项后,模式中的字母会进行大小写不敏感匹配
m 多行匹配模式,{<field>{$regex/pattern/,$options:'m'},m选项会更改^和$元字符的默认行为,分别使用与行的开头和结尾匹配,而不是与输入字符串的开头和结尾匹配。
x 忽略非转义的空白字符,{<field>:{$regex:/pattern/,$options:'m'},设置x选项后,正则表达式中的非转义的空白字符将被忽略,同时井号(#)被解释为注释的开头注,只能显式位于option选项中。
s 单行匹配模式{<field>:{$regex:/pattern/,$options:'s'},设置s选项后,会改变模式中的点号(.)元字符的默认行为,它会匹配所有字符,包括换行符(\n),只能显式位于option选项中。

使用$regex操作符时,需要注意下面几个问题:
i,m,x,s可以组合使用,例如:{name:{$regex:/j*k/,$options:""si""}}
在设置索弓}的字段上进行正则匹配可以提高查询速度,而且当正则表达式使用的是前缀表达式时,查询速度会进一步提高,例如:{name:{$regex: /^joe/} "


"MongoDB GridFS
GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。

GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。

GridFS 可以更好的存储大于16M的文件。

GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。

GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。

每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。

以下是简单的 fs.files 集合文档:
{
""filename"": ""test.txt"",
""chunkSize"": NumberInt(261120),
""uploadDate"": ISODate(""2014-04-13T11:32:33.557Z""),
""md5"": ""7b762939321e146569b07f72c62cca4f"",
""length"": NumberInt(646)
}
以下是简单的 fs.chunks 集合文档:

{
""files_id"": ObjectId(""534a75d19f54bfec8a2fe44b""),
""n"": NumberInt(0),
""data"": ""Mongo Binary Data""
}
GridFS 添加文件
现在我们使用 GridFS 的 put 命令来存储 mp3 文件。 调用 MongoDB 安装目录下bin的 mongofiles.exe工具。

打开命令提示符,进入到MongoDB的安装目录的bin目录中,找到mongofiles.exe,并输入下面的代码:
>mongofiles.exe -d gridfs put song.mp3
GridFS 是存储文件的数据名称。如果不存在该数据库,MongoDB会自动创建。Song.mp3 是音频文件名。

使用以下命令来查看数据库中文件的文档:
>db.fs.files.find()
以上命令执行后返回以下文档数据:
{
_id: ObjectId('534a811bf8b4aa4d33fdf94d'),
filename: ""song.mp3"",
chunkSize: 261120,
uploadDate: new Date(1397391643474), md5: ""e4f53379c909f7bed2e9d631e15c1c41"",
length: 10401959
}
我们可以看到 fs.chunks 集合中所有的区块,以下我们得到了文件的 _id 值,我们可以根据这个 _id 获取区块(chunk)的数据:
>db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})
以上实例中,查询返回了 40 个文档的数据,意味着mp3文件被存储在40个区块中。"

"MongoDB 固定集合(Capped Collections)
MongoDB 固定集合(Capped Collections)是性能出色且有着固定大小的集合,对于大小固定,我们可以想象其就像一个环形队列,当集合空间用完后,再插入的元素就会覆盖最初始的头部的元素!

创建固定集合
我们通过createCollection来创建一个固定集合,且capped选项设置为true:

>db.createCollection(""cappedLogCollection"",{capped:true,size:10000})
还可以指定文档个数,加上max:1000属性:

>db.createCollection(""cappedLogCollection"",{capped:true,size:10000,max:1000})
判断集合是否为固定集合:

>db.cappedLogCollection.isCapped()
如果需要将已存在的集合转换为固定集合可以使用以下命令:

>db.runCommand({""convertToCapped"":""posts"",size:10000})
以上代码将我们已存在的 posts 集合转换为固定集合。

固定集合查询
固定集合文档按照插入顺序储存的,默认情况下查询就是按照插入顺序返回的,也可以使用$natural调整返回顺序。

>db.cappedLogCollection.find().sort({$natural:-1})
固定集合的功能特点
可以插入及更新,但更新不能超出collection的大小,否则更新失败,不允许删除,但是可以调用drop()删除集合中的所有行,但是drop后需要显式地重建集合。

在32位机子上一个cappped collection的最大值约为482.5M,64位上只受系统文件大小的限制。

固定集合属性及用法
属性
属性1:对固定集合进行插入速度极快
属性2:按照插入顺序的查询输出速度极快
属性3:能够在插入最新数据时,淘汰最早的数据
用法
用法1:储存日志信息
用法2:缓存一些少量的文档

db.createCollection(""cappedLogCollection"",{capped:true,size:10000,max:1000})
size 是整个集合空间大小,单位为【KB】
max 是集合文档个数上线,单位是【个】
如果空间大小到达上限,则插入下一个文档时,会覆盖第一个文档;如果文档个数到达上限,同样插入下一个文档时,会覆盖第一个文档。两个参数上限判断取的是【与】的逻辑。"

"MongoDB 自动增长
MongoDB 没有像 SQL 一样有自动增长的功能, MongoDB 的 _id 是系统自动生成的12字节唯一标识。

但在某些情况下,我们可能需要实现 ObjectId 自动增长功能。

由于 MongoDB 没有实现这个功能,我们可以通过编程的方式来实现,以下我们将在 counters 集合中实现_id字段自动增长。

使用 counters 集合
考虑以下 products 文档。我们希望 _id 字段实现 从 1,2,3,4 到 n 的自动增长功能。

{
""_id"":1,
""product_name"": ""Apple iPhone"",
""category"": ""mobiles""
}
为此,创建 counters 集合,序列字段值可以实现自动长:

>db.createCollection(""counters"")
现在我们向 counters 集合中插入以下文档,使用 productid 作为 key:

{
""_id"":""productid"",
""sequence_value"": 0
}
sequence_value 字段是序列通过自动增长后的一个值。

使用以下命令插入 counters 集合的序列文档中:

>db.counters.insert({_id:""productid"",sequence_value:0})
创建 Javascript 函数
现在,我们创建函数 getNextSequenceValue 来作为序列名的输入, 指定的序列会自动增长 1 并返回最新序列值。在本文的实例中序列名为 productid 。

>function getNextSequenceValue(sequenceName){
var sequenceDocument = db.counters.findAndModify(
{
query:{_id: sequenceName },
update: {$inc:{sequence_value:1}},
""new"":true
});
return sequenceDocument.sequence_value;
}
使用 Javascript 函数
接下来我们将使用 getNextSequenceValue 函数创建一个新的文档, 并设置文档 _id 自动为返回的序列值:

>db.products.insert({
""_id"":getNextSequenceValue(""productid""),
""product_name"":""Apple iPhone"",
""category"":""mobiles""})

>db.products.insert({
""_id"":getNextSequenceValue(""productid""),
""product_name"":""Samsung S3"",
""category"":""mobiles""})
就如你所看到的,我们使用 getNextSequenceValue 函数来设置 _id 字段。

为了验证函数是否有效,我们可以使用以下命令读取文档:

>db.products.find()
以上命令将返回以下结果,我们发现 _id 字段是自增长的:

{ ""_id"" : 1, ""product_name"" : ""Apple iPhone"", ""category"" : ""mobiles""}

{ ""_id"" : 2, ""product_name"" : ""Samsung S3"", ""category"" : ""mobiles"" }"


"1. 准备工作
在开始之前,请确保已经安装好了MongoDB并启动了其服务,并且安装好了Python的PyMongo库。

2. 连接MongoDB
连接MongoDB时,我们需要使用PyMongo库里面的MongoClient。一般来说,传入MongoDB的IP及端口即可,其中第一个参数为地址host,第二个参数为端口port(如果不给它传递参数,默认是27017):
import pymongo
client = pymongo.MongoClient(host='localhost', port=27017)
这样就可以创建MongoDB的连接对象了。
另外,MongoClient的第一个参数host还可以直接传入MongoDB的连接字符串,它以mongodb开头,例如:

client = MongoClient('mongodb://localhost:27017/')
这也可以达到同样的连接效果。

3. 指定数据库
MongoDB中可以建立多个数据库,接下来我们需要指定操作哪个数据库。这里我们以test数据库为例来说明,下一步需要在程序中指定要使用的数据库:
db = client.test
这里调用client的test属性即可返回test数据库。当然,我们也可以这样指定:
db = client['test']
这两种方式是等价的。

4. 指定集合
MongoDB的每个数据库又包含许多集合(collection),它们类似于关系型数据库中的表。

下一步需要指定要操作的集合,这里指定一个集合名称为students。与指定数据库类似,指定集合也有两种方式:

collection = db.students
collection = db['students']
这样我们便声明了一个Collection对象。"

"插入数据

接下来,便可以插入数据了。对于students这个集合,新建一条学生数据,这条数据以字典形式表示:

student = {
'id': '20170101',
'name': 'Jordan',
'age': 20,
'gender': 'male'
}
这里指定了学生的学号、姓名、年龄和性别。接下来,直接调用collection的insert()方法即可插入数据,代码如下:

result = collection.insert(student)
print(result)
在MongoDB中,每条数据其实都有一个_id属性来唯一标识。如果没有显式指明该属性,MongoDB会自动产生一个ObjectId类型的_id属性。insert()方法会在执行后返回_id值。

运行结果如下:

5932a68615c2606814c91f3d
当然,我们也可以同时插入多条数据,只需要以列表形式传递即可,示例如下:

student1 = {
'id': '20170101',
'name': 'Jordan',
'age': 20,
'gender': 'male'
}

student2 = {
'id': '20170202',
'name': 'Mike',
'age': 21,
'gender': 'male'
}

result = collection.insert([student1, student2])
print(result)
返回结果是对应的_id的集合:

[ObjectId('5932a80115c2606a59e8a048'), ObjectId('5932a80115c2606a59e8a049')]
实际上,在PyMongo 3.x版本中,官方已经不推荐使用insert()方法了。当然,继续使用也没有什么问题。官方推荐使用insert_one()和insert_many()方法来分别插入单条记录和多条记录,示例如下:

student = {
'id': '20170101',
'name': 'Jordan',
'age': 20,
'gender': 'male'
}

result = collection.insert_one(student)
print(result)
print(result.inserted_id)
运行结果如下:

<pymongo.results.InsertOneResult object at 0x10d68b558>
5932ab0f15c2606f0c1cf6c5
与insert()方法不同,这次返回的是InsertOneResult对象,我们可以调用其inserted_id属性获取_id。

对于insert_many()方法,我们可以将数据以列表形式传递,示例如下:

student1 = {
'id': '20170101',
'name': 'Jordan',
'age': 20,
'gender': 'male'
}

student2 = {
'id': '20170202',
'name': 'Mike',
'age': 21,
'gender': 'male'
}

result = collection.insert_many([student1, student2])
print(result)
print(result.inserted_ids)
运行结果如下:

<pymongo.results.InsertManyResult object at 0x101dea558>
[ObjectId('5932abf415c2607083d3b2ac'), ObjectId('5932abf415c2607083d3b2ad')]
该方法返回的类型是InsertManyResult,调用inserted_ids属性可以获取插入数据的_id列表。"












"查询

插入数据后,我们可以利用find_one()或find()方法进行查询,其中find_one()查询得到的是单个结果,find()则返回一个生成器对象。示例如下:
result = collection.find_one({'name': 'Mike'})
print(type(result))
print(result)
这里我们查询name为Mike的数据,它的返回结果是字典类型,运行结果如下:
<class 'dict'>
{'_id': ObjectId('5932a80115c2606a59e8a049'), 'id': '20170202', 'name': 'Mike', 'age': 21, 'gender': 'male'}
可以发现,它多了_id属性,这就是MongoDB在插入过程中自动添加的。

此外,我们也可以根据ObjectId来查询,此时需要使用bson库里面的objectid:

from bson.objectid import ObjectId
result = collection.find_one({'_id': ObjectId('593278c115c2602667ec6bae')})
print(result)
其查询结果依然是字典类型,具体如下:

{'_id': ObjectId('593278c115c2602667ec6bae'), 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male'}
当然,如果查询结果不存在,则会返回None。

对于多条数据的查询,我们可以使用find()方法。例如,这里查找年龄为20的数据,示例如下:
results = collection.find({'age': 20})
print(results)
for result in results:
print(result)
运行结果如下:
<pymongo.cursor.Cursor object at 0x1032d5128>
{'_id': ObjectId('593278c115c2602667ec6bae'), 'id': '20170101', 'name': 'Jordan', 'age': 20, 'gender': 'male'}
{'_id': ObjectId('593278c815c2602678bb2b8d'), 'id': '20170102', 'name': 'Kevin', 'age': 20, 'gender': 'male'}
{'_id': ObjectId('593278d815c260269d7645a8'), 'id': '20170103', 'name': 'Harden', 'age': 20, 'gender': 'male'}
返回结果是Cursor类型,它相当于一个生成器,我们需要遍历取到所有的结果,其中每个结果都是字典类型。

如果要查询年龄大于20的数据,则写法如下:
results = collection.find({'age': {'$gt': 20}})
这里查询的条件键值已经不是单纯的数字了,而是一个字典,其键名为比较符号$gt,意思是大于,键值为20。

这里将比较符号归纳:
$lt

小于

{'age': {'$lt': 20}}

$gt

大于

{'age': {'$gt': 20}}

$lte

小于等于

{'age': {'$lte': 20}}

$gte

大于等于

{'age': {'$gte': 20}}

$ne

不等于

{'age': {'$ne': 20}}

$in

在范围内

{'age': {'$in': [20, 23]}}

$nin

不在范围内

{'age': {'$nin': [20, 23]}}

另外,还可以进行正则匹配查询。例如,查询名字以M开头的学生数据,示例如下:

results = collection.find({'name': {'$regex': '^M.*'}})
这里使用$regex来指定正则匹配,^M.*代表以M开头的正则表达式。

这里将一些功能符号再归类:

$regex

匹配正则表达式

{'name': {'$regex': '^M.*'}}

name以M开头

$exists

属性是否存在

{'name': {'$exists': True}}

name属性存在

$type

类型判断

{'age': {'$type': 'int'}}

age的类型为int

$mod

数字模操作

{'age': {'$mod': [5, 0]}}

年龄模5余0

$text

文本查询

{'$text': {'$search': 'Mike'}}

text类型的属性中包含Mike字符串

$where

高级条件查询

{'$where': 'obj.fans_count == obj.follows_count'}

自身粉丝数等于关注数

关于这些操作的更详细用法,可以在MongoDB官方文档找到:

https://docs.mongodb.com/manual/reference/operator/query/。"




"计数

要统计查询结果有多少条数据,可以调用count()方法。比如,统计所有数据条数:

count = collection.find().count()
print(count)
或者统计符合某个条件的数据:

count = collection.find({'age': 20}).count()
print(count)
运行结果是一个数值,即符合条件的数据条数。

排序

排序时,直接调用sort()方法,并在其中传入排序的字段及升降序标志即可。示例如下:

results = collection.find().sort('name', pymongo.ASCENDING)
print([result['name'] for result in results])
运行结果如下:

['Harden', 'Jordan', 'Kevin', 'Mark', 'Mike']
这里我们调用pymongo.ASCENDING指定升序。如果要降序排列,可以传入pymongo.DESCENDING。

偏移

在某些情况下,我们可能想只取某几个元素,这时可以利用skip()方法偏移几个位置,比如偏移2,就忽略前两个元素,得到第三个及以后的元素:

results = collection.find().sort('name', pymongo.ASCENDING).skip(2)
print([result['name'] for result in results])
运行结果如下:

['Kevin', 'Mark', 'Mike']
另外,还可以用limit()方法指定要取的结果个数,示例如下:

results = collection.find().sort('name', pymongo.ASCENDING).skip(2).limit(2)
print([result['name'] for result in results])
运行结果如下:

['Kevin', 'Mark']
如果不使用limit()方法,原本会返回三个结果,加了限制后,会截取两个结果返回。

值得注意的是,在数据库数量非常庞大的时候,如千万、亿级别,最好不要使用大的偏移量来查询数据,因为这样很可能导致内存溢出。此时可以使用类似如下操作来查询:

from bson.objectid import ObjectId
collection.find({'_id': {'$gt': ObjectId('593278c815c2602678bb2b8d')}})
这时需要记录好上次查询的_id。"

"更新

对于数据更新,我们可以使用update()方法,指定更新的条件和更新后的数据即可。例如:

condition = {'name': 'Kevin'}
student = collection.find_one(condition)
student['age'] = 25
result = collection.update(condition, student)
print(result)
这里我们要更新name为Kevin的数据的年龄:首先指定查询条件,然后将数据查询出来,修改年龄后调用update()方法将原条件和修改后的数据传入。

运行结果如下:

{'ok': 1, 'nModified': 1, 'n': 1, 'updatedExisting': True}
返回结果是字典形式,ok代表执行成功,nModified代表影响的数据条数。

另外,我们也可以使用$set操作符对数据进行更新,代码如下:

result = collection.update(condition, {'$set': student})
这样可以只更新student字典内存在的字段。如果原先还有其他字段,则不会更新,也不会删除。而如果不用$set的话,则会把之前的数据全部用student字典替换;如果原本存在其他字段,则会被删除。

另外,update()方法其实也是官方不推荐使用的方法。这里也分为update_one()方法和update_many()方法,用法更加严格,它们的第二个参数需要使用$类型操作符作为字典的键名,示例如下:

condition = {'name': 'Kevin'}
student = collection.find_one(condition)
student['age'] = 26
result = collection.update_one(condition, {'$set': student})
print(result)
print(result.matched_count, result.modified_count)
这里调用了update_one()方法,第二个参数不能再直接传入修改后的字典,而是需要使用{'$set': student}这样的形式,其返回结果是UpdateResult类型。然后分别调用matched_count和modified_count属性,可以获得匹配的数据条数和影响的数据条数。

运行结果如下:

<pymongo.results.UpdateResult object at 0x10d17b678>
1 0
我们再看一个例子:

condition = {'age': {'$gt': 20}}
result = collection.update_one(condition, {'$inc': {'age': 1}})
print(result)
print(result.matched_count, result.modified_count)
这里指定查询条件为年龄大于20,然后更新条件为{'$inc': {'age': 1}},也就是年龄加1,执行之后会将第一条符合条件的数据年龄加1。

运行结果如下:

<pymongo.results.UpdateResult object at 0x10b8874c8>
1 1
可以看到匹配条数为1条,影响条数也为1条。

如果调用update_many()方法,则会将所有符合条件的数据都更新,示例如下:

condition = {'age': {'$gt': 20}}
result = collection.update_many(condition, {'$inc': {'age': 1}})
print(result)
print(result.matched_count, result.modified_count)
这时匹配条数就不再为1条了,运行结果如下:

<pymongo.results.UpdateResult object at 0x10c6384c8>
3 3
可以看到,这时所有匹配到的数据都会被更新。"


"删除

删除操作比较简单,直接调用remove()方法指定删除的条件即可,此时符合条件的所有数据均会被删除。示例如下:

result = collection.remove({'name': 'Kevin'})
print(result)
运行结果如下:

{'ok': 1, 'n': 1}
另外,这里依然存在两个新的推荐方法——delete_one()和delete_many()。示例如下:

result = collection.delete_one({'name': 'Kevin'})
print(result)
print(result.deleted_count)
result = collection.delete_many({'age': {'$lt': 25}})
print(result.deleted_count)
运行结果如下:

<pymongo.results.DeleteResult object at 0x10e6ba4c8>
1
4
delete_one()即删除第一条符合条件的数据,delete_many()即删除所有符合条件的数据。它们的返回结果都是DeleteResult类型,可以调用deleted_count属性获取删除的数据条数。"
"其他操作

另外,PyMongo还提供了一些组合方法,如find_one_and_delete()、find_one_and_replace()和find_one_and_update(),它们是查找后删除、替换和更新操作,其用法与上述方法基本一致。

另外,还可以对索引进行操作,相关方法有create_index()、create_indexes()和drop_index()等。"

posted @ 2019-06-29 13:17  love_water  阅读(695)  评论(0编辑  收藏  举报