查看版本号
[[email protected]_0_12_centos bin]# ./mongo -version MongoDB shell version v3.6.5 git version: a20ecd3e3a174162052ff99913bc2ca9a839d618 OpenSSL version: OpenSSL 1.0.1e-fips 11 Feb 2013 allocator: tcmalloc modules: none build environment: distmod: rhel70 distarch: x86_64 target_arch: x86_64
查看
show dbs; 查看数据库,查看库占用空间
查找
db.tablename.find() 查找所有
使用
use databaseName 选库
show tables/collections 显示表
注意 system开头的表不要动
admin和Local这两个自带的库不要动
show databases; 个别版本可以使用
db.help(); 查看帮助
创建
mongodb是隐式创建数据库,直接use就能创建一个不存在的数据库。
use newdbName;
表也可以隐式创建。
db.goods.insert({_id:1,name:‘NOKIAn86‘,price:29.9})
db.createCollection(‘user‘); 创建user表
插入
db.user.insert({name:‘zhangsan‘,age:22}); 插入数据
db.user.find(); 查看user表数据,会查到一个主键,_id,也是主键,自动生成的
db.user.insert({_id:2,name:‘poly‘,age:23}); 指定主键的插入方式
mongodb可以插入格式不一样的文档格式
例如:
db.user.insert({_id:3,name:‘hmm‘,hobby:[‘basketball‘,‘football‘],intro:{‘title‘:‘my intro‘,‘content‘:‘from china‘}})
以上这种格式和之前的格式并不冲突,没有结构的特点。
删除
db.user.drop(); 删除表
db.dropDatabase(); 删除库
增删改查:
需要传一个json对象。
修改:
语法:
db.collection.update(查询表达式,新值,选项)
db.table.update(匹配条件,新文档,true(upsert),true(修改多个值))
例:
db.news.update({name:‘QQ‘},{name:‘MSN‘});
查找news表中name值为QQ的文档,并将值改为msn,注意:这是替换,其他的值也会不见。
如果只想修改某一列,用$set关键字
db.collectionName.update(query,{$set:{name:‘QQ‘}})
$set 修改某列的值
$unset 删除某个列
$rename 重命名某个列
$inc 增长某个列
$setOnInsert 当upsert为true时,并发生了Isnert操作时,可以补充的字段。
在mongodb的命令交互界面中可以执行以下命令:
db 查看目前指向的数据库
还可以执行数学计算
x = 200
x / 5 ;
可以使用JavaScript标准库
Math.sin(Math.PI / 2);
new Date("2010/1/1");
"Hello,World".replace("World","Mongodb");
新建表
> post={"title":"My Blog Post","content":"Here‘s my blog post.","date":new Date()}
{
"title" : "My Blog Post",
"content" : "Here‘s my blog post.",
"date" : ISODate("2018-07-06T02:35:57.716Z")
}
> db.blog.insert(post)
WriteResult({ "nInserted" : 1 })
> show collections;
blog
fuzzing_agent.configuration
system.users
system.version
定义一个字典
然后db.表名.insert(变量)
只查看一个文档:
findOne()
> db.blog.findOne();
{
"_id" : ObjectId("5b3ed5c1de8e397067390e0d"),
"title" : "My Blog Post",
"content" : "Here‘s my blog post.",
"date" : ISODate("2018-07-06T02:35:57.716Z")
}
find命令一次最多显示20个匹配的文档。
update
update需要两个参数,第一个是限定条件(老文档,被替换的),第二个是新的文档。
> post.comments = []
[ ]
> db.blog.update({title:"My Blog Post"},post)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.find()
{ "_id" : ObjectId("5b3ed5c1de8e397067390e0d"), "title" : "My Blog Post", "content" : "Here‘s my blog post.", "date" : ISODate("2018-07-06T02:35:57.716Z"), "comments" : [ ] }
>
删除
remove
db.blog.remove({title:"My Blog Post"})
修改
$set
需要用$修改器,如果不用,直接update({目标文档},{新文档}),会将原来的文档直接替换。
db.tester.update({"baz":0},{"$set":{"favorite book":"war and peace"}})
如果{"baz":0}这个字段中没有"favorite book"这个字段,则添加;有则修改
修改内嵌文档
例:
> db.blog.findOne();
{
"_id" : ObjectId("5b431029f9cc01fb9708024b"),
"title" : "A Blog Post",
"content" : "...",
"author" : {
"name" : "joe",
"email" : "[email protected]"
}
}
> db.blog.update({"author.name":"joe"},{"$set":{"author.name":"michel"}})
> db.blog.findOne();
{
"_id" : ObjectId("5b431029f9cc01fb9708024b"),
"title" : "A Blog Post",
"content" : "...",
"author" : {
"name" : "michel",
"email" : "[email protected]"
}
}
$inc增加某个键的数字值
例:
> db.games.findOne();
{
"_id" : ObjectId("5b431235f9cc01fb9708024c"),
"game" : "pinball",
"user" : "tom"
}
> db.games.update({"user":"tom"},{"$inc":{"score":50}}) #没有则新建
注意,$inc必须是数字格式,用于整型,长整型,双精度浮点型,不能用于null,布尔型,数字构成的字符串
> db.games.findOne();
{
"_id" : ObjectId("5b431235f9cc01fb9708024c"),
"game" : "pinball",
"user" : "tom",
"score" : 50
}
$inc的用处如下:
> db.games.update({"user":"tom"},{"$inc":{"score":1000}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.games.findOne();
{
"_id" : ObjectId("5b431235f9cc01fb9708024c"),
"game" : "pinball",
"user" : "tom",
"score" : 1050
}
会将score这个值在原有50的基础上增加1000,改为1050
$push,向某个数组中添加元素
例:
> db.blog.findOne({"title":"A blog post"});
{
"_id" : ObjectId("5b431538f9cc01fb9708024d"),
"title" : "A blog post",
"content" : "...",
"commnet" : [
{
"name" : "joe",
"email" : "[email protected]",
"content" : "nice post"
}
]
}
之前没有commnet这个键值对,通过
> db.blog.update({"title" : "A blog post", "content" : "..."},{"$push":{"commnet":{"name":"joe","email":"[email protected]","content":"nice post"}}})
增加,会生成commnet:[]这个格式。
在原有基础上,向commnet这个集合中添加内容:
> db.blog.update({"title":"A blog post"},{"$push":{"commnet":{"name":"michel","email":"[email protected]","content":"good job"}}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.blog.findOne({"title":"A blog post"});
{
"_id" : ObjectId("5b431538f9cc01fb9708024d"),
"title" : "A blog post",
"content" : "...",
"commnet" : [
{
"name" : "joe",
"email" : "[email protected]",
"content" : "nice post"
},
{
"name" : "michel",
"email" : "[email protected]",
"content" : "good job"
}
]
}
列表中的多了一个文档。
其他:
$each 可以通过一次push添加多个值
$slice slice必须是负数,限制包含最后加入的slice个数,比如-10,就会只保留最后添加的10个数,如果不够10个则全保留。
$sort 排序
注意,不能只将slice或sort与push配合使用,必须使用$each
$addToSet,可以避免添加重复内容:
db.users.update({"_id":ObjectId("123")},{"$addToSet":{"emails":"[email protected]"}})
如上,添加[email protected]的时候,会检查是否已经存在,如果已经存在则不变,不存在则添加
一次添加多个邮件地址,并去重:
db.users.update({"_id":ObjectId("123")},{"$addToSet":{"emails":{"$each":["[email protected]","[email protected]","[email protected]"]}}})
$ne也是避免添加重复内容,在查找条件中写,效果不如addToSet
基于位置的数组修改器
通过数组的下标找到对应元素,下标从0开始。
db.blog.update({"post":"post_id"},{"$inc":{"comments.0.vates":1}})
如上,找到comments集合中下标为0的元素,将其vates+1
如果不知道下标位置,但知道需要更改的内容,可以通过一下方法修改:
db.blog.update({"comments.author":"John"},{"$set":{"comments.$.author":"jim"}})
如上,将已经匹配的John修改为jim,不知道下标则写$
修改器的速度
有的修改器速度比较快,如$inc,可以就地更改,不需要修改文档的大小,只需要改值。
有的会修改文档的大小,速度就会慢一些,$set能在文档大小不发生变化时立即修改,否则性能也会下降。
文档在插入Mongodb的时候,依次插入的文档会在磁盘上的位置是相邻的,因此如果一个文档变大了,原先的位置放不下了,这个文档就会被移动到集合的另一个位置。
比如
{"x":"a"}
{"x":"b"}
{"x":"c"}
将x:b改为x:bbbbbb
结果是
{"x":"a"}
{"x":"c"}
{"x":"bbbbb"}
填充因子
db.tablename.stats()
原理:
填充因子是Mongodb为每个新文档预留的增长空间,如果一个文档增大之后,填充因子会随之增加,当初没有多余空间的时候,文档会移动位置,然后之前的位置被填充因子覆盖,填充因子变大之后,所有的空间都会增加
到填充因子的大小,直到文档不在继续变大,之后,填充因子会缓慢变小。
填充因子的大小无法手动设置。
upsert
特殊的更新机制,如果没找到符合更新条件的文档,就会以这个条件和更新文档为基础,创建一个新的文档。
传统的修改方式:
js
> blog=db.analytics.findOne({"url":"/blog"}) #判断是否有这个文档
null
> if (blog){blog.pageviews++;db.analytics.save(blog);} else{db.analytics.save({"url":"/blog",pageviews:1})} #如果有则+1,没有则保存
WriteResult({ "nInserted" : 1 })
> show tables;
analytics
blog
games
list
tester
> db.analytics.find();
{ "_id" : ObjectId("5b432c34f9cc01fb9708024f"), "url" : "/blog", "pageviews" : 1 }
>
使用upsert,既可以避免竞态问题,又可以缩减代码量,
第三个参数表示这个是upsert
db.analytics.update({"url":"/blog"},{"$inc":{"pageviews":1}},true)
作用和上面的一样,但是更高效,并且是原子性。
> db.users.update({"rep":25},{"$inc":{"rep":3}},true)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("5b432dc2764f0dcf8c8302cf")
})
如上,upsert创建一个rep值为25的文档,随后将这个值+3,最后得到28.
如果不加这个true,也就是不用upsert,update则匹配不到这个rep:25,然后就不会对集合进行任何更新。
如果再执行这条代码。则会在次创建一个文档,因为没有匹配到rep:25,唯一一个文档是rep:28
$setOnInsert
创建文档的同时并为他赋值,但是在之后的所有更新操作中,这个字段的值不在改变。
例:
> db.time.update({},{"$setOnInsert":{"createdAt":new Date()}},true)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("5b432f81764f0dcf8c8302dc")
})
> db.time.find();
{ "_id" : ObjectId("5b432f81764f0dcf8c8302dc"), "createdAt" : ISODate("2018-07-09T09:48:48.986Z") }
> db.time.update({},{"$setOnInsert":{"createdAt":new Date()}},true)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
> db.time.find();
{ "_id" : ObjectId("5b432f81764f0dcf8c8302dc"), "createdAt" : ISODate("2018-07-09T09:48:48.986Z") }
>
再次运行这个更新,会匹配到这个文档已存在,所以不会再插入文档,因此createdAt的值也不会改变。
同时更新多个文档
默认情况,更新只针对第一个匹配的文档执行操作,要是需要有多个文档符合条件并被更新,需要将Update的第四个参数设置为true。
> db.users.find();
{ "_id" : ObjectId("5b44117cf9cc01fb97080252"), "name" : "tom", "birthday" : "10/13/1991" }
{ "_id" : ObjectId("5b441194f9cc01fb97080253"), "name" : "jerry", "birthday" : "05/24/1993" }
{ "_id" : ObjectId("5b4411a6f9cc01fb97080254"), "name" : "mike", "birthday" : "10/13/1991" }
{ "_id" : ObjectId("5b4411bff9cc01fb97080255"), "name" : "michel", "birthday" : "03/14/1989" }
{ "_id" : ObjectId("5b4411d0f9cc01fb97080256"), "name" : "lucy", "birthday" : "10/13/1991" }
> db.users.update({"birthday":"10/13/1991"},{"$set":{"gift":"happy birthday!!!"}},false,true)
WriteResult({ "nMatched" : 3, "nUpserted" : 0, "nModified" : 3 })
> db.users.find();
{ "_id" : ObjectId("5b44117cf9cc01fb97080252"), "name" : "tom", "birthday" : "10/13/1991", "gift" : "happy birthday!!!" }
{ "_id" : ObjectId("5b441194f9cc01fb97080253"), "name" : "jerry", "birthday" : "05/24/1993" }
{ "_id" : ObjectId("5b4411a6f9cc01fb97080254"), "name" : "mike", "birthday" : "10/13/1991", "gift" : "happy birthday!!!" }
{ "_id" : ObjectId("5b4411bff9cc01fb97080255"), "name" : "michel", "birthday" : "03/14/1989" }
{ "_id" : ObjectId("5b4411d0f9cc01fb97080256"), "name" : "lucy", "birthday" : "10/13/1991", "gift" : "happy birthday!!!" }
如上,给users表生日为10/13/1991的人一个gift,效果如上。
删除
删除某个键
db.tester.update({"baz":1},{"$unset":{"favorite book":"Ender‘s Game"}})
删除baz:1这个条目的favorite book字典
删除数组中的元素
$pop
把数组看成队列,可以用$pop
{"$pop":{"key":1}} 从数组最后一个元素删除
{"$pop":{"key":-1}} 从数组第一个元素删除
$pull
根据条件删除数组元素
> db.list.insert({"todo":["dishes","laundry","dry cleaning"]})
WriteResult({ "nInserted" : 1 })
> db.list.find();
{ "_id" : ObjectId("5b431e50f9cc01fb9708024e"), "todo" : [ "dishes", "laundry", "dry cleaning" ] }
> db.list.update({},{"$pull":{"todo":"laundry"}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.list.find();
{ "_id" : ObjectId("5b431e50f9cc01fb9708024e"), "todo" : [ "dishes", "dry cleaning" ] }
如上,删除了laundry
$pull会将所有匹配的文档删除,而不是只删除一个,对数组[1,1,2,1]执行Pull 1 则结果只剩2.
数组操作符只能用于包含数组值得键,例如,不能将一个整数插入数组,也不能将一个字符串从数组中弹出,要修改标量值,需要$set和$inc。
getLastError
检查最后一次操作中的错误。编写脚本的时候可能会用到。
db.runCommand({getLastError:1})
findAndModify
更新文档并返回被更新的文档
db.runCommand({"findAndModify":"processes","query":{"status":"READY"},"sort":{"priority":-1},"update":{"$set":{"status":"RUNNING"}}})
findAndModify的其他字段
query 查询文档,用于检索文档的条件
sort 排序结果的条件
update 修改文档,用于对匹配的文档进行更新
remove 布尔类型,表示是否删除文档
new 布尔类型,表示返回更新前还是更新后的文档,默认是更新前的文档
fields 文档中需要返回的字段
upsert 布尔类型,值为true表示upsert,默认为false
查询
查询条件
db.table.find(匹配条件,指定返回内容)
db.users.find({"name":"tom","age":27}) 两个查询条件,条件1 and 条件2
指定返回内容
通过find或findOne的第二个参数来指定想要返回的键
例
db.users.find({},{"username":1,"email":1})
默认情况下,_id这个键是被返回的。
不希望返回_id的做法:
db.users.find({},{"username":1,"id":0})
如上返回了username,但是没有返回id
$lt = ‘<‘
$lte = ‘<=‘
$gt = ‘>‘
$gte =‘>=‘
$ne 不等于 <>
例:
db.users.find({"age":{"$gte":18,"$lte":30}})
如上查找大于等于18 and小于等于30的人
注意:
{"x":5}
{"x":15}
{"x":25}
{"x":[5,25]}
>db.test.find({"x":{"$gt":10,"$lt":20}})
{"x":15}
{"x":[5,25]}
如上,之所以会返回[5,25] 是因为25大于等于10,所以也一同返回了,
db.test.find({"x":{"$elemMatch":{"$gt":10,"$lt":20}}})
$elemMatch可以同时查询条件中两个语句与一个数组元素做比较,但是$elemMatch不会匹配非数组元素,所以在这里找不到任何匹配内容。
如果当前字段创建过索引,可以使用min()和max()将查询条件遍历的索引范围限制为$gt和$lt的值
>db.test.find({"x":{"$gt":10,"$lt":20}}).min({"x":10}).max({"x":20})
这种方法对日期也同样有用
例:
start=new Date("01/01/2007")
db.users.find({"registered":{"$lt":start}})
如上,查找01/01/2007以前注册的人。
$ne用法
db.users.find("username":{"$ne":"joe"})
查找名字不叫Joe的人
OR查询
$in
$or
$nin
$not
$in
例:
查找中奖号码是725,542,390的所有中奖文档。
db.raffle.find({"ticket_no":{"$in":[725,542,390]}})
例:
超找需要同时匹配id和用户名
db.users.find({"user_id":{"$in":[12345,"joe"]}})
如上,会匹配user_id是12345的,也会匹配user_id是joe的
$nin
与$in相反
例:
找到所有没中奖的人
db.raffle.find({"ticket_no":{"$nin":[725,542,390]}})
$or
例:
找到中奖的人
db.raffle.find({"$or":[{"ticket_no":725},{"winner":true}]})
如上,找到ticket_no为725的或者有winner为true的人
复杂一点的查找中奖人,结合$or和$in:
db.raffle.find({"$or":[{"ticket_no":{"$in":[725,542,390]}},{"winner":true}]})
如上,为查找ticket_no为725或542或390的,或者winner为true的人。
$or可以同时使用不同的查询条件,而$in只能或同样类型的条件。
$not
db.users.find({"id_num":{"$not":{"$mod":[5,1]}}})
如上$not为取反,$mod为取模
null
匹配null
如果直接通过"z":null,会将其他键不是z但值是null的也匹配出来,所以需要写成以下方式:
db.c.find({"z":{"$in":[null],"$exists":true}})
如上,匹配z的值为null的同时判断是否存在。
正则表达式
匹配名字为Joe或者joe的用户
db.users.find({"name":/joe/i})
Mongodb使用PCRE正则表达式库来匹配正则,可以先用js检查一下语法。
数组查询
数组元素查询和普通查询是一样的。
db.food.insert({"fruit":["apple","banana","peach"]})
查询:
db.food.find({"fruit":"banana"})
也能找到该条数据,效果如下(不合法):
{"fruit":"apple","fruit":"banana","furit":"peach"}
$all
匹配既有a也有b的,和and类似
> db.food.find();
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 2, "fruit" : [ "apple", "kumquat", "orange" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
> db.food.find({"fruit":"apple","fruit":"banana"})
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
> db.food.find({"fruit":{"$all":["apple","banana"]}})
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
如上第二个,是使用$all的写法,作用是匹配既有apple又有banana的情况。
第一种是我自己想的方法,效果是一样的。
$size
根据指定列表长度,来筛选符合列表长度的文档。
> db.food.find();
{ "_id" : 1, "fruit" : [ "apple", "banana", "peach" ] }
{ "_id" : 2, "fruit" : [ "apple", "kumquat", "orange" ] }
{ "_id" : 3, "fruit" : [ "cherry", "banana", "apple" ] }
{ "_id" : 4, "fruit" : [ "orange", "blueberry" ] }
> db.food.find({"fruit":{"$size":2}})
{ "_id" : 4, "fruit" : [ "orange", "blueberry" ] }
$slice
find的第二个参数是指定返回内容,配合$slice可以指定返回匹配数组元素的一个子集
例如:
db.blog.findOne(匹配条件,{"comments":{"$slice":10}})
如上,查找博客评论前10条评论
例如:
db.blog.findOne(匹配条件,{"comments":{"$slice":-10}})
如上,查找博客评论后10条评论
例如:
db.blog.findOne(匹配条件,{"comments":{"$slice":[23,10]}})
如上,查找评论集合第24-33的元素,23是指跳过前23个元素,10是指取10个元素。
注意,slice会默认返回除了指定子集的其他所有的键,比如_id,title什么的。
例如:
db.blog.find({"comments.name":"bob"},{"comments.$":1})
如上查找评论名为bob的数组元素,并取第一个,如果bob评论了多条也只返回第一个。
内嵌文档查询:
> db.score.findOne()
{
"_id" : ObjectId("5b4468fef9cc01fb97080257"),
"content" : "joe",
"comments" : [
{
"author" : "joe",
"score" : 3,
"comment" : "nice post"
},
{
"author" : "mary",
"score" : 6,
"comment" : "terrible post"
}
]
}
查询mary分数在5分以上的评论
> db.score.find({"comments":{"$elemMatch":{"author":"mary","score":{"$gte":5}}}})
{ "_id" : ObjectId("5b4468fef9cc01fb97080257"), "content" : "joe", "comments" : [ { "author" : "joe", "score" : 3, "comment" : "nice post" }, { "author" : "mary", "score" : 6, "comment" : "terrible post" } ] }
$elemMatch将限定条件进行分组,仅当需要对一个内嵌文档的多个键操作时才会用到。
内嵌文档不能使用如下查找方式:
db.score.find({"comments":{"author":"mary","score":{"$gte":5}}})
内嵌文档的匹配必须整个文档完全匹配,这个查询不会匹配comments键
db.score.find({"comments.author":"mary","comments.score":{"$gte":5}})
符合author条件的评论和符合score条件的评论可能不是一个评论。
$where
$where可以后面使用javascript语句,所以尽量限制用户使用$where方式查询。
例子
> db.fooo.find();
{ "_id" : ObjectId("5b446d78f9cc01fb97080258"), "apple" : 1, "banana" : 6, "speach" : 3 }
{ "_id" : ObjectId("5b446d92f9cc01fb97080259"), "apple" : 8, "spinach" : 4, "watermelon" : 4 }
查找两个键具有相同值得文档,第二个文档中spinach和watermelon的值相同,所以应该返回它
> db.fooo.find({"$where":function(){for (var current in this){ for (var other in this){if (current != other && this[current] == this [other]){return true ;}}}return false;}});
{ "_id" : ObjectId("5b446d92f9cc01fb97080259"), "apple" : 8, "spinach" : 4, "watermelon" : 4 }
>
$where查询比常规查询慢很多,而且有一定危险,所以尽量不用。
每个文档都要从BSON转换成JavaScript对象,然后通过$where表达式运行,而且$where不能使用索引。
limit
>db.c.find().limit(3)
如上,限制返回结果为前三条
skip
db.c.find().skip(3)
如上,跳过前三条结果,如果集合里不足3条则不予显示
sort
db.stock.find().sort({username:1,age:-1})
如上,按照username升序及age降序排序
1为升序,-1为降序
组合使用:
db.stock.find({"desc":"mp3"}).limit(50).sort("price":-1)
如上,可以作为在线商店的分页使用,返回Mp3内容前50条,并按价格从高到低排序。
点击下一页后:
db.stock.find({"desc":"mp3"}).limit(50).skip(50).sort("price":-1)
注意,跳过过多会导致性能问题。
注意,不同类型的优先级:
默认如下:
1 最小值
2 null
3 数字
4 字符串
5 对象/文档
6 数组
7 二进制数据
8 对象ID
9 布尔型
10 日期
11 时间戳
12 正则表达式
13 最大值
索引
创建索引
> db.users.ensureIndex({"name":1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
如上,给username字段创建索引。
db.currentOp() 可以查看状态。
因为创建索引是有代价的,在插入更新删除的时候都要更耗费时间,还要更新集合所有的索引。所以Mongodb限制每个集合最多只能有64个索引。
通常在一个特定的集合上,不应该拥有2个以上的索引。
创建复合索引
db.users.ensureIndex({"age":1,"username":1})
复合索引在查询中有多个键,或者查询中有多个排序方向作用比较大。
索引嵌套文档
{
"username":"sid",
"loc":{
"ip":"1.2.3.4",
"city":"Springfield",
"state":"NY"
}
}
db.users.ensureIndex({"loc.city":1})
如上,给city字段建立索引,提高这个字段的查询速度。
注意:给内嵌文档创建索引和给内嵌文档的字段创建索引是不同的,除非查找整个内嵌文档,不然查找内嵌文档的某个字段的时候,内嵌文档的索引是没有作用的,需要建立字段索引。
数组索引
给数组创建索引相当于给数组中所有的元素创建索引,这样对数组的更改会比较耗时。
唯一索引
db.users.ensureIndex({"username":1},{"unique":true})
如上,给username设置了唯一索引,如果你插入两个同名的人就会报错。
_id也是唯一索引,区别是_id不能被删除,而其他唯一索引可以被删除。
强制唯一索引
在创建唯一索引的时候有时候会失败,因为已有的数据可能会有重复,但由于数据过多又不知道哪些数据重复,这个时候可以使用
dropDups
db.people.ensureIndex({"username":1},{"unique":true,"dropDups":true})
dropDups会强制删除重复的文档。慎用
查询计划
如果被查询的字段有多个索引,mongodb会从这个字段的索引子集中为每次查询计划选择一个,这些查询计划是并行执行的,最早返回100个结果的就会保留,其他的计划被终止。
查询计划会被缓存,这个查询以后会使用这条计划直到集合数据发生了比较大的变动。建立索引时或每执行1000次查询之后,查询优化器都会重新评估查询计划。
何时不应该使用索引
当数据比较少的时候,不使用索引反而比有索引快,因为使用索引需要先去索引表查找,再根据指针去数据表查找,需要找两次。
所有的索引都保存在system.index集合中,只能通过ensureIndex或者dropIndexes对其进行操作。
可以通过db.tablename.getIndexes()来查看这个表所有索引信息。
删除索引
db.people.dropIndex("x_1_y_1")
用索引描述信息里name字段的值来指定需要删除的索引。
数据类型
null
{"x":null}
布尔型
{"x":true}
数值
默认64位浮点数
{"x":3.14}
{"x":3}
整型
NumberInt表示4字节带符号整数
NumberLong表示8字符带符号整数
{"x":NumberInt("3")}
{"x":NumberLong("3")}
字符串
UTF-8字符串
{"x":"foobar"}
日期
毫秒数,不存储时区
{"x":new Date()}
正则表达式
使用正则表达式作为限定条件
{"x":/foorbar/i}
数组
{"x":["a","b","c"]}
内嵌文档
{"x":{"foo":"bar"}}
对象id
{"x":ObjectId()}
代码
{"x":function() {/*...*/}}
插入校验
所有文档都必须小于16MB。
批量插入脚本
for (var i=0 ;i<100; i++) {db.tester.insert({"foo":"bar","baz":i,"z":10-i})}
固定集合
固定集合相当于一个队列,如果固定集合已经满了,如果再向固定集合中添加内容,那么最老的文档会被删除。
固定集合不能被分片。
固定集合可以用于记录日志。
固定集合必须在使用之前显式创建。
db.createCollection("my_collection",{"capped":true,"size":100000})
如上方式创建固定集合,创建的固定集合叫my_collection,大小为100000字节,除了大小,固定集合还可以指定固定集合中文档的数量。
> db.createCollection("my_collection2",{"capped":true,"size":100000,"max":100});
{ "ok" : 1 }
固定集合创建之后就不能改变了。如果需要修改只能删除之后再重建。
创建固定集合的另一种方式,可以将常规集合转为固定集合。
db.runCommand({"convertToCapped":"test","size":10000})
test为集合名字。
无法将固定集合转为常规集合。只能删除重建。
自然排序
对于固定集合来说,自然排序就是从旧到新排序,也可以按照从新到旧排序。是按照文档的插入顺序排列的。
db.my_collection.find().sort({"$natural":-1})
创建没有_id索引的集合
如果在调用createCollection创建集合时指定autoIndexId选项为false,创建的集合就不会自动在_id上创建索引
一般不这么用,但是如果对只有插入操作的集合来说,效率会提升一些。
TTL索引
(time-to-live index)具有生命周期的索引。
这种索引允许为每一个文档设置一个超时时间,一个文档到达预期设置的超时时间后会被删除。这种类型的索引对于缓存问题非常有帮助。
db.foo.ensureIndex({"lastUpdated":1},{"expireAfterSecs":60*60*24})
如上,给lastUpdated字段建立了TTL索引,当服务器比对发现文件lastUpdated字段的时间晚expireAfterSecs秒时,文档就会被删除。
mongodb每分钟会对TTL索引进行一次清理。
聚合框架
用于对一连串的文档进行处理
包括:
管道(pipeline)
筛选(filtering)
投射(projecting)
分组(grouping)
排序(sorting)
限制(limiting)
跳过(skipping)
例如
找到发表文章最多的前五个作者
db.articles.aggregate({"$project":{"author":1}},{"$group":{"_id":"$author","count":{"$sum":1}}},{"$sort":{"count":-1}},{"$limit":5})
1 将每个文章中的作者投射出来
2 将作者按照名字排序,统计每个名字出现的次数
指定需要分组的字段author,这个操作完成后,每个作者只对应一个文档结果
3 将作者按照名字出现次数降序排列
4 将返回结果限制为前五个
aggregate()会返回一个文档数组。
$match
用于对文档集合进行筛选,筛选之后可以再对文档子集做聚合。
$match可以使用所有常规的查询操作符($gt,$lt$in等)
通常尽可能将$match放在管道的前面位置,好处1是可以快速将不需要的文档过滤掉,2是在投射分组之前执行match可以使用索引。
$project
使用$project可以从子文档中提取字段,可以重命名字段等等。
db.articles.aggregate({"$project":{"author":1,"_id":0}})
如上,可以只返回author字段内容,却不返回_id
将投射过的字段重命名
db.users.aggregate({"$project":{"userId":"$_id","_id":0}})
如上,将_id重命名为userId
注意需要将_id:0,不然这个字段会返回,也就是相当于返回两次,一个被重命名为userId
数学表达式
可以对数值做操作。
db.employees.aggregate({"$project":{"totalPay":{"$add":["$salary","$bonus"]}}})
如上,是将salary和bonus字段相加。
db.employees.aggregate({"$project":{"totalPay":{"$subtract":[{"$add":["$salary","$bonus"]},"$401k"]}}})
如上,是用salary+bonus-401k
操作符语法
"$add":[expr1,expr2]
这个操作符接收一个或多个表达式作为参数,将这些表达式相加。
"$subtract":[expr1,expr2]
接受两个参数,用第一个参数减去第二个参数作为结果。
"$multiply":[expr1,expr2...]
接收一个或多个表达式,将其相乘。
"$divide":[expr1,expr2]
接收两个表达式,用第一个表达式除以第二个表达式的商作为结果。
"$mod":[expr1,expr2]
接收两个表达式,将第一个表达式除以第二个表达式得到的余数作为结果。
日期表达式
$year
$month
$week
$dayOfMonth
$dayOfWeek
$dayOfYear
$hour
$minute
$second
只能对日期做操作,不能对数字做操作。
字符串表达式
"$substr":[expr,startOffset,numToReturn]
expr必须是字符串,startOffset字节开始到numToReturn字节。
"$concat":[expr1,expr2..]
将给定的字符串连接在一起作为结果返回。
"$toLower":expr
参数expr必须是个字符串,这个操作返回expr小写
"$toUpper":expr
参数expr必须是个字符串,这个操作返回expr大写
例如
db.employees.aggregate({"$project":{"email":{"$concat":[{"$substr":["$firstName",0,1]},".","$lastName","@example.com"]}}})
生成[email protected]格式的例子。
逻辑表达式
"$cmp":[expr1,expr2]
如果expr1=expr2,返回0,如果expr1<2返回一个负数,如果expr1>expr2,返回一个正数
"$strcasecmp":[string1,string2]
比较string1和string2,区分大小写,只对罗马字符组成的字符串有效。
"$eq"/"$ne"/"$gt"/"$gte"/"$lt"/"$lte"
"$and"
"$or" 或
"$not" 取反
控制语句
"$cond":[booleanExpr,trueExpr,falseExpr]
如果booleanExpr为true,返回trueExpr,否则返回falseExpr
"$ifNull":[expr,replacementExpr]
如果expr是null,返回replacementExpr,否则返回expr。
原文地址:https://www.cnblogs.com/ArmoredTitan/p/9309680.html