目录

【查询表达式】

比较运算

$ne 不等于

$gt 大于 , $gte 大于等于

$lt 小于 , $lte 小于等于

$in 在...之内 , $nin 不在...之内

$all 所有单元匹配

逻辑运算

用 $and 实现

用 $nin 实现,稍微简单一点

用 $nor 实现

元素运算符

{field:{$exists:1}}

{field:{$type:2}}

JS运算符

用$where表达式来查询。

查询表达式总结

【索引】

索引的常用命令

创建普通的单列索引

查看当前索引状态

删除单个索引

删除所有索引  

创建多列索引 

创建子文档索引

按照索引性质创建索引

创建唯一索引

创建稀疏索引

创建哈希索引(2.4新增)

重建索引

【游标操作】

【查询表达式】

比较运算

最简单的查询表达式 {filed:value} ,是指查询field列的值为value的文档。

首先导入测试数据:https://github.com/rxbook/mongodb_test/blob/master/test1.json

例:查询主键为32的商品:

db.goods.find({goods_id:32});
MySQL中:select * from goods where goods_id=32

MongoDB Compass比对索引 mongodb索引查询_笔记

$ne 不等于

用法:{field:{$ne:value}} ,查找filed列的值 不等于 value 的文档

例2:cat_id不等于3的相关记录

db.goods.find({cat_id:{$ne:3}},{goods_id:1,cat_id:1,goods_name:1,_id:0});
MySQL中: select field from goods where cat_id!=3

MongoDB Compass比对索引 mongodb索引查询_mongodb_02

$gt 大于 , $gte 大于等于

用法:{field:{$gt:value}} ,查找filed列的值 大于 value 的文档

例3: shop_price大于3的相关记录

db.goods.find({shop_price:{$gt:3}},{goods_id:1,goods_name:1,shop_price:1,_id:0});
MySQL中: select field from goods where shop_price>3

MongoDB Compass比对索引 mongodb索引查询_数据库_03

$lt 小于 , $lte 小于等于

例: shop_price小于或等于100元的商品($lte)

db.goods.find({shop_price:{$lte:100}},{goods_name:1,shop_price:1});
MySQL中:select field from goods where shop_price<=3000

MongoDB Compass比对索引 mongodb索引查询_笔记_04

$in 在...之内 , $nin 不在...之内

例:取出cat_id=4或cat_id=11的商品($in)  

db.goods.find({cat_id:{$in:[4,11]}},{cat_id:1,goods_name:1});
MySQL中: select field from goods where cat_id IN (4,11)

MongoDB Compass比对索引 mongodb索引查询_mongodb_05

$all 所有单元匹配

语法: {field:{$all:[v1,v2..]}} ,是指取出 field列是一个数组,且至少包含 v1,v2值。MySQL中没有此用法。

例:取出emp表中,hobby字段包含“A”和“B”的:

db.emp.find({hobby:{$all:[‘A’,’B’]}});

MongoDB Compass比对索引 mongodb索引查询_数据库_06

逻辑运算

例:取出价格介于100到500之间的商品($and)

db.goods.find({
    $and:[
        {shop_price:{$gt:100}},
        {shop_price:{$lt:500}}
    ]} , {
        goods_name:1,
        shop_price:1,
        _id:0
});

MySQL中:where price>=100 AND price<=500 或 where price BETWEEN 100 AND 500

分析:

{shop_price:{$gte:100}} -- A
{shop_price:{$lte:500}} -- B
{$and:[A,B]},{goods_name:1,shop_price:1,_id:0}

MongoDB Compass比对索引 mongodb索引查询_MongoDB Compass比对索引_07

例:取出(价格大于40且小于100),或者(大于1000且小于2000)的商品($or)

这样写效率比较高,但是有点复杂,可以通过$where的方式写,比较简单,同时效率较低。参照后面的$where部分。

db.goods.find({$or:[
    {$and:[{shop_price:{$gt:40}},{shop_price:{$lt:100}}]},
    {$and:[{shop_price:{$gt:1000}},{shop_price:{$lt:2000}}]}
]},{goods_name:1,shop_price:1,_id:0});

MySQL中: where (price>40 AND price<100) OR (price>1000 AND price<2000)

MongoDB Compass比对索引 mongodb索引查询_mongodb_08

例:取出cat_id不等于3,并且cat_id不等于11的商品($and 、 $nin 、 $nor分别实现)

用 $and 实现

db.goods.find({$and:[{cat_id:{$ne:3}},{cat_id:{$ne:11}}]},{goods_name:1,cat_id:1})

MongoDB Compass比对索引 mongodb索引查询_笔记_09

用 $nin 实现,稍微简单一点

db.goods.find({cat_id:{$nin:[3,11]}},{goods_name:1,cat_id:1});

MongoDB Compass比对索引 mongodb索引查询_数据库_10

 {$nor,[条件1,条件2]} —— (既不是...,也不是...),所有列举条件都不成功则为真,其实就是和$and互逆的运算符。

用 $nor 实现

db.goods.find({$nor:[{cat_id:3},{cat_id:11}]},{goods_name:1,cat_id:1});

MongoDB Compass比对索引 mongodb索引查询_mongodb_11

元素运算符

例: 取出{goods_id%5 == 1} , 即goods_id等于1,6,11,..这样的商品 [MySQL中没有此功能]

db.goods.find({goods_id:{$mod:[5,1]}},{goods_id:1,goods_name:1,_id:0});

MongoDB Compass比对索引 mongodb索引查询_mongodb_12

{field:{$exists:1}}

查询出含有field字段的文档,存在即可被查找出来。

例:取出有age属性的文档 [MySQL中没有此功能,因为MySQL每一条数据对应的列是固定的 ]

db.stu.find({age:{$exists:1}}); #含有age属性的文档将会被查出

MongoDB Compass比对索引 mongodb索引查询_nosql_13

{field:{$type:2}}

查询出含有field字段的类型为2的。

参考文档:$type — MongoDB Manual

MongoDB Compass比对索引 mongodb索引查询_MongoDB Compass比对索引_14

例:取出name字段是字符串类型的 [MySQL中没有此功能,因为MySQL每一列的类型都是固定的,例如varchar、int ]

db.foo.find({age:{$type:2}}); #name字段属于字符串类型的将会被查出

MongoDB Compass比对索引 mongodb索引查询_笔记_15

同样的,要查出数值类型的(Double),使用如下:

MongoDB Compass比对索引 mongodb索引查询_nosql_16

JS运算符

用$where表达式来查询。

$where效率较低,因为要把二进制转换成js引擎后再进行查询,所以一般情况下不建议这样查询。

例: db.goods.find({$where:'this.cat_id != 3 && this.cat_id != 11'});

db.goods.find({$where:'this.shop_price>5000'});

MongoDB Compass比对索引 mongodb索引查询_mongodb_17

例:取出(价格大于40且小于100),或者(大于1000且小于2000)的商品,上面用 $or 实现了,这里用$where 试一下。当然,简单了一些,效率也较低。

db.goods.find({
    $where: '(this.shop_price>40 && this.shop_price<100) || (this.shop_price>1000 && this.shop_price<2000)'
},{
    goods_name:1,
    shop_price:1,
    _id:0
});

MongoDB Compass比对索引 mongodb索引查询_数据库_18

注意: 用$where查询时, mongodb是把bson结构的二进制数据转换为json结构的对象, 然后比较对象的属性是否满足表达式,因此速度较慢!

用正则表达式查询 以”诺基亚”开头的商品,这样查询效率也比较低。

db.goods.find({goods_name:{$regex:/^诺基亚*/}},{goods_id:1,goods_name:1,_id:0});

MongoDB Compass比对索引 mongodb索引查询_nosql_19

查询表达式总结

1:主键为32的商品
db.goods.find({goods_id:32});

2:不属第3栏目的所有商品($ne)
db.goods.find({cat_id:{$ne:3}},{goods_id:1,cat_id:1,goods_name:1});

3:本店价格高于3000元的商品{$gt}
db.goods.find({shop_price:{$gt:3000}},{goods_name:1,shop_price:1});

4:本店价格低于或等于100元的商品($lte)
db.goods.find({shop_price:{$lte:100}},{goods_name:1,shop_price:1});

5:取出第4栏目或第11栏目的商品($in)
db.goods.find({cat_id:{$in:[4,11]}},{goods_name:1,shop_price:1});

6:取出100<=价格<=500的商品($and)
db.goods.find({$and:[{price:{$gt:100},{$price:{$lt:500}}}]);

7:取出不属于第3栏目且不属于第11栏目的商品($and $nin和$nor分别实现)
db.goods.find({$and:[{cat_id:{$ne:3}},{cat_id:{$ne:11}}]},{goods_name:1,cat_id:1})
db.goods.find({cat_id:{$nin:[3,11]}},{goods_name:1,cat_id:1});
db.goods.find({$nor:[{cat_id:3},{cat_id:11}]},{goods_name:1,cat_id:1});

8:取出价格大于100且小于300,或者大于4000且小于5000的商品()
db.goods.find({$or:[{$and:[{shop_price:{$gt:100}},{shop_price:{$lt:300}}]},{$and:[{shop_price:{$gt:4000}},{shop_price:{$lt:5000}}]}]},{goods_name:1,shop_price:1});

9:取出goods_id%5 == 1, 即,1,6,11,..这样的商品
db.goods.find({goods_id:{$mod:[5,1]}});

10:取出有age属性的文档
db.stu.find({age:{$exists:1}});
# 含有age属性的文档将会被查出

【索引】

准备工作:给stu文档中插入1000条数据:

MongoDB Compass比对索引 mongodb索引查询_数据库_20

查询sn:99的用户:

MongoDB Compass比对索引 mongodb索引查询_笔记_21

可以使用 db.find(query).explain(); 查看查询计划,类似于MySQL的explain解析。

db.stu.find({sn:99}).explain();

MongoDB Compass比对索引 mongodb索引查询_笔记_22

"cursor" : "BasicCursor", ----说明没有索引发挥作用
"nscannedObjects" : 1000 ----理论上要扫描多少行:1000行

关于索引的说明:

  • 索引提高查询速度,降低写入速度,权衡常用的查询字段,不必在太多列上建索引
  • 在mongodb中,索引可以按字段升序/降序来创建,便于排序
  • 默认是用btree来组织索引文件,2.4版本以后,也允许建立hash索引.

索引的常用命令

创建普通的单列索引

db.collection.ensureIndex({field:1/-1}); #1是升续,2是降续。

db.stu.ensureIndex({sn:1});

MongoDB Compass比对索引 mongodb索引查询_笔记_23

然后再explain查看就不一样了:

MongoDB Compass比对索引 mongodb索引查询_数据库_24

"cursor" : "BtreeCursor sn_1" ----用到的btree索引
"nscannedObjects" : 1 ----只需要扫描1行即可

查看当前索引状态

db.collection.getIndexes();

db.stu.getIndexes();

MongoDB Compass比对索引 mongodb索引查询_数据库_25

可以看到,_id是默认的必须有的索引!

删除单个索引

必须指定是1还是-1:db.collection.dropIndex({filed:1/-1});

db.stu.dropIndex({sn:1});

MongoDB Compass比对索引 mongodb索引查询_mongodb_26

删除所有索引  

db.collection.dropIndexes();

db.stu.dropIndexes();

MongoDB Compass比对索引 mongodb索引查询_nosql_27

删除所有索引之后,_id还在,说明 _id是必须存在的索引!

创建多列索引 

db.collection.ensureIndex({field1:1/-1, field2:1/-1});

db.stu.ensureIndex({sn:1,name:1});

MongoDB Compass比对索引 mongodb索引查询_MongoDB Compass比对索引_28

注意:两个列一起加索引 和 给两个列分别加索引是不一样的,多列索引是两个列绑定在一起作为索引的。

此例中,一般用于 sn 和 name 放在一起查询次数较多的情况下使用。

创建子文档索引

db.collection.ensureIndex({filed.subfield:1/-1});

① 首先插入两条包含子文档的数据:

MongoDB Compass比对索引 mongodb索引查询_nosql_29

② 现在需要查询 area 属性值是’taiwan’的记录,就是 查询子文档,需要使用: db.collectionName.find({filed.subfield:1/-1});

db.shop.find({'spc.area':'taiwan'});

MongoDB Compass比对索引 mongodb索引查询_mongodb_30

③ 给子文档 area 添加索引,和查询的方法很类似:

db.shop.ensureIndex({'spc.area':1});

MongoDB Compass比对索引 mongodb索引查询_笔记_31

按照索引性质创建索引

创建唯一索引

db.collection.ensureIndex({filed.subfield:1/-1}, {unique:true});

① [准备工作] 给 tea 文档添加两条数据:

MongoDB Compass比对索引 mongodb索引查询_MongoDB Compass比对索引_32

② [创建唯一索引命令]

db.tea.ensureIndex({email:1},{unique:true});

MongoDB Compass比对索引 mongodb索引查询_笔记_33

③ 现在测试,插入两条同样的记录,就会报错:

MongoDB Compass比对索引 mongodb索引查询_nosql_34

创建稀疏索引

[准备工作] 插入一条空的信息 {} ,然后给email列添加普通索引,再查询 {email:null} 和 explain的结果:

db.tea.insert({});
db.tea.ensureIndex({email:1});
db.tea.find({email:null});
db.tea.find({email:null}).explain();

MongoDB Compass比对索引 mongodb索引查询_nosql_35

稀疏索引 :如果针对field做索引,针对不含field列的文档,将不建立索引.与之相对,普通索引,会把该文档的field列的值认为NULL,并建索引.适宜于:小部分文档含有某列时. db.collection.ensureIndex({field:1/-1},{sparse:true});

接下来,删除所有已有的索引,然后添加稀疏索引,继续上面的查询 {email:null},就查不到结果了。

db.tea.ensureIndex({email:1},{sparse:true});

MongoDB Compass比对索引 mongodb索引查询_数据库_36

稀疏索引使用情景总结:

> db.tea.find();
{ "_id" : ObjectId("5275f99b87437c610023597b"), "email" : "a@163.com" }
{ "_id" : ObjectId("5275f99e87437c610023597c"), "email" : "b@163.com" }
{ "_id" : ObjectId("5275f9e887437c610023597e"), "email" : "c@163.com" }
{ "_id" : ObjectId("5275fa3887437c6100235980") }

如上内容,最后一行没有email列,如果分别加普通索引,和稀疏索引,对于最后一行的email分别当成null(普通索引) 和 忽略最后一行(稀疏索引) 来处理。根据{email:null}来查询,前者(普通索引)能查到,而后者(稀疏索引)查不到最后一行.

创建哈希索引(2.4新增)

哈希索引速度比普通索引快,但是,无能对范围查询进行优化.适宜于---随机性强的散列

语法: db.collection.ensureIndex({file:’hashed’});

db.tea.ensureIndex({email:'hashed'});

MongoDB Compass比对索引 mongodb索引查询_数据库_37

重建索引

一个表经过很多次修改后,导致表的文件产生空洞,索引文件也如此.可以通过索引的重建,减少索引文件碎片,并提高索引的效率.类似mysql中的optimize table

语法: db.collection.reIndex();

MongoDB Compass比对索引 mongodb索引查询_MongoDB Compass比对索引_38

【游标操作】

通俗的说,游标不是查询结果,而是查询的返回资源,或者接口.通过这个接口,你可以逐条读取.就像php中的fopen打开文件,得到一个资源一样, 通过资源,可以一行一行的读文件.

测试:在Mongodb中一次性插入10000条数据:

for(var i=0;i<10000;i++){
    db.bar.insert({_id:i+1,title:'hello world'+i,content:'aaa'+i});
};

MongoDB Compass比对索引 mongodb索引查询_笔记_39

声明游标:

var mycursor = db.collectioName.find(query,projection);
mycursor.hasNext() //判断游标是否已经取到尽头
mycursor.Next() //取出游标的下1个单元

[完整代码,只取前5条作为演示]
var mycursor = db.bar.find({_id:{$lte:5}});
print(mycursor.next()); //取出的是bson对象的格式,需要使用printjson打印
printjson(mycursor.next()); //取出的是json格式

MongoDB Compass比对索引 mongodb索引查询_mongodb_40

用while循环来打印游标,避免一个一个的手动操作。

var mycursorr = db.bar.find({_id:{$lte:5}});
while(mycursorr.hasNext()) {
    printjson(mycursorr.next());
}

MongoDB Compass比对索引 mongodb索引查询_nosql_41

用for循环来打印游标,更简单。

for(var cursor=db.bar.find({_id:{$lte:5}});cursor.hasNext();) {
    printjson(cursor.next());
}

MongoDB Compass比对索引 mongodb索引查询_MongoDB Compass比对索引_42

游标的迭代函数forEach,允许我们自定义回调函数来逐个处理每个单元.

cursor.forEach(回调函数);

var mycursor = db.bar.find({_id:{$lte:5}});
mycursor.forEach(function(obj){
    printjson(obj)
});

MongoDB Compass比对索引 mongodb索引查询_nosql_43

【扩展】function(obj){...} 这里不光可以print json,还可以进行编程处理,如下:

var mycursor = db.bar.find({_id:{$lte:5}});
mycursor.forEach(function(obj){
    print('your id is:'+obj._id+',your content is:'+obj.content)
});

MongoDB Compass比对索引 mongodb索引查询_nosql_44

游标在分页中的应用

比如查到10000行,跳过100页,取10行.一般地,我们假设每页N行, 当前是page页,就需要跳过前 (page-1)*N 行, 再取N行, 在mysql中, limit (offset,N) 来实现。在mongo中,用skip(), limit() 函数来实现

var mycursor = db.bar.find().skip(9995); 则是查询结果中,跳过前9995行

var mycursor = db.bar.find().skip(9995);
mycursor.forEach(function(obj){printjson(obj)});

MongoDB Compass比对索引 mongodb索引查询_nosql_45

var mycursor = db.bar.find().skip(9000).limit(10); 则是查询第901页,每页10条;也就是从第9000条开始取10条数据。

var mycursor = db.bar.find().skip(9000).limit(10);
mycursor.forEach(function(obj){printjson(obj)});

MongoDB Compass比对索引 mongodb索引查询_MongoDB Compass比对索引_46

不用游标也可以直接使用skip()limit()来查询

db.bar.find().skip(9000).limit(10);

MongoDB Compass比对索引 mongodb索引查询_数据库_47

通过cursor一次性得到所有数据, 并返回数组.

var cursor = db.bar.find().skip(9000).limit(5);
printjson(cursor.toArray()); //看到所有行
printjson(cursor.toArray()[2]); //看到第2行

MongoDB Compass比对索引 mongodb索引查询_笔记_48

MongoDB Compass比对索引 mongodb索引查询_笔记_49

注意: 不要随意使用toArray(),原因: 会把所有的行立即以对象形式组织在内存里.可以在取出少数几行时用此功能.