1. MongoDB中数据查询的方法
(1)find函数的使用;
(2)条件操作符;
(3)distinct找出给定键所有不同的值;
(4)group分组;
(5)游标;
(6)存储过程。
文档查找
1.数据准备,find函数()
var persons = [{
name:"jim",
age:25,
email:"75431457@qq.com",
c:89,m:96,e:87,
country:"USA",
books:["JS","C++","EXTJS","MONGODB"],
address:{city:"beijing",street:"861 Park Street"}
}......
数组中有12个文档(文档中有很多类型)
persons
show collections --查询集合中有没有student
db.student.drop() --先删除
db.student.insert(persons) --再添加
db.student.count()
2.查询所有的记录
db.student.find()
db.student.find({})
find()的语法介绍
db.collection.find(query, projection)
query :可选,使用查询操作符指定查询条件
projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。
3.投影操作,即是列的操作
db.student.find({},{_id:0}) 0代表是不显示 1代表显示
db.student.find({},{_id:0,name:1})
db.student.find({},{_id:0,name:1,age:1})
要么写想要的字段,要么写不想要的字段,最好不要混合写
db.student.find({},{name:1,age:1})
db.student.find({},{_id:0,name:0,age:0})
db.student.find({},{age:0,name:1}) --混合写出错
db.student.find({},{_id:1,name:0}) --混合写出错
- 条件操作符;
MongoDB中条件操作符有:
(>) 大于 - $gt
(<) 小于 - $lt
(>=) 大于等于 - $gte
(<= ) 小于等于 - $lte
查询出年龄在25到27岁之间的学生
db.student.find({age:{$gte:25,$lte:27}})
db.student.find({age:{$gte:25,$lte:27}},{_id:0,name:1,age:1})
II.查询出所有不是韩国籍的学生
db.student.find({country:{$ne:"Korea"}})
db.student.find({country:{$ne:"Korea"}},{_id:0,name:1,country:1})
(2)包含或不包含
$in或$nin
I.查询国籍是中国或美国的学生信息
db.student.find({country:{$in:["USA","China"]}},{_id:0,name:1,country:1})
II.查询国籍不是中国或美国的学生信息
db.student.find({country:{$nin:["USA","China"]}},{_id:0,name:1,country:1})
5.逻辑运算符
$and
I.查询语文成绩大于85并且英语大于90的学生信息
db.student.find({$and:[{c:{$gte:85}},{e:{$gte:90}}]})
$or
II.查询语文成绩大于85或者英语大于90的学生信息
db.student.find({$or:[{c:{$gte:85}},{e:{$gte:90}}]})
$not的使用
III.查询出名字中不存在”li”的学生的信息
db.student.find({name:{$not:/li/i}},{_id:0,name:1})
可先查询名字的值为li并不区分大小写
db.student.find({name:/li/i})
6.元素查询操作符
$exists
I.查询地址存在的学生信息
db.student.find({address:{$exists:true}})
db.student.find({address:{$exists:false}})无地址
II.查询年龄为空值的学生信息
db.student.find({age:null})或
db.student.find({age:{$type:10}})
7.内嵌文档查询
db.student.find({address:{city:"beijing",street:"861 Park Street"}})
db.student.find({"address.city":"beijing"})
8.数组查询
db.student.find({books:["JS","C#","EXTJS","MONGODB"]})
db.student.find({books:"JS"}) 只要books中有JS
db.student.find({"books.3":"PHP"}) books第三个元素的值为PHP
db.student.find({books:{$all:["PHP","JS"]}}) 只要books中有PHP和JS
db.student.find({},{books:{$slice:[1,2]}}) --跳过数组里面的第一个元素,返回两个元素
- distinct找出给定键所有不同的值,并以数组形式返回
语法:
db.collection_name.distinct(field,query,options)
field -----指定要返回的字段(string)
query-----条件查询(document)
options-----其他的选项(document)
options
{ collation: <document> }
collation: {
locale: <string>,
caseLevel: <boolean>,
caseFirst: <string>,
strength: <int>,
numericOrdering: <boolean>,
alternate: <string>,
maxVariable: <string>,
backwards: <boolean>
}
示例集合与文档:
> db.inventory.distinct(“dept”) //获取dept字段的不重复值
> db.inventory.distinct(“item.sku”) //获取item子字段sku的不重复值
>db.inventory.distinct(“sizes”) //获取数组格式字段的不重复值
>db.inventory.distinct(“item.sku”,{dept:”A”}) //满足dept为A数据的item字段的子字段的不重复值
4 group分组;
MongoDB中聚合(aggregate)主要用于处理数据(诸如统计平均值,求和等),并返回计算后的数据结果。有点类似sql语句中的 count(*)
语法:
>db.COLLECTION_NAME.aggregate(AGGREGATE_OPERATION)
数据准备:
var info = [{
title: 'MongoDB Overview',
description: 'MongoDB is no sql database',
by_user: 'runoob.com',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 100
},
{
title: 'NoSQL Overview',
description: 'No sql database is very fast',
by_user: 'runoob.com',
url: 'http://www.runoob.com',
tags: ['mongodb', 'database', 'NoSQL'],
likes: 10
},
{
title: 'Neo4j Overview',
description: 'Neo4j is no sql database',
by_user: 'Neo4j',
url: 'http://www.neo4j.com',
tags: ['neo4j', 'database', 'NoSQL'],
likes: 750
}]
一些聚合表达式:
表达式 |
描述 |
实例 |
$sum |
计算总和。 |
db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$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"}}}]) |
管道的概念
管道在Unix和Linux中一般用于将当前命令的输出结果作为下一个命令的参数。
MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。
表达式:处理输入文档并输出。表达式是无状态的,只能用于计算当前聚合管道的文档,不能处理其它的文档。
这里我们介绍一下聚合框架中常用的几个操作:
- $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
- $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
- $limit:用来限制MongoDB聚合管道返回的文档数。
- $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
- $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
- $group:将集合中的文档分组,可用于统计结果。
- $sort:将输入文档排序后输出。
- $geoNear:输出接近某一地理位置的有序文档。
管道操作符实例
1、$project实例
db.article.aggregate(
{ $project : {
title : 1 ,
author : 1 ,
}}
);
这样的话结果中就只还有_id,tilte和author三个字段了,默认情况下_id字段是被包含的,如果要想不包含_id话可以这样:
db.article.aggregate(
{ $project : {
_id : 0 ,
title : 1 ,
author : 1
}});
2.$match实例
db.articles.aggregate( [
{ $match : { score : { $gt : 70, $lte : 90 } } },
{ $group: { _id: null, count: { $sum: 1 } } }
] );
$match用于获取分数大于70小于或等于90记录,然后将符合条件的记录送到下一阶段$group管道操作符进行处理。
3.$skip实例
db.article.aggregate(
{ $skip : 5 });
经过$skip管道操作符处理后,前五个文档被"过滤"掉。
5游标;
游标查询
var cursor = db.student.find({})
cursor.count()
cursor.hasNext()
cursor.next()
cursor.toArray()
循环遍历
while(cursor.hasNext()){
printjson(cursor.next())
}
10.模糊查询
使用正则表达式
db.student.find({name:{$regex:/l/}},{_id:0,name:1})
db.student.find({name:{$regex:/l/i}},{_id:0,name:1})
db.student.find({name:{$regex:/^l/i}},{_id:0,name:1})
db.student.find({name:/^l/i},{_id:0,name:1})
11.查询结果排序
db.student.find({},{_id:0,name:1,age:1}).sort({age:1}) age升序
db.student.find({},{_id:0,name:1,age:1}).sort({age:-1}) age降序
db.student.find({},{_id:0,name:1,age:1}).sort({name:-1,age:1})name降序age升序
12.limit 返回结果集中的前几个
db.student.find({},{_id:0,name:1,age:1}) 查所有的
a.使用第三个参数
db.student.find({},{_id:0,name:1,age:1},2) 只显示前两行
b.使用limit()函数
db.student.find({},{_id:0,name:1,age:1}).limit(2) 只显示前两行
13.skip 跳过一定数量
a.使用第四个参数
db.student.find({},{_id:0,name:1,age:1},2,3) 跳过前三行,显示前两行
b.使用skip()函数
db.student.find({},{_id:0,name:1,age:1},2).skip(3) 跳过前三行,显示前两行
*利用limit()和skip()实现分页技术
db.student.find({},{_id:0,name:1,age:1})
第一页 db.student.find({},{_id:0,name:1,age:1}).limit(3).skip(0)
第二页 db.student.find({},{_id:0,name:1,age:1}).limit(3).skip(3)
第三页 db.student.find({},{_id:0,name:1,age:1}).limit(3).skip(6)
第四页 db.student.find({},{_id:0,name:1,age:1}).limit(3).skip(9)
每页三行
6存储过程。
MongoDB支持存储过程的使用,它的存储过程是用javascript实现的,被存在于system.js表中,可以接收和输出参数,返回执行存储过程的状态值,也可以嵌套调用。
所以我理解的MongoDB的存储过程就是:
把javascript变量,存储到MongoDB的数据库的特殊集合:system.js表中,然后这些变量可以在何MongoDB的javascript上下文中调用,包括"$where"子句,db.eval调用,MapReduce作业。
2. MongoDB中索引及其创建;
(1)基础索引、文档索引;
(3)组合索引;
(4)唯一索引;
(5)强制使用索引;
(6)扩展索引;
(7)删除索引;
(8)explain执行计划。
一、索引
为什么使用索引?
索引(Index)是帮助数据库高效获取数据的数据结构。
建立索引的目的:用来加快查询速度,提高查询效率。
例:db.person.insert({name:"jack",age:19})
db.person.insert({name:"rose",age:20})
db.person.insert({name:"jack",age:18})
db.person.insert({name:"tony",age:21})
db.person.insert({name:"adam",age:18})
db.person.find()查询person集合下的所有文档
当往某各个集合插入多个文档后,每个文档在经过底层的存储引擎持久化后,会有一个位置信息,通过这个位置信息,就能从存储引擎里读出该文档。比如上面的例子里,person集合里包含插入了5个文档,假设其存储后位置信息如下(为方便描述,文档省去_id字段)
位置信息 文档
pos1 {“name” : “jack”, “age” : 19 }
pos2 {“name” : “rose”, “age” : 20 }
pos3 {“name” : “jack”, “age” : 18 }
pos4 {“name” : “tony”, “age” : 21}
pos5 {“name” : “adam”, “age” : 18}
假设现在有个查询 db.person.find({age: 18}), 查询所有年龄为18岁的人,这时需要遍历所有的文档(『全表扫描』),根据位置信息读出文档,对比age字段是否为18。当然如果只有4个文档,全表扫描的开销并不大,但如果集合文档数量到百万、甚至千万上亿的时候,对集合进行全表扫描开销是非常大的,一个查询耗费数十秒甚至几分钟都有可能。
如果想加速 db.person.find({age: 18}),就可以考虑对person表的age字段建立索引。方法为:db.person.createIndex({age: 1}) // 按age字段创建升序索引
建立索引后,MongoDB会额外存储一份按age字段升序排序的索引数据,索引结构类似如下,索引通常采用类似btree的结构持久化存储,以保证从索引里快速(O(logN)的时间复杂度)找出某个age值对应的位置信息,然后根据位置信息就能读取出对应的文档。
AGE 位置信息
18 pos3
18 pos5
19 pos1
20 pos2
21 pos4
简单的说,索引就是将文档按照某个(或某些)字段顺序组织起来,以便能根据该字段高效的查询。有了索引,至少能优化如下场景的效率:
(1)查询,比如查询年龄为18的所有人
(2)更新/删除,将年龄为18的所有人的信息更新或删除,因为更新或删除时,需要根据条件先查询出所有符合条件的文档,所以本质上还是在优化查询
(3)排序,将所有人的信息按年龄排序,如果没有索引,需要全表扫描文档,然后再对扫描的结果进行排序
众所周知,MongoDB默认会为插入的文档生成_id字段(如果应用本身没有指定该字段),_id是文档唯一的标识,为了保证能根据文档id快递查询文档,MongoDB默认会为集合创建_id字段的索引。
db.person.getIndexes() // 查询集合的索引信息
[
{
"v" : 2, // 索引版本
"key" : { // 索引的字段及排序方向
"_id" : 1 // 根据_id字段升序索引
},
"name" : "_id_" // 索引的名称
"ns" : "test.person", // 集合名
}
]
索引操作:
1.查看索引
语法:db.collection.getIndexes()
例:db.person.getIndexes()
2.创建索引
从3.0版本后使用 db.collection.createIndex()代替db.collection.ensureIndex()
语法:db.collection.createIndex(key, option)
参数说明:
(1) key: {字段名:1或-1}: 1标识索引升序,-1降序
(2) option: 设置索引选项,如设置名称、设置成为唯一索引
例:db.person.createIndex({age: 1}) // 按age字段创建升序索引
执行成功后可以看到返回的numIndexesAfter比numIndexesBefore大
db.person.getIndexes()
例:db.person.createIndex({age: -1},{name:"AgeIndex"}) // 按age字段创建降序索引,且索引的名字为AgeIndex
db.person.getIndexes()
3.删除索引
db.person.getIndexes()
(1)删除指定索引
db.person.dropIndex("age_1") 传索引的名字
db.person.dropIndex({age:-1}) 传文档的参数
(2)删除所有索引
db.person.dropIndexes()
注:不能删除_id索引,即自动创建索引不能删除。
4.修改索引
Mongodb没有单独的修改索引的方法,如果需要修改某个索引,需要先删除旧有的索引,再创建新的索引
5.重建索引
如果您需要重建集合中的索引,您可以使用 db.person.reIndex() ,一条操作就可以重建这个集合上的所有索引。它会删除所有索引,包括 _id 索引 ,然后重建所有索引。
6.查询索引大小
db.person.totalIndexSize() --单位:字节
7.索引类别
a.默认索引
MongoDB每个collection都会有一个默认主键_id,不能删除
db.student.dropIndex("_id_")
db.student.dropIndex({_id:1})
b.单列索引
如2.所述
c.组合索引
db.person.createIndex({name:1,age:-1}) --两个字段组合或多个字段组合
d.内嵌文档索引
db.student.createIndex({address:1}) --在address整个列上创建索引
db.student.createIndex({"address.city":1}) --在address列的city上创建索引
e.唯一索引
db.student.dropIndex({name:1})
db.student.createIndex({name:1},{unique:true}) --创建唯一索引
db.student.find({},{_id:0,name:1})
第一种情况:插入重复项,则报错(唯一索引建立后不允许插入重复值)
db.student.insert({name:"lisi"})
db.student.insert({name:"a"})不报错
第二种情况:插入的文档中不包含name属性,则name的值默认为null,反复插入也会报错
db.student.insert({school:"nyist"})
db.student.insert({school:"nyist"})报错
db.student.insert({university:"nyist"})报错
第三种情况:如果在创建唯一索引时已经存在了重复项,先消除重复文档,保留一个文档后,再创建
db.student.createIndex({age:1},{unique:true})
8.explain执行计划
db.student.find().explain()
可加参数查看 "executionStats"
例:db.student.find().explain("executionStats")
说明:
explain会返回查询使用的索引情况,耗时等统计信息。
其中:
exectutionTimemillis: 耗时(毫秒)
indexBounds:当前查询具体使用的索引
9.强制索引
db.student.find().hint("_id_")
db.student.find().hint({_id:1})
db.student.find().hint("_id_").explain("executionStats") --再查看执行计划
10.举个使用索引查询案例,看效果
a.插入10W条数据
for(var i=0;i<100000;i++){
db.person.insert({name:"zs"+i,age:i})
}
b.查询name为zs10000的数据(不建立索引)
db.person.find({name:"zs"+10000})
db.person.find({name:"zs"+10000}).explain("executionStats") --看效果,此时没建name索引
c.建立索引
db.person.getIndexes()
db.person.createIndex({name:1})
d.查询name为zs10000的数据
db.person.find({name:"zs"+10000}).explain("executionStats")
db.person.find({name:"zs"+10000}).hint({name:1}).explain("executionStats") --看效果,此时已建name索引
索引的基本操作
数据准备:
先插入100w条记录
for(var i=0;i<1000000;i++){
db.orders.insert({
"onumber":i,
"date":"2015-07-02",
"cname":"zcy"+i,
"items":[{
"ino":i,
"quantity":i,
"price":4.0
},{
"ino":i+1,
"quantity":i+1,
"price":6.0
}
]
})
};
等待几分钟
db.orders.count()总共1000000条记录。
1. 默认索引
存储在MongoDB集合中的每个文档(document)都有一个默认的主键“_id“,如果在添加新的文档时,没有指定“_id“值时,MongoDB会创建一个ObjectId值,并创建会自动创建一个索引在“_id“键上,默认索引的名称是”_id_“,并无法删除,如下面的命令查看:
>db.orders.getIndexes()
2. 查看索引信息
返回一个数组,该数组保存标识和描述集合上现有索引的文档列表,可以查看是否有对某个集合创建索引,并创建哪些索引,方便管理。
语法:
>db.collection.getIndexes()
3. 创建单列索引
对文档单个字段创建索引或者对内嵌文档的单个字段创建索引
语法:
db.collection.createIndex({field:boolean} })
boolean:对于某个领域的上升索引,指定一个值为1;对于下降的索引,指定一个值为1。
(1)创建
>db.orders.createIndex({cname:1})
>db.orders.getIndexes()
对orders集合创建了cname索引,默认的索引名称是”cname_1“
(2)根据条件查询文档,并查看查询效率怎么样
> db.orders.find({"cname":"zcy100000"})
查询orders 集合根据条件cname为zcy100000的文档
我们测试有建索引和没建索引在1000000条文档执行查询的效率怎么样,这边先使用explain()函数,
可加参数查看 "executionStats"
例:db.student.find().explain("executionStats")
说明:
explain会返回查询使用的索引情况,耗时等统计信息。
其中:
exectutionTimemillis: 耗时
indexBounds:当前查询具体使用的索引
> db.orders.find({"cname":"zcy100000"}).explain("executionStats")
1) 没建索引时,查询条件cname为zcy100000的文档
可把之前建立的索引删除:db.orders.dropIndex(“cname_1”)
db.orders.find({"cname":"zcy100000"}).explain("executionStats")
查询可得”exectutionTimemillis”的值为:551毫秒,没使用到索引
2) 有建索引,查询条件cname为zcy100000的文档
db.orders.createIndex({cname:1})
db.orders.getIndexes()
db.orders.find({"cname":"zcy100000"}).explain("executionStats")
返回一个记录,花费时间很少,有使用到cname索引
我们结果是相差很大的,有建索引字段,查询效率比较高,在大数据时,差别更明显。
(3)查询和排序组合使用
我们查询集合cname大于zcy100的文档并对onumber进行降序排序
db.orders.find({"cname":{$gt:"zcy1000"}}).sort({"onumber":1}).explain("executionStats")
执行出现错误:
"errorMessage" : "Runner error:Overflow sort stage buffered data usage of 33554456 bytes exceeds internal limit of 33554432 bytes",
我们的内存只有33554432字节,对于包括一个没有索引的排序操作的查询,服务器必须在返回任何结果之前将所有的文档加载到内存中来进行排序。
可对onumber创建索引
> db.orders.createIndex({onumber:-1})
> db.orders.getIndexes()
> db.orders.find({"cname":{$gt:"zcy1000"}}).sort({"onumber":1}).explain("executionStats")
这次在执行时,可以正常执行,已经减少了内存的缓存的数据
4. 创建组合索引
我们可以同时对多个键创建组合索引
语法:
db.collection.createIndex({field1:boolean, field2:boolean } })
说明:
db.collection.createIndex({a:1,b:1,c:1 } })
(1) 创建组合索引
我们同时对onumber和cname进行组合索引
例子:
>db.orders.createIndex({cname:1,onumber:-1})
>db.orders.getIndexes()
索引存储在一个易于遍历读取的数据集合中,存储的数据
{_id:..,"onumber" : 2, "date" : "2015-07-02", "cname" : "zcy1"})
{_id:..,"onumber" : 1, "date" : "2015-07-02", "cname" : "zcy1"})
{_id:..,"onumber" : 1, "date" : "2015-07-02", "cname" : "zcy2"})
(2) 查询
1)我们对cname和onumber作为查询条件时
>db.orders.find({"cname":{$gt:"zcy1000"},"onumber":2000}).explain("executionStats")
我们查询条件cname大于zcy1000并且onumber等于2000的数据,我们用explain()查询索引使用情况
2)我们只用两个索引其中一个作为查询时
第一种情况:我们条件只使用"cname":{$gt:"zcy1000"}作为查询条件
>db.orders.find({"cname":{$gt:"zcy1000"}}).explain("executionStats")
会使用到索引,符合我们前面介绍的我们对a、b、c进组合创建索引,支持查询时会用到索引的第一种。
第二种情况:我们条件只使用"onumber":2000作为查询条件
> db.orders.find({"onumber":2000}).explain("executionStats")
会使用到索引,但不符合我们前面介绍的我们对a、b、c进组合创建索引,支持查询时会用到几种。
(3)查询和排序组合使用
我们查询集合cname大于zcy100的文档并对onumber进行降序排序
>db.orders.find({"cname":{$gt:"zcy1000"}}).sort({"onumber":1}).explain("executionStats")
5. 内嵌文档的索引
我们对内嵌文档创建索引时,跟基本文档创建索引一样
语法:
db.collection.createIndex({field:boolean} })
field说明:以“.“来指明内嵌文档的路径
(1) 单列的内嵌文档的索引创建
>db.orders.createIndex({"items.info":1})
>db.orders.getIndexes()
我们orders集合下的内嵌items集合的info字段创建索引
我们以items.info字段作为查询条件,并使用索引的情况
>db.orders.find({"items.info":{$lt:100}}).explain("executionStats")
我们查询items.info小于100的数据
(2) 组合的内嵌文档的索引的创建
我们对内嵌文档创建组合索引时,跟基本文档创建组合索引一样
>db.collection.createIndex({field1:boolean, field2:boolean } })
>db.orders.createIndex({"items.info":1, "items. quantity":-1})
6. 删除索引
我们对已经创建的索引进行删除,可以针对具体的集合中索引进行删除,也可以对所有的集合中的所有索引删除
(1)具体索引名称删除索引
db.collection.dropIndex(index)
删除具体的索引,根据索引名称删除,如果不知道索引的名称,可以通过db.collection.getIndexes()查看索引名称
> db.orders.dropIndex("cname_1")
我们删除cname字段的索引,现在只剩下onumber字段索引
(2)删除集合中所有索引
db.collection.dropIndexes()
> db.orders.dropIndexes()
我们对集合中的索引都删除,我们删除cname字段的索引和onumber字段索引,现在只剩默认的_id字段索引,索引我们在使用时,要谨慎,以免把集合中的索引都删除。
(3)对dropIndexes方法,我们还有一种用法,可以指定集合的具体索引的删除
> db.runCommand({"dropIndexes":"orders","index":"cname_1"})