对结果集的操作
在RDBMS中获得的结果是一张虚拟的表,可以在其上继续操作。MongoDB中查询到的结果是一个文档集合,也可以在其上做一些简单操作。
limit()方法
相当于SQL中的TOP子句,会取结果集中的前指定条文档,使用
结果集.limit(数字n)
能获取结果集中的前n条文档。
例
> db.lzhCllctn1.find()
{ "_id" : ObjectId("5b29dedaf5b9d062d3a61e61"), "a" : 1 }
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
> db.lzhCllctn1.find().limit(3)
{ "_id" : ObjectId("5b29dedaf5b9d062d3a61e61"), "a" : 1 }
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
>
skip()方法
使用
结果集.skip(数字n)
能跳过结果集中的前n条文档,获取其后的全部文档。
例
> db.lzhCllctn1.find()
{ "_id" : ObjectId("5b29dedaf5b9d062d3a61e61"), "a" : 1 }
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
> db.lzhCllctn1.find().skip(3)
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
>
看来skip()和limit()相配合,就能一块一块的访问结果集中的文档了,如完成网页商品的分页效果大概可以用这个。
limit()和skip()的优先性
不能想当然地认为这两个方法的调用顺序会影响结果,不会。limit()
和skip()
一起使用时,总是先skip()
再limit()
。
例
> db.lzhCllctn1.find()
{ "_id" : ObjectId("5b29dedaf5b9d062d3a61e61"), "a" : 1 }
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
> db.lzhCllctn1.find().limit(3).skip(1)
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
> db.lzhCllctn1.find().skip(1).limit(3)
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
>
sort()方法
sort可以指定排序的字段,用1表示升序,-1表示降序,即
结果集.sort({排序key:1或-1})
例
> db.lzhCllctn1.find()
{ "_id" : ObjectId("5b29dedaf5b9d062d3a61e61"), "a" : 1 }
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
> db.lzhCllctn1.find().sort({a:1})
{ "_id" : ObjectId("5b29dedaf5b9d062d3a61e61"), "a" : 1 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
> db.lzhCllctn1.find().sort({a:-1})
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
{ "_id" : ObjectId("5b29dedaf5b9d062d3a61e61"), "a" : 1 }
>
可以看到排序比较上相同的文档总是按_id
从小到大进行排序。
limit()和skip()和sort()的优先性
总是先sort()
,再skip()
,最后limit()
,不论它们的书写顺序如何。
例
> db.lzhCllctn1.find()
{ "_id" : ObjectId("5b29dedaf5b9d062d3a61e61"), "a" : 1 }
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dedef5b9d062d3a61e63"), "a" : 4 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
> db.lzhCllctn1.find().limit(3).sort({a:-1}).skip(1)
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
> db.lzhCllctn1.find().sort({a:-1}).skip(1).limit(3)
{ "_id" : ObjectId("5b29dedcf5b9d062d3a61e62"), "a" : 3 }
{ "_id" : ObjectId("5b29dee1f5b9d062d3a61e64"), "a" : 3 }
{ "_id" : ObjectId("5b29dee8f5b9d062d3a61e65"), "a" : 2 }
>
索引
为什么使用索引
不使用索引时,查询需要做代价比较高的顺序扫描。MongoDB中的索引可以建立一种按某些字段的某种排序规则进行排序的数据结构,如果这种排序能让业务内常用的查询变快(如博客/新闻网站时常需要按时间降序排序),那么只要花费一次建立索引的时间(这个时间可能比较长),就能带来MongoDB数据库在该业务下的性能提升。
建立索引
在要建立索引的DB下,使用
db.集合名.createIndex(
{
排序key1:升1降-1,
排序key2:升1降-1,
...
},
{
background:是否将建立索引操作放入后台,
unique:建立的索引是否唯一,
name:显式指明索引名称,
v:索引的版本号,
weights:该索引相对于其它索引的重要权重,
default_language:文本索引停用词规则,
language_override:文本索引包含在文档中的字段名,
expireAfterSeconds:集合的生存时间
}
)
来建立一个索引,其中第二个BSON体及其中的参数是可选的。当在第一个BSON体内使用多个排序规则时,相当于RDBMS中的复合索引。
特别注意,runoob和其它教程中提及的
dropDups:是否删除重复记录以建立唯一索引
这一选项从MongoDB2.7.5开始就已经废弃了,大多教程都没有更新,甚至在使用老旧的ensureIndex()
,目前它就是指向createIndex()
。
例
> db.lzhCllctn1.createIndex({a:1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
>
查看索引
在要查看索引的DB下,使用
db.集合名.getIndexes()
查看该集合的所有索引。
使用
db.集合名.totalIndexSize()
查看集合中所有索引的总大小。
例
> db.lzhCllctn1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "lzh.lzhCllctn1"
},
{
"v" : 2,
"key" : {
"a" : 1
},
"name" : "a_1",
"ns" : "lzh.lzhCllctn1"
}
]
> db.lzhCllctn1.totalIndexSize()
53248
>
可以看到刚刚为a字段建立的索引,而主键_id
的索引是MongoDB自动建立的。
重建索引
MongoDB和热门的RDBMS一样是用B-tree做索引。索引需要重建是因为对数据库的某些操作可能破坏索引或产生索引碎片,让索引带来的性能下降。具体的重建时机是比较高级的部分,涉及对索引当前情况的分析,以后再细学。
在要重建索引的DB下,使用
db.集合名.reIndex()
为该集合重建索引。
例
> db.lzhCllctn1.reIndex()
{
"nIndexesWas" : 2,
"nIndexes" : 2,
"indexes" : [
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "lzh.lzhCllctn1"
},
{
"v" : 2,
"key" : {
"a" : 1
},
"name" : "a_1",
"ns" : "lzh.lzhCllctn1"
}
],
"ok" : 1
}
>
删除索引
从前面可以看到,索引都是有唯一的name
字段的,可以在建立索引时显式指定,也可以由MongoDB来指定。
在要删除索引的DB下,使用
db.集合名.dropIndex('要删除的索引的name')
来删除指定name的索引。
使用
db.集合名.dropIndexes()
删除集合中的所有索引。
例
> db.lzhCllctn1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "lzh.lzhCllctn1"
},
{
"v" : 2,
"key" : {
"a" : 1
},
"name" : "a_1",
"ns" : "lzh.lzhCllctn1"
}
]
> db.lzhCllctn1.dropIndex('a_1')
{ "nIndexesWas" : 2, "ok" : 1 }
> db.lzhCllctn1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "lzh.lzhCllctn1"
}
]
> db.lzhCllctn1.dropIndexes()
{
"nIndexesWas" : 1,
"msg" : "non-_id indexes dropped for collection",
"ok" : 1
}
> db.lzhCllctn1.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "lzh.lzhCllctn1"
}
]
>
可以看到,MongoDB为主键_id
建立的默认索引不能被删除。