Mongo
基础知识与对文档的增删改查
文档就是键值对的一个有序集,例如
{"greeting":"hello"}
文档中的值可以时多种不同的数据类型;文档中的键时字符串,但有少数例外情况
集合就是一组文档,一个集合就相当于关系数据库的一张表
集合时动态模式的,就是说集合里面的文档可以时各式各样的。
命名需要满足以下条件:
使用.来分割不同命名空间的子集合,例如一个博客系统可能包含两个集合,分别时blog.posts和blog.authors。
数据库就是多个集合,一个mongo实例可以承载多个数据库,每个数据库可以有多个集合,每个数据库都有独立的权限。
数据库命名需要满足以下条件
有一些数据库名时保留的,可以直接访问这些特殊含义的数据库
功能完备的JavaScript解释器,可以运行任意JavaScript程序
insert函数将一个文档添加到集合中。
// 声明post变量
post={
"title":"my blog test",
"content":"blog post",
"date":new Date()
}
// 插入blog集合
db.blog.insert(post)
查询调用find方法
db.blog.find()
使用update()进行更新操作,接受两个参数,第一个限定条件,第二个时新的文档。
post.comments=[]
db.blog.update({title:"my blog test"},post)
使用remove()方法将文档从数据库永久删除
在启动shell指定机器名和端口,就可以连接不同的机器
$ mongo some-host:30000/myDB
在脚本中可以访问db变量,以及其他全局变量,然而shell辅助函数不可以在文件中使用
使用insert()方法向目标集合插入一个文档
db.foo.insert({"bar":"baz"})
使用batchInsert()方法向目标集合批量插入文档
db.foo.insert([{"_id":0},{"_id":1},{"_id":2}])
mongo只对数据进行最基本的检查,检查文档的基本结构,如果没有_id字段,就自动增加一个,并且所有文档都必须小于16MB
使用remove()删除
删除文档通常很快,如果要清空集合,建议使用drop直接删除集合(然后在空集合上重建索引)
使用update()进行更新
更新操作不可分割,先到先执行
用一个新文档完全替换匹配的文档,这适用于大规模迁移的情况
通常文档只会有一部分字段要更新,所以可以使用原子性的更新修改器,指定对文档中的某些字段进行更新。
更新修改器是种特殊的键,用来制定复杂的更新操作
$set修改器:用来制定一个字段的值,如果这个字段不存在,则创建它。这对更新模式或者增加用户定义的键非常方便。
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$set":{
"test":"test"
}
}
)
增加、修改、删除键时,应该使用$修改器
$inc修改器:用来增加已有键的值,如果该键不存在那就创建一个。对于更新分析数据、因果关系等有数值变化的地方非常方便
更新id为xxx的value=value+1
db.foo.update({
"_id":ObjectId("5ace332ff02a40eb6148fc36")
},
{
"$inc":{
"value":1
}
}
)
有一大类修改器可以用于操作数组
push修改器:如果数组已经存在,push会向已有的数组末尾加入一个元素,要是没有就创建一个新的数组
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$push":{
"posts":{
"name":"joe",
"email":"xxx@qq.com"
}
}
}
)
/* 1 */
{
"_id" : ObjectId("5ace2559f02a40eb6148fc34"),
"title" : "my blog test2",
"content" : "blog post2",
"date" : ISODate("2018-04-11T15:10:17.952Z"),
"test" : "test",
"posts" : [
{
"name" : "joe",
"email" : "xxx@qq.com"
}
]
}
以上是一种比较简单的push使用形式,也可以应用在一些比较复杂的数组操作,使用each子操作符,可以通过一次
添加多个元素到数组中
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$push":{
"posts":{
"$each":[111,1111,111111]
}
}
}
)
如果希望数组的长度时固定的,可以使用slice和push组合在一起使用,可以保证数组不会超过设定好的最大长度,实际上就得到了一个最多包含n个元素的数组
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$push":{
"posts":{
"$each":[111,1111,111111],
"$slice":-10
}
}
}
)
可以在清理元素之前使用$sort,只要向数组中添加子对象就需要清理
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$push":{
"posts":{
"$each":[111,1111,111111],
"$slice":-10,
"$sort":{
"rating":-1
}
}
}
}
)
这样会根据rating字段的值对数组中所有元素进行排序,然后保留前10个。
不能只将slice或者sort和push配合使用,且必须使用each
如果想将数组作为数据集使用,保证数组内的元素不会重复。可以使用$ne实现。
例如:要是作者不在引文列表中,就添加进去
db.papers.update(
{
"authors cited":{
"$ne": "Richie"
}
},
{
"$push":{
"authors cited":"Richie"
}
}
)
实现上述需求,还可以使用addToSet实现,有些时候,更适合用addToSet例如:有一个表示用户的文档,已经有了电子邮件地址的数据集,添加新地址时,用
db.users.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$addToSet":{
"emails":"doe@gmail.com"
}
}
)
将addToSet和each组合可以实现添加多个不同的值,可以一次添加多个邮件地址,
db.users.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$addToSet":{
"emails":{
"$each”: ["xxx@xxx.com","1111@xxx.com"]
}
}
}
)
$pop修改器:从数组的任何一端删除元素
从数组末尾删除一个元素
{
"$pop":{
"key":1
}
}
从数组头部删除一个元素
{
"$pop":{
"key":-1
}
}
$pull:居于特定条件删除元素,而不仅仅以及元素位置
db.lists.update({},
{
"$pull":{
"todo":"xxx"
}
}
)
删除todo等于xxx的文档
若时数组中有多个值,我们只想对其中的一部分进行操作,有另种方式
db.blog.update(
{
"post":post_id
},
{
"$inc":{
"commonts.0.votes:1
}
}
)
db.blog.update(
{
"comments.author":"john"
},
{
"$set":{
"commonts.$.votes":"jim"
}
}
)
是一种特殊的更新,要是没有找到符合更新条件的文档,就会以这个条件和更新文档为基础创建一个新的文档,如果找到了匹配的文档,则正常更新。
upsert非常方便,不必预制集合,同一套代码既可以用于创建文档也可以用于更新文档
记录网站页面访问次数的例子:
db.analytics.update(
{
"url":"/blog"
},
{
"$inc":{
"pageviews":1
}
},
true
)
save时一个shell函数,如果文档不存在,它会自动创建文档,如果文档存在,它就更新这个文档,它只有一个参数,文档。要是这个文档含有_id键,save会调用upsert,否在会调用insert
默认情况下,更新只能对符合匹配条件的第一个文档执行操作,要是有多个文档符合条件,只有第一个文档会呗更新。
如果要更新所有匹配的文档,可以将update的第四个参数设置为true
db.users.update({
"brithday":"10/13/1978"
},
{
"$set":{
"gift":"happy birhday"
}
},
false,
true
)
findAndModift能够在一个操作中返回匹配结果并进行更新
有时并不需要将文档中所有键/值对都返回,可以通过find(或findOne)的第二个参数来指定想要的键。 这样可以减少传输的数据量,又能节省客户端解码文档的时间和内存消耗。
db.users.find({},{
"username":1,
"email":1
})
如果不指定”_od”是否返回,”_id”是默认呗返回的
既然可以选择需要的键,当然也可以排除查询结果中的某些键值对
db.users.find({},{
"xxx":0
})
比较操作符:
例如查询“age”字段大于等于18、小于等于30的所有文档
db.users.find({
"age":{
"$gte":18,
"$lte":30
}
})
有两种方式进行OR查询:
$in可以用于查询一个键的多个值
db.users.find({
"user_id":{
"$in":[123456,"joe"]
}
})
与in相反的是nin,将返回与数组中所有条件都不匹配的文档
$or可以在多个键中查询任意的给定值
db.raffle.find({
"$or";[
{
"ticket_no":725
},
{
"winner":true
}
]
})
是元条件句,可以用在任何其他条件之上,表示否定的含义
条件语句时内层文档的键,而修改器是外层文档的键
一个键可以在任意多个条件,但是一个键不能对应多个更新修改器
null不仅会匹配某个键的值为null的文档,而且还会匹配不包含这个键的文档。这个匹配还会返回缺少这个键的所有文档
如果仅想匹配键值为null的文档,既要检查该键的值是否时null,还要通过$exists条件判断键值是否存在。
正则表达式能够有效地匹配字符串。
例如: 想要查找所有名为Joe或者joe的用户,就可以使用正则表达式执行不区分大小写的匹配
db.users.find({
"name":/joe/i
})
查询数组元素和查询标量值是一样的
例如有一个水果列表
db.food.insert({
"fruit":["aople","banana","peach"]
})
通过下面的查询可以成功匹配到文档
db.food.find({
"fruit":"banana"
})
如果需要通过多个元素来匹配数组,就需要使用$all。
下面将不能匹配到文档
db.food.find({
"fruit":["apple","banana"]
})
将数组第三个元素和peach进行匹配
db.food.find({
"fruit.2":"peach"
})
用它查询特定长度的数组。
db.food.find({
"fruit":{
"$size":3
}
})
$size并不能与其他查询条件组合使用,但是这种查询可以通过在文档中添加一个“size”的值
可以返回某个键匹配的数组元的一个子集
假设现在有一个博客文章的文档,我们希望返回前10条评论
db.blog.posts.findOne(criteria,{
"comments":{
"$slice":10
}
})
返回后10条
db.blog.posts.findOne(criteria,{
"comments":{
"$slice":-10
}
})
指定偏移量以及希望返回的元素数量,来返回元素集合中间位置的某些结果
db.blog.posts.findOne(criteria,{
"comments":{
"$slice":[23,10]
}
})
除非特别声明,否则使用$slice时返回文档中的所欲键,别的键说明符都是默认返回未提及的键
希望返回与查询条件相匹配的任意一个数组元素,可以使用$操作符得到一个匹配的元素。
用如下的方式得到Bob的评论
db.blog.posts.find({
"comments.name":"bob"
},{
"comments.$":1
})
文档中的标量(非数组元素)必须与查询条件中的每一条语句相匹配
使用elemMatch要求使用查询条件中的两个语句与一个数组元素进行比较,elemMatch不会匹配非数组元素
db.test.find({
"x":{
"$elemMatch":{
"$gt"10,
"$lt":20
}
}
})
有两种方法可以查询内嵌文档
查询整个内嵌文档与普通查询完全相同,例如有如下文档
{
"name":{
"first":"joe",
"last":"schmoe"
},
"age":45
}
要查询姓名为joe schmoe的人可以这样
db.peop;e.find({
"name":{
"first":"joe",
"last":"schmoe"
}
})
如果想要查询一个完整的子文档,那么子文档必须精确匹配,如果joe决定添加一个代表钟建明的键,那么查询就不在可行。
如果允许的话,通常只针对内嵌文档的特定键值进行查询,我们一般使用点表示法查询内嵌文档的键
db.people.find({
"name.first":"joe",
"name.last":"schmoe"
})
查询文档可以包含点来表达进入内嵌文档内部的意思
为安全起见,应该严格限制或消除$where语句的使用
最常见的应用就是比较文档中的两个键的值是否相等
数据库使用游标返回find的执行结果,客户端对游标的实现通常能够对最终结果进行有效的控制。可以限制结果的数量,略过部分结果,根据任意键按任意顺序的组合对结果进行各种排序,或者执行一些强大的操作。
要限制结果数量,可在find后使用limit函数
db.c.find().limit(3)
要是匹配的结果不到3个,则返回匹配数量的结果。
skip和limit类似,不过时跳过前n个匹配的文档,返回余下的文档
db.c.find().skip(3)
sort接受一个独享作为参数,这个对象时一组键值对,键对应文档的键名,值代表排序的方向。排序方向可以是1(升序)或者-1(降序)
db.c.find({
"username":1,
"age":-1
})
如果混合类型的键排序,其排序顺序是预先定义好的,优先级从小到大,其顺序如下:
两种类型查询:
用于向查询中添加各种选项:
注: