目录
【查询表达式】
比较运算
$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
$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
$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
$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
$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)
$all 所有单元匹配
语法: {field:{$all:[v1,v2..]}} ,是指取出 field列是一个数组,且至少包含 v1,v2值。MySQL中没有此用法。
例:取出emp表中,hobby字段包含“A”和“B”的:
db.emp.find({hobby:{$all:[‘A’,’B’]}});
逻辑运算
例:取出价格介于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}
例:取出(价格大于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)
例:取出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的。
例:取出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 试一下。当然,简单了一些,效率也较低。
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
});
注意: 用$where查询时, mongodb是把bson结构的二进制数据转换为json结构的对象, 然后比较对象的属性是否满足表达式,因此速度较慢!
用正则表达式查询 以”诺基亚”开头的商品,这样查询效率也比较低。
db.goods.find({goods_name:{$regex:/^诺基亚*/}},{goods_id:1,goods_name:1,_id:0});
查询表达式总结
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条数据:
查询sn:99的用户:
可以使用 db.find(query).explain(); 查看查询计划,类似于MySQL的explain解析。
db.stu.find({sn:99}).explain();
"cursor" : "BasicCursor", ----说明没有索引发挥作用
"nscannedObjects" : 1000 ----理论上要扫描多少行:1000行
关于索引的说明:
- 索引提高查询速度,降低写入速度,权衡常用的查询字段,不必在太多列上建索引
- 在mongodb中,索引可以按字段升序/降序来创建,便于排序
- 默认是用btree来组织索引文件,2.4版本以后,也允许建立hash索引.
索引的常用命令
创建普通的单列索引
db.collection.ensureIndex({field:1/-1}); #1是升续,2是降续。
db.stu.ensureIndex({sn:1});
然后再explain查看就不一样了:
"cursor" : "BtreeCursor sn_1" ----用到的btree索引
"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的结果:
db.tea.insert({});
db.tea.ensureIndex({email:1});
db.tea.find({email:null});
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});
稀疏索引使用情景总结:
> 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'});
重建索引
一个表经过很多次修改后,导致表的文件产生空洞,索引文件也如此.可以通过索引的重建,减少索引文件碎片,并提高索引的效率.类似mysql中的optimize table
语法: db.collection.reIndex();
【游标操作】
通俗的说,游标不是查询结果,而是查询的返回资源,或者接口.通过这个接口,你可以逐条读取.就像php中的fopen打开文件,得到一个资源一样, 通过资源,可以一行一行的读文件.
测试:在Mongodb中一次性插入10000条数据:
for(var i=0;i<10000;i++){
db.bar.insert({_id:i+1,title:'hello world'+i,content:'aaa'+i});
};
声明游标:
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格式
用while循环来打印游标,避免一个一个的手动操作。
var mycursorr = db.bar.find({_id:{$lte:5}});
while(mycursorr.hasNext()) {
printjson(mycursorr.next());
}
用for循环来打印游标,更简单。
for(var cursor=db.bar.find({_id:{$lte:5}});cursor.hasNext();) {
printjson(cursor.next());
}
游标的迭代函数forEach,允许我们自定义回调函数来逐个处理每个单元.
cursor.forEach(回调函数);
var mycursor = db.bar.find({_id:{$lte:5}});
mycursor.forEach(function(obj){
printjson(obj)
});
【扩展】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)
});
游标在分页中的应用
比如查到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)});
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)});
不用游标也可以直接使用skip()和limit()来查询
db.bar.find().skip(9000).limit(10);
通过cursor一次性得到所有数据, 并返回数组.
var cursor = db.bar.find().skip(9000).limit(5);
printjson(cursor.toArray()); //看到所有行
printjson(cursor.toArray()[2]); //看到第2行
注意: 不要随意使用toArray(),原因: 会把所有的行立即以对象形式组织在内存里.可以在取出少数几行时用此功能.