当前位置:   article > 正文

MongoDB的查询表达式、索引、游标_mongodb计算表达式

mongodb计算表达式

目录

【查询表达式】

比较运算

$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的商品:

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

$ne 不等于

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

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

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

$gt 大于 , $gte 大于等于

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

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

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

$lt 小于 , $lte 小于等于

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

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

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

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

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

$all 所有单元匹配

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

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

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

逻辑运算

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

  1. db.goods.find({
  2. $and:[
  3. {shop_price:{$gt:100}},
  4. {shop_price:{$lt:500}}
  5. ]} , {
  6. goods_name:1,
  7. shop_price:1,
  8. _id:0
  9. });
  10. MySQL中:where price>=100 AND price<=500 或 where price BETWEEN 100 AND 500

分析:

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

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

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

  1. db.goods.find({$or:[
  2. {$and:[{shop_price:{$gt:40}},{shop_price:{$lt:100}}]},
  3. {$and:[{shop_price:{$gt:1000}},{shop_price:{$lt:2000}}]}
  4. ]},{goods_name:1,shop_price:1,_id:0});
  5. MySQL中: where (price>40 AND price<100) OR (price>1000 AND price<2000)

例:取出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})

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

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

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

用 $nor 实现

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

元素运算符

例: 取出{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});

{field:{$exists:1}}

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

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

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

{field:{$type:2}}

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

参考文档:$type — MongoDB Manual

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

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

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

JS运算符

用$where表达式来查询。

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

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

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

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

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

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

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

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

查询表达式总结

  1. 1:主键为32的商品
  2. db.goods.find({goods_id:32});
  3. 2:不属第3栏目的所有商品($ne)
  4. db.goods.find({cat_id:{$ne:3}},{goods_id:1,cat_id:1,goods_name:1});
  5. 3:本店价格高于3000元的商品{$gt}
  6. db.goods.find({shop_price:{$gt:3000}},{goods_name:1,shop_price:1});
  7. 4:本店价格低于或等于100元的商品($lte)
  8. db.goods.find({shop_price:{$lte:100}},{goods_name:1,shop_price:1});
  9. 5:取出第4栏目或第11栏目的商品($in)
  10. db.goods.find({cat_id:{$in:[4,11]}},{goods_name:1,shop_price:1});
  11. 6:取出100<=价格<=500的商品($and)
  12. db.goods.find({$and:[{price:{$gt:100},{$price:{$lt:500}}}]);
  13. 7:取出不属于第3栏目且不属于第11栏目的商品($and $nin和$nor分别实现)
  14. db.goods.find({$and:[{cat_id:{$ne:3}},{cat_id:{$ne:11}}]},{goods_name:1,cat_id:1})
  15. db.goods.find({cat_id:{$nin:[3,11]}},{goods_name:1,cat_id:1});
  16. db.goods.find({$nor:[{cat_id:3},{cat_id:11}]},{goods_name:1,cat_id:1});
  17. 8:取出价格大于100且小于300,或者大于4000且小于5000的商品()
  18. 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});
  19. 9:取出goods_id%5 == 1, 即,1,6,11,..这样的商品
  20. db.goods.find({goods_id:{$mod:[5,1]}});
  21. 10:取出有age属性的文档
  22. db.stu.find({age:{$exists:1}});
  23. # 含有age属性的文档将会被查出

【索引】

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

查询sn:99的用户:

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

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

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

关于索引的说明:

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

索引的常用命令

创建普通的单列索引

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

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

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

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

查看当前索引状态

db.collection.getIndexes();

db.stu.getIndexes();

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

删除单个索引

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

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

删除所有索引  

db.collection.dropIndexes();

db.stu.dropIndexes();

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

创建多列索引 

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

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

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

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

创建子文档索引

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

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

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

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

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

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

按照索引性质创建索引

创建唯一索引

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

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

② [创建唯一索引命令]

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

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

创建稀疏索引

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

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

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

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

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

稀疏索引使用情景总结:

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

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

创建哈希索引(2.4新增)

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

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

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

重建索引

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

语法: db.collection.reIndex();

【游标操作】

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

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

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

声明游标:

  1. var mycursor = db.collectioName.find(query,projection);
  2. mycursor.hasNext() //判断游标是否已经取到尽头
  3. mycursor.Next() //取出游标的下1个单元
  4. [完整代码,只取前5条作为演示]
  5. var mycursor = db.bar.find({_id:{$lte:5}});
  6. print(mycursor.next()); //取出的是bson对象的格式,需要使用printjson打印
  7. printjson(mycursor.next()); //取出的是json格式

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

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

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

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

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

cursor.forEach(回调函数);

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

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

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

游标在分页中的应用

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

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

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

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

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

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

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

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

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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/630234
推荐阅读
相关标签
  

闽ICP备14008679号