MongoDB是面向文档的数据库。
索引:MongoDB支持通用辅助索引,能进行多种快速查询,也提供唯一的、复合的和地理空间索引能力。
存储JavaScript:开发人员不必使用存储过程了,可以直接在服务端存取JavaScript的函数和值。
聚合:MongoDB支持MapReduce和其他聚合工具。
固定集合:集合的大小是有上限的,这对某些数据类型的数据特别有用(日志)。
文件存储:MongoDB支持用一种容易使用的协议存储大型文件和文件的元数据。
MongoDB使用MongoDB传输协议作为与服务器交互的主要方式。它对文档进行动态填充,预分配数据文件,用空间换取性能的稳定。默认的存储引擎中使用了内存映射文件,将内存管理工作交给操作系统去处理。动态查询优化器会“记住”执行查询最高效的方式。
文档是MongoDB的核心概念。多个键及其管理的值有序地放置在一起便是文档。文档中的键/值是有序的。文档的键使用任意UTF-8字符。键不能含有\0(空字符),这个字符用来表示键的结尾;.和$是被保留字,不可以使用;以下划线“_”开头的键是保留的。
集合是一组文档。集合是无模式的。集合的命名:集合名不可以是空字符串"";集合名不能含有\0;集合名不能以"system."开头,这是为系统集合保留的前缀;用户创建的集合名子不能含有保留字符$。
组织集合的一种惯例是使用"."字符分开的按命名空间划分的子集合。
数据库最终会变成文件系统中的文件,因此数据库名要满足不能是空字符串;不能还有""、.、/、\和\0;应全部小写;最多64字节。
保留的数据库:
admin:从权限的角度来看,这是"root"数据库。若将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。
local:这个数据库永远不会被复制,可以用来存储限于本地单台服务器的任意集合。
config:当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
MongoDB自带一个JavaScript shell,可以从命令行与MongoDB实例交互。可以通过shell执行管理操作、检查运行实例等。shell会在启动时自动连接MongoDB服务器。shell是功能完备的JavaScript解析器,可以运行任何JavaScript程序。
在shell查看操作数据会用到4个基本操作:创建、读取、更新和删除。
创建(insert):
>post = {"title" : "my blog", ... "content" : "Here is my blog", ... "data" : new Date() }
{
"title" : "My Blog" ,
"content" : "Here is my blog" ,
"data" : " "
}
db.blog.insert(post)
查找:db.blog.find(){}
db.blog.findOne(){}
find和findOne可以接受查询文档形式的限定条件。使用find时,shell自动显示最多20个匹配文档,但可以获取更多的文档。
更新:update接受至少2个参数:第一个是要更新文档的限定条件;第二个是新的文档。
post.comments=[]
db.blog.update({title:"My Blog"},post)
删除:remove用来从数据库中永久性的删除文档。在不使用参数进行调用的情况下,它会删除一个集合内的所有文档。
db.blog.remove({title:"My Blog"})
当属性与集合同名时,可以使用getCollection函数。若要查看名称中含有无效JavaScript字符的集合,也可使用getCollection函数。
MongoDB在保留JSON(null、布尔、数字、字符串、数组和对象)基本的键/值对特性的基础上,添加了其他一些数据类型(32位整数、64位整数、64位浮点数、对象id、日期、正则表达式、代码)。
MongoDB数字只能表示为双精度,因此尽量不要在shell下覆盖整个文档,并且有些64位的整数并能精确地表示为64位浮点数,所以若是存入了一个64位整数,然后在shell中查看它,它会显示一个内嵌文档,但表示可能不准。要插入的64位整数不能精确地作为双精度数显示,shell会添加两个键:"top"和"bottom",分别表示高32位和低32位。
数组是一组值,既可以作为有序对象来操作,也可作为无序对象操作。数组可以包含不同数据类型的元素,甚至是嵌套数组。
内嵌文档就是把整个MongoDB文档作为另一个文档中键的值。
MongoDB中存储的文档必须有一个"_id"键。这个键值可以是任何类型的,默认的是ObjectId对象。在一个集合里面,每个文档都有唯一的"_id"值来确保集合里面每个文档都能被唯一标识。
ObjectId是"_id"的默认类型。它设计成轻量型的,不同的机器都能用全局唯一的同种方法方便地生成它。ObjectId使用12字节的存储空间,每个字节两位16进制数字,是一个24位的字符串。ObjectId的创建方式:
0|1|2|3|4|5|6|7|8|9|10|11
时间戳 |机器 |PID| 计数器
前4个字节是从标准纪元开始的时间戳,单位为秒;随后的5个字节组合起来提供了秒级别的唯一性;后3个字节是一个自动增加的计数器,确保同一秒产生的ObjectId是不同的。同一秒最多允许每个进程拥有256^3个不同的ObjectId。
当执行插入操作的时候,使用的驱动程序会将数据转换成BSON的形式,然后将其送入数据库。数据库解析BSON,检验是否包含"_id"键并且文档不超过4MB,除此之外,不做别的数据验证,就只简单地将文档原样存入数据库中。
删除数据是永远的,不能撤销也不能恢复。删除文档通常会很快,但要清楚整个集合会更快。
更新操作是原子的:若要两个更新同时发生先到达服务器的先执行,接着执行另一个,所有冲突的更新都不互相干扰,最后个更新会取得胜利。
当模式结构发生了较大变化时,用一个较新的文档代替匹配的文档比较好;当文档只有部分要更新,利用原子的更新修改器性能会很高效。更新修改器是一种特殊的键。
修改器:
$set用来指定一个键的值。若这个键不存在,则创建它。
db.users.findOne(){
"_id" : ObjectId(1),
"name" : "joe",
"age" : 30
}
db.users.update({"_id" : ObjectId(1)},{"$set" : {"sex" : "male" }})
db.users.update({"_id" : ObjectId(1)},{"$set":{"favorite books": ["war and peace","cat's cradle","foundation trilogy"]}})
db.users.update({"name" : "joe",{"favorite books" :1}})
$inc用来增加已有的键的值,或在键不存在时创建一个键。只能用于整数、长整数或双精度浮点数。
db.games.update({"game" : "pinball","user" : "joe"},{"$inc":{"score : 50"}})
$push会向已有的数组末尾加入一个元素,要是没有则新建一个数组。
db.blog.update({"title" : "A blog",{$push : {"comments" : {"name":"joe","email" : "123@123.com","content" :"nice blog"}}})
db.blog.update({"title" : "A blog",{$push : {"comments" : [{"name":"joe1","email" : "1123@123.com","content" :"nice blog1"},{"name":"joe2","email" : "2123@123.com","content" :"nice blog2"}]}})
$ne可以查询文档。
db.papers.update({"authors cited" : {"$ne" : "Richie"},{$push : {"authors cited" : "Richie"}})
$addToSet添加集合并避免重复。
db.users.update({"_id" : ObjectId(1)},{"$addToSet" : {"emails":"12@qq.com"}})
$addToSet和$each组合起来,可以添加多个不同的值。
db.users.update({"_id" : ObjectId(1)},{"$addToSet" : {"emails" :{"$each" :["1@qq.com","2@qq.com","3@qq.com"]}}})
$pop修改器用来从数组任何一段删除元素。
{$pop :{key : 1}} 从数组末尾删除一个元素
{$pop :{key : -1}} 从数组头部删除一个元素
$pull用来删除基于特定条件的元素,而不仅仅是位置。
db.users.update({},{"$pull" : {"emails" : "1@qq,.com"}})
$数组的定位修改器。除此之外,还可以通过位置定位数组(从0开始)。
db.blog.update({"comments.author" : "John"},{"$set" : {"comment.$.auhtor" : "jim"}})
upsert是一种特殊的更新。要是没有文档符合更新条件,就会以这个条件和更新文档为基础创建一个新的文档。若找到了匹配的文档,则正常更新。
db.users.update({"url" : "/blog"},{"$inc" : {"visitis" : 1}},true)
最后一个参数表示upsert。
save是一个shell函数,可以在文档不存在时插入,存在实时更新。它只有一个参数:文档。
默认情况下,更新只能对符合匹配条件的第一个文档执行操作。要是有多个文档复合条件,其余的文档就没没有变化。要使所有匹配的文档都得到更新,可以设置update的第4个参数为true。
db.users.update({birthday : "10/30/1967"},{$set : {gift : "Happy Birthday!"}},false,true)
若想知道文档到底更新了多少文档,可以运行getLastError命令(getLastOpStatus),键值n就是想要的数字。
db.count.update({x : 1},{$inc : {x : 1}},false,true)
db.runCommand({getLastError : 1}){
"err" : null,
"updateExisting" :true,
"n" : 5,
"ok" : true
}
getLastModify可以返回已更新的文档。
ps = db.runCommand({"findAndModify" : "processes", "query" : {"status" : "READY"}, "sort" : {"priority" : -1}, "update" : {"$set" : {"status" : "RUNNING"}}})
findAndModify命令中的每个键对应的值:
findAndModify 字符串、集合名
query 插叙文档,用来检索文档的条件
sort 排序结果的条件
remove 布尔类型,表示是否删除文档
new 布尔类型,表示返回的是更新前的文档还是更新后的文档,默认是更新前的文档
update 修改器文档,对所有找到的文档执行更新。
update和remove必须有一个,也只能有一个。要是匹配不到文档,则报错。
安全的版本在执行完了操作后立即运行getLastError命令,来检查是否执行成功。
数据库会为每个MongoDB数据库连接创建一个队列,存放这个连接的请求。当客户端发送一个请求,会被放到队列的末尾。只有队列中的请求都执行完毕,后续的请求才会执行。
有时查询并不需要返回所有的键/值对。可以通过find的第二个参数来指定想要的键,也可用它来剔除查询结果中的某个键/值对。
db.users.find({},{"username" : 1, "email" : 1})
db.users.find({},{"sex" : 0})
查询条件
$lt < $lte <= $ge > $gte >= $ne !=
db.users.find({"age" : { "$gte" : 20 , "$lte" : 30}})
MongoDB中有两种方式进行OR查询。"$in"可以用来查询一个键的多个值。"$nin"将返回与数组中所有条件都不匹配的文档。"$or"用来完成多个键值的任意给定值。
db.raffle.find({"ticket_num" : {"$in"} : [110,119,120]})
db.raffle.find({"ticket_num" : {"$nin"} : [110,119,120]})
db.users.find({"user_id" : {"$in" : [110,"police"]}})
db.raffle.find({"$or" : [{"ticket_no" : 110,{"winner" : true}}]})
$not是元条件句,可以用在任何其他条件之上。
在查询中,$lt在内层文档,而更新中$inc在外层文档的键。条件句是内层文档的键,而修改器则是外层文档的键。
db.c.find({"z" : null})
若要匹配所有键值为null的文档,既要检查该键的值是否为null,还要通过"$exists"条件判定键值已经存在
db.c.find({"z" : {"$in" : [null], "$exists" : true}})
MongoDB使用Perl兼容的正则表达式(PCRE)库来匹配正则表达式,PCRE支持的正则表达式语法都能被MongoDB所接受。
$all若需要通过多个元素来匹配数组,就要用"$all"了。
db.food.find({fruit : {$all : ["apple","banana"]}})
$size可以查询其指定长度的数组。
db.food.insert({"_id" : 1, "fruit" : ["apple", "banana", "peach"] })
db.food.insert({"_id" : 2, "fruit" : ["apple", "orange", "peach"] })
db.food.insert({"_id" : 3, "fruit" : ["apple", "kumquat", "cherry"] })
db.food.find({"fruit"} : {"$size" : 3})
$slice返回数组的一个子集合。默认返回所有的键
返回前10条评论
db.blog.findOne(criteria, {"comments" : {"$slice" : 10}})
返回最后10条评论
db.blog.findOne(criteria, {"comments" : {"$slice" : -10}})
有两种方法查询内嵌文档:查询整个文档,或只针对其键/值对进行查询。查询文档可以包含点,来表达“深入内嵌文档内部”的意思。这就是点表示法样式插入的文档不能包含“.”的原因。将键作为url保存的时候会经常遇到这个问题。一种解决方法是在插入前或提取后执行一个全局替换,将“.”替换成一个URL中的非法字符。
使用$selemMatch这种模糊的命名条件句能用来部分指定匹配数组中的单个内嵌文档的限定条件。
db.blog.find({"comments" : {"$selemMatch" : {"author" : "joe" , "score" : {"$gte" : 5}}}})
索引:MongoDB支持通用辅助索引,能进行多种快速查询,也提供唯一的、复合的和地理空间索引能力。
存储JavaScript:开发人员不必使用存储过程了,可以直接在服务端存取JavaScript的函数和值。
聚合:MongoDB支持MapReduce和其他聚合工具。
固定集合:集合的大小是有上限的,这对某些数据类型的数据特别有用(日志)。
文件存储:MongoDB支持用一种容易使用的协议存储大型文件和文件的元数据。
MongoDB使用MongoDB传输协议作为与服务器交互的主要方式。它对文档进行动态填充,预分配数据文件,用空间换取性能的稳定。默认的存储引擎中使用了内存映射文件,将内存管理工作交给操作系统去处理。动态查询优化器会“记住”执行查询最高效的方式。
文档是MongoDB的核心概念。多个键及其管理的值有序地放置在一起便是文档。文档中的键/值是有序的。文档的键使用任意UTF-8字符。键不能含有\0(空字符),这个字符用来表示键的结尾;.和$是被保留字,不可以使用;以下划线“_”开头的键是保留的。
集合是一组文档。集合是无模式的。集合的命名:集合名不可以是空字符串"";集合名不能含有\0;集合名不能以"system."开头,这是为系统集合保留的前缀;用户创建的集合名子不能含有保留字符$。
组织集合的一种惯例是使用"."字符分开的按命名空间划分的子集合。
数据库最终会变成文件系统中的文件,因此数据库名要满足不能是空字符串;不能还有""、.、/、\和\0;应全部小写;最多64字节。
保留的数据库:
admin:从权限的角度来看,这是"root"数据库。若将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。
local:这个数据库永远不会被复制,可以用来存储限于本地单台服务器的任意集合。
config:当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。
MongoDB自带一个JavaScript shell,可以从命令行与MongoDB实例交互。可以通过shell执行管理操作、检查运行实例等。shell会在启动时自动连接MongoDB服务器。shell是功能完备的JavaScript解析器,可以运行任何JavaScript程序。
在shell查看操作数据会用到4个基本操作:创建、读取、更新和删除。
创建(insert):
>post = {"title" : "my blog", ... "content" : "Here is my blog", ... "data" : new Date() }
{
"title" : "My Blog" ,
"content" : "Here is my blog" ,
"data" : " "
}
db.blog.insert(post)
查找:db.blog.find(){}
db.blog.findOne(){}
find和findOne可以接受查询文档形式的限定条件。使用find时,shell自动显示最多20个匹配文档,但可以获取更多的文档。
更新:update接受至少2个参数:第一个是要更新文档的限定条件;第二个是新的文档。
post.comments=[]
db.blog.update({title:"My Blog"},post)
删除:remove用来从数据库中永久性的删除文档。在不使用参数进行调用的情况下,它会删除一个集合内的所有文档。
db.blog.remove({title:"My Blog"})
当属性与集合同名时,可以使用getCollection函数。若要查看名称中含有无效JavaScript字符的集合,也可使用getCollection函数。
MongoDB在保留JSON(null、布尔、数字、字符串、数组和对象)基本的键/值对特性的基础上,添加了其他一些数据类型(32位整数、64位整数、64位浮点数、对象id、日期、正则表达式、代码)。
MongoDB数字只能表示为双精度,因此尽量不要在shell下覆盖整个文档,并且有些64位的整数并能精确地表示为64位浮点数,所以若是存入了一个64位整数,然后在shell中查看它,它会显示一个内嵌文档,但表示可能不准。要插入的64位整数不能精确地作为双精度数显示,shell会添加两个键:"top"和"bottom",分别表示高32位和低32位。
数组是一组值,既可以作为有序对象来操作,也可作为无序对象操作。数组可以包含不同数据类型的元素,甚至是嵌套数组。
内嵌文档就是把整个MongoDB文档作为另一个文档中键的值。
MongoDB中存储的文档必须有一个"_id"键。这个键值可以是任何类型的,默认的是ObjectId对象。在一个集合里面,每个文档都有唯一的"_id"值来确保集合里面每个文档都能被唯一标识。
ObjectId是"_id"的默认类型。它设计成轻量型的,不同的机器都能用全局唯一的同种方法方便地生成它。ObjectId使用12字节的存储空间,每个字节两位16进制数字,是一个24位的字符串。ObjectId的创建方式:
0|1|2|3|4|5|6|7|8|9|10|11
时间戳 |机器 |PID| 计数器
前4个字节是从标准纪元开始的时间戳,单位为秒;随后的5个字节组合起来提供了秒级别的唯一性;后3个字节是一个自动增加的计数器,确保同一秒产生的ObjectId是不同的。同一秒最多允许每个进程拥有256^3个不同的ObjectId。
当执行插入操作的时候,使用的驱动程序会将数据转换成BSON的形式,然后将其送入数据库。数据库解析BSON,检验是否包含"_id"键并且文档不超过4MB,除此之外,不做别的数据验证,就只简单地将文档原样存入数据库中。
删除数据是永远的,不能撤销也不能恢复。删除文档通常会很快,但要清楚整个集合会更快。
更新操作是原子的:若要两个更新同时发生先到达服务器的先执行,接着执行另一个,所有冲突的更新都不互相干扰,最后个更新会取得胜利。
当模式结构发生了较大变化时,用一个较新的文档代替匹配的文档比较好;当文档只有部分要更新,利用原子的更新修改器性能会很高效。更新修改器是一种特殊的键。
修改器:
$set用来指定一个键的值。若这个键不存在,则创建它。
db.users.findOne(){
"_id" : ObjectId(1),
"name" : "joe",
"age" : 30
}
db.users.update({"_id" : ObjectId(1)},{"$set" : {"sex" : "male" }})
db.users.update({"_id" : ObjectId(1)},{"$set":{"favorite books": ["war and peace","cat's cradle","foundation trilogy"]}})
db.users.update({"name" : "joe",{"favorite books" :1}})
$inc用来增加已有的键的值,或在键不存在时创建一个键。只能用于整数、长整数或双精度浮点数。
db.games.update({"game" : "pinball","user" : "joe"},{"$inc":{"score : 50"}})
$push会向已有的数组末尾加入一个元素,要是没有则新建一个数组。
db.blog.update({"title" : "A blog",{$push : {"comments" : {"name":"joe","email" : "123@123.com","content" :"nice blog"}}})
db.blog.update({"title" : "A blog",{$push : {"comments" : [{"name":"joe1","email" : "1123@123.com","content" :"nice blog1"},{"name":"joe2","email" : "2123@123.com","content" :"nice blog2"}]}})
$ne可以查询文档。
db.papers.update({"authors cited" : {"$ne" : "Richie"},{$push : {"authors cited" : "Richie"}})
$addToSet添加集合并避免重复。
db.users.update({"_id" : ObjectId(1)},{"$addToSet" : {"emails":"12@qq.com"}})
$addToSet和$each组合起来,可以添加多个不同的值。
db.users.update({"_id" : ObjectId(1)},{"$addToSet" : {"emails" :{"$each" :["1@qq.com","2@qq.com","3@qq.com"]}}})
$pop修改器用来从数组任何一段删除元素。
{$pop :{key : 1}} 从数组末尾删除一个元素
{$pop :{key : -1}} 从数组头部删除一个元素
$pull用来删除基于特定条件的元素,而不仅仅是位置。
db.users.update({},{"$pull" : {"emails" : "1@qq,.com"}})
$数组的定位修改器。除此之外,还可以通过位置定位数组(从0开始)。
db.blog.update({"comments.author" : "John"},{"$set" : {"comment.$.auhtor" : "jim"}})
upsert是一种特殊的更新。要是没有文档符合更新条件,就会以这个条件和更新文档为基础创建一个新的文档。若找到了匹配的文档,则正常更新。
db.users.update({"url" : "/blog"},{"$inc" : {"visitis" : 1}},true)
最后一个参数表示upsert。
save是一个shell函数,可以在文档不存在时插入,存在实时更新。它只有一个参数:文档。
默认情况下,更新只能对符合匹配条件的第一个文档执行操作。要是有多个文档复合条件,其余的文档就没没有变化。要使所有匹配的文档都得到更新,可以设置update的第4个参数为true。
db.users.update({birthday : "10/30/1967"},{$set : {gift : "Happy Birthday!"}},false,true)
若想知道文档到底更新了多少文档,可以运行getLastError命令(getLastOpStatus),键值n就是想要的数字。
db.count.update({x : 1},{$inc : {x : 1}},false,true)
db.runCommand({getLastError : 1}){
"err" : null,
"updateExisting" :true,
"n" : 5,
"ok" : true
}
getLastModify可以返回已更新的文档。
ps = db.runCommand({"findAndModify" : "processes", "query" : {"status" : "READY"}, "sort" : {"priority" : -1}, "update" : {"$set" : {"status" : "RUNNING"}}})
findAndModify命令中的每个键对应的值:
findAndModify 字符串、集合名
query 插叙文档,用来检索文档的条件
sort 排序结果的条件
remove 布尔类型,表示是否删除文档
new 布尔类型,表示返回的是更新前的文档还是更新后的文档,默认是更新前的文档
update 修改器文档,对所有找到的文档执行更新。
update和remove必须有一个,也只能有一个。要是匹配不到文档,则报错。
安全的版本在执行完了操作后立即运行getLastError命令,来检查是否执行成功。
数据库会为每个MongoDB数据库连接创建一个队列,存放这个连接的请求。当客户端发送一个请求,会被放到队列的末尾。只有队列中的请求都执行完毕,后续的请求才会执行。
有时查询并不需要返回所有的键/值对。可以通过find的第二个参数来指定想要的键,也可用它来剔除查询结果中的某个键/值对。
db.users.find({},{"username" : 1, "email" : 1})
db.users.find({},{"sex" : 0})
查询条件
$lt < $lte <= $ge > $gte >= $ne !=
db.users.find({"age" : { "$gte" : 20 , "$lte" : 30}})
MongoDB中有两种方式进行OR查询。"$in"可以用来查询一个键的多个值。"$nin"将返回与数组中所有条件都不匹配的文档。"$or"用来完成多个键值的任意给定值。
db.raffle.find({"ticket_num" : {"$in"} : [110,119,120]})
db.raffle.find({"ticket_num" : {"$nin"} : [110,119,120]})
db.users.find({"user_id" : {"$in" : [110,"police"]}})
db.raffle.find({"$or" : [{"ticket_no" : 110,{"winner" : true}}]})
$not是元条件句,可以用在任何其他条件之上。
在查询中,$lt在内层文档,而更新中$inc在外层文档的键。条件句是内层文档的键,而修改器则是外层文档的键。
db.c.find({"z" : null})
若要匹配所有键值为null的文档,既要检查该键的值是否为null,还要通过"$exists"条件判定键值已经存在
db.c.find({"z" : {"$in" : [null], "$exists" : true}})
MongoDB使用Perl兼容的正则表达式(PCRE)库来匹配正则表达式,PCRE支持的正则表达式语法都能被MongoDB所接受。
$all若需要通过多个元素来匹配数组,就要用"$all"了。
db.food.find({fruit : {$all : ["apple","banana"]}})
$size可以查询其指定长度的数组。
db.food.insert({"_id" : 1, "fruit" : ["apple", "banana", "peach"] })
db.food.insert({"_id" : 2, "fruit" : ["apple", "orange", "peach"] })
db.food.insert({"_id" : 3, "fruit" : ["apple", "kumquat", "cherry"] })
db.food.find({"fruit"} : {"$size" : 3})
$slice返回数组的一个子集合。默认返回所有的键
返回前10条评论
db.blog.findOne(criteria, {"comments" : {"$slice" : 10}})
返回最后10条评论
db.blog.findOne(criteria, {"comments" : {"$slice" : -10}})
有两种方法查询内嵌文档:查询整个文档,或只针对其键/值对进行查询。查询文档可以包含点,来表达“深入内嵌文档内部”的意思。这就是点表示法样式插入的文档不能包含“.”的原因。将键作为url保存的时候会经常遇到这个问题。一种解决方法是在插入前或提取后执行一个全局替换,将“.”替换成一个URL中的非法字符。
使用$selemMatch这种模糊的命名条件句能用来部分指定匹配数组中的单个内嵌文档的限定条件。
db.blog.find({"comments" : {"$selemMatch" : {"author" : "joe" , "score" : {"$gte" : 5}}}})