MongoDB 查询篇

上一篇文章说了MongoDB一些常用的更新操作,这篇就来写写常用的查询操作。

1、最基本的查询

最基本的查询就莫过于,我们之前的findOne()和find()了。基本上已经非常熟悉了,但是我们在后面会慢慢探讨findOne和find的多种多样的查询方式。先来简单的回忆一下

db.product.findOne();
db.product.find();


其实我们可以像更新一样,添加查询条件:

db.product.find({product_name:"iPhoneX"});


2、查询条件

在我们的查询中好像只有=的匹配,就没有别的了。下面我们看看其他的查询条件

"$lt" 小于  "$lte"小于等于   "$gt" 大于   "$gte" 大于等于 $ne 不等于

下面的例子,我们查询商品价格大于5000元的商品:

db.product.find({price:{$gte:5000}});

查询上架日期为2017年10月的

db.product.find({create_date:{$gte:new Date("2017-10-01")},create_date:{$lt:new Date("2017-11-01")}});


查询不是官方自营店的iPhone X商品

db.product.find({store:{$ne:"官方自营店"}});

目前我们的所有查询都是AND形式去组合条件查询,后面会通过一些操作符实现其他形式的组合查询。


3、指定返回数据的键

在SQL当中,我们可以在select 关键字后面指定要返回数据字段。MongoDB也可以实现类似的功能。

我们可以通过find方法的第二个参数指定我们需要返回什么字段,或者指定不返回什么字段

db.product.find({product_name:"iPhoneX"},{product_name:1,price:1,brand:1,store:1});
我们通过第二个参数,指定了我们需要显示那几个字段。你会看到在find第二个参数当中,字段属性的值都是1,代表显示,如果是0代表不显示。需要注意的是,默认_id就算没有指定显示也会自动显示,当然你可以显性指定为不显示

结果:

{ 
    "_id" : ObjectId("59e47fdb203e071a1b02e544"), 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "brand" : "Apple", 
    "store" : "官方自营店"
}
{ 
    "_id" : ObjectId("59e5a20f7dfc75087c893b50"), 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "brand" : "Apple", 
    "store" : "第三方渠道"
}
我们也可以指定不现实那几个字段属性:

db.product.find({product_name:"iPhoneX"},{_id:0,size:0,newest_commment:0,sku:0,popular_comment:0,keyword:0,create_date:0});
显示结果如下:

{ 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "description" : "一台贵到666的手机", 
    "product_number" : "9088816371", 
    "brand" : "Apple", 
    "store" : "官方自营店"
}
{ 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "description" : "一台贵到666的手机", 
    "product_number" : "9088816371", 
    "brand" : "Apple", 
    "store" : "第三方渠道"
}


4、OR查询

上面我们所有的组合查询都是AND方式去查询,我们可以通过$in、$nin 或者 $or  操作符去进行OR方式的组合查询

为了测试,我们创建一个用户collection,然后通过手机号码通过$in获得用户数据:

var userA = {username:"TONY",user_pic:"default.jpg",phone_number:"13333333338"};
var userB = {username:"YAN", user_pic:"default.jpg",phone_number:"13888888883"};
db.user.insert(userA);
db.user.insert(userB);
db.user.find({"phone_number":{$in:["13333333338","13888888883"]}});
返回的结果:

{ 
    "_id" : ObjectId("59e800722d68f77114ec4fac"), 
    "username" : "TONY", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13333333338"
}
{ 
    "_id" : ObjectId("59e800822d68f77114ec4fad"), 
    "username" : "YAN", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13888888883"
}
当然我们还可以使用 $nin 不包含在数值的匹配条件都查询出来

var userA = {username:"TONY",user_pic:"default.jpg",phone_number:"13333333338"};
var userB = {username:"YAN", user_pic:"default.jpg",phone_number:"13888888883"};
var userC = {username:"CHAO",user_pic:"default.jpg",phone_number:"18333333338"};
var userD = {username:"HONO",user_pic:"default.jpg",phone_number:"18888888883"};
db.user.insert(userA);
db.user.insert(userB);
db.user.insert(userC);
db.user.insert(userD);
db.user.find({"phone_number":{$nin:["13333333338","13888888883"]}});
返回的结果:

{ 
    "_id" : ObjectId("59e802432d68f77114ec4fae"), 
    "username" : "CHAO", 
    "user_pic" : "default.jpg", 
    "phone_number" : "18333333338"
}
{ 
    "_id" : ObjectId("59e802442d68f77114ec4faf"), 
    "username" : "HONO", 
    "user_pic" : "default.jpg", 
    "phone_number" : "18888888883"
}

还有就是$or操作符,这个就是跟SQL 的OR语句差不多。一下就是使用$or查询username 是 TONY 或者 phone_number 是 18888888883 而且user_pic 一定要是 default.jpg 就等以下这种情况:

(username = "TONY" OR phone_number = "18888888883") AND user_pic = "default.jpg"

db.user.find({$or:[{"username":"TONY"},{"phone_number":"18888888883"}],"user_pic":"default.jpg"});


查询结果如下
{ 
    "_id" : ObjectId("59e800722d68f77114ec4fac"), 
    "username" : "TONY", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13333333338"
}
{ 
    "_id" : ObjectId("59e802442d68f77114ec4faf"), 
    "username" : "HONO", 
    "user_pic" : "default.jpg", 
    "phone_number" : "18888888883"
}


除了一个$or 还有一个$nor。就是取反:

db.user.find({$nor:[{"phone_number":"18888888883"},{"username":"YAN"}]});

返回结果:

{ 
    "_id" : ObjectId("59e800722d68f77114ec4fac"), 
    "username" : "TONY", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13333333338"
}
{ 
    "_id" : ObjectId("59e802432d68f77114ec4fae"), 
    "username" : "CHAO", 
    "user_pic" : "default.jpg", 
    "phone_number" : "18333333338"
}


5、$not操作符

这个比较难解释,这个跟上面OR小节有比较大的关系。可以在其他任意条件之前去做取反操作,打个比方:

db.user.find({"phone_number":{$not:{$in:["18888888883","13333333338"]}}});


搜索结果如下:

{ 
    "_id" : ObjectId("59e800822d68f77114ec4fad"), 
    "username" : "YAN", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13888888883"
}
{ 
    "_id" : ObjectId("59e802432d68f77114ec4fae"), 
    "username" : "CHAO", 
    "user_pic" : "default.jpg", 
    "phone_number" : "18333333338"
}


6、null的特别处理

对于null这种空值处理还是比较麻烦的,因为再MongoDB不像SQL每一行记录的数据格式都是固定的。笔者在SpringCloud的微服务项目当中,通过MongoDB repository 去添加文档,如果为null 的字段,根本就不会把改字段属性添加到MongoDB当中。所以除了去判断空值,还需要去判断字段属性(KEY)是否存在。

为了测试,我为已经存在的user collection当中添加gender字段属性。我不会全部添加,部分我们添加gender的key但是写上null,还有一些我不会去添加gender的key。【其实,在MongoDB当中,字段属性是不太正确的做法,但是作为一个SQL的资深用户,为了好理解一直这样说,后面我会改成KEY 或者 键】

var tony = db.user.findOne({"username":"TONY"});
tony.gender = "male";
db.user.save(tony);

var yan = db.user.findOne({"username":"YAN"});
yan.gender = "male";
db.user.save(yan);

var chao = db.user.findOne({"username":"CHAO"});
chao.gender = null;
db.user.save(chao);

db.user.find();

修改结果:

{ 
    "_id" : ObjectId("59e83a2d2d68f77114ec4fb4"), 
    "username" : "TONY", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13333333338", 
    "gender" : "male"
}
{ 
    "_id" : ObjectId("59e83a2d2d68f77114ec4fb5"), 
    "username" : "YAN", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13888888883", 
    "gender" : "male"
}
{ 
    "_id" : ObjectId("59e83a2e2d68f77114ec4fb6"), 
    "username" : "CHAO", 
    "user_pic" : "default.jpg", 
    "phone_number" : "18333333338", 
    "gender" : null
}
{ 
    "_id" : ObjectId("59e83a2f2d68f77114ec4fb7"), 
    "username" : "HONO", 
    "user_pic" : "default.jpg", 
    "phone_number" : "18888888883"
}

目前可以发现HONO 是没有gender ,CHAO这个用户gender 则是NULL,其他用户是有gender而且都不为空。 现在我们查询gender为NULL的用户

db.user.find({gender:null});
我们通过上面的查询可以同时获得HONO 和 CHAO 两个用户,可能是版本的原因。在以前的旧版本当中是无法使用上面的查询的。

我们可以使用$exists 获得不存在gender 的用户

db.user.find({gender:{$exists:false}});

7、使用正则表达式进行查询

MongoDB最为方便的查询就莫过于正则表达式查询了。我们可以通过MongoDB的正则表达式查询实现如SQL的LIKE模糊查询

db.user.find({username:/O/i});

查询结果:

{ 
    "_id" : ObjectId("59e83a2d2d68f77114ec4fb4"), 
    "username" : "TONY", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13333333338", 
    "gender" : "male"
}
{ 
    "_id" : ObjectId("59e83a2e2d68f77114ec4fb6"), 
    "username" : "CHAO", 
    "user_pic" : "default.jpg", 
    "phone_number" : "18333333338", 
    "gender" : null
}
{ 
    "_id" : ObjectId("59e83a2f2d68f77114ec4fb7"), 
    "username" : "HONO", 
    "user_pic" : "default.jpg", 
    "phone_number" : "18888888883"
}


8、查询数组

首先我们通过一个普通的查询,看看MongoDB数组的查询是怎么样的:

db.product.find({"keyword":"iphone"},{"product_name":1,"keyword":1,"price":1,"store":1});

结果可以发现,只要keyword数组当中,有iphone 就匹配上。

{ 
    "_id" : ObjectId("59e47fdb203e071a1b02e544"), 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "keyword" : [
        "苹果", 
        "iphone", 
        "apple"
    ], 
    "store" : "官方自营店"
}
{ 
    "_id" : ObjectId("59e5a20f7dfc75087c893b50"), 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "keyword" : [
        "苹果", 
        "iphone", 
        "apple"
    ], 
    "store" : "第三方渠道"
}

但是事实上却没有我们想象中这么方便,如我们希望在一个数组属性当中同时匹配其中两个关键字的话,上面的方法就不是这么奏效了。

看看下面的列子:

var userA = {username:"TONY",user_pic:"default.jpg",phone_number:"13333333338",favorite:["swimming","computer game","reading","running"]};
var userB = {username:"YAN", user_pic:"default.jpg",phone_number:"13888888883",favorite:["football","drawing","video game","shopping"]};
var userC = {username:"CHAO", user_pic:"default.jpg",phone_number:"13888888889",favorite:["talking show","swimming","computer game","kidding"]};
var userD = {username:"LTT", user_pic:"default.jpg",phone_number:"13888888833",favorite:["shopping","watching tv","running","travel"]};
db.user.insert(userA);
db.user.insert(userB);
db.user.insert(userC);
db.user.insert(userD);
db.user.find({favorite:["swimming","reading"]});

结果将会返回空,因为上面这条语句会精确匹配只有两个元素而且是swimming 和 reading的文档结果。最重要的是,精确匹配会连顺序都要完全匹配。

如果希望在一个数组当中同时匹配到指定的元素,我们可以使用$all 操作符。(匹配跟顺序无关)

db.user.find({favorite:{$all : ["swimming","reading"]}});
返回结果:

{ 
    "_id" : ObjectId("59eb577e89c5ddc1ad8058b6"), 
    "username" : "TONY", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13333333338", 
    "favorite" : [
        "swimming", 
        "computer game", 
        "reading", 
        "running"
    ]
}


指定元素位置匹配,我们可以获得指定第二个元素为drawing 的文档。

db.user.find({"favorite.1":"drawing"});

返回结果:

{ 
    "_id" : ObjectId("59eb577f89c5ddc1ad8058b7"), 
    "username" : "YAN", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13888888883", 
    "favorite" : [
        "football", 
        "drawing", 
        "video game", 
        "shopping"
    ]
}

我们可以通过使用$size操作符,匹配指定长度的数组:

db.user.find({"favorite":{$size:4}});


9、通过$slice获得指定数组属性的子数组

我们希望返回用户的前两个favorite

db.user.find({"username":"TONY"},{username:1,phone_number:1,favorite:{$slice:2}});

返回的结果:

{ 
    "_id" : ObjectId("59eb577e89c5ddc1ad8058b6"), 
    "username" : "TONY", 
    "phone_number" : "13333333338", 
    "favorite" : [
        "reading", 
        "running"
    ]
}

如果希望返回最后两个favorite元素,将2改成-1即可。

最牛逼的还是可以截取中间指定范围的子数组:

db.user.find({"username":"TONY"},{username:1,phone_number:1,favorite:{$slice:[1,2]}});
返回结果:

{ 
    "_id" : ObjectId("59eb577e89c5ddc1ad8058b6"), 
    "username" : "TONY", 
    "phone_number" : "13333333338", 
    "favorite" : [
        "computer game", 
        "reading"
    ]
}

需要提醒的是,如果我们只在find第二个参数当中指定了$slice会默认返回全部的键

db.user.find({"username":"TONY"},{favorite:{$slice:[1,2]}});
返回结果:

{ 
    "_id" : ObjectId("59eb577e89c5ddc1ad8058b6"), 
    "username" : "TONY", 
    "user_pic" : "default.jpg", 
    "phone_number" : "13333333338", 
    "favorite" : [
        "computer game", 
        "reading"
    ]
}

有时候我们不知道我们想要的数组元素具体的下标,我们可以通过查询的方位去定位到某一个数组元素:

db.product.findOne({"popular_comment.user_id":119},{"popular_comment.$":1});

返回结果

{ 
    "_id" : ObjectId("59e47fdb203e071a1b02e544"), 
    "popular_comment" : [
        {
            "content" : "Comment C content !", 
            "user_id" : 119.0, 
            "popularity_degree" : 108.0
        }
    ]
}


10、坑爹数组组合查询

来看看下面的查询例子:

db.product.update({"store":"官方自营店"},{$set:{"sku.0.price":8388,"sku.1.price":8388,"sku.2.price":9688,"sku.3.price":9688}});
db.product.findOne({"store":"官方自营店"});
db.product.update({"store":"第三方渠道"},{$set:{"sku.0.price":9600,"sku.1.price":9800,"sku.2.price":12388,"sku.3.price":12699}});
db.product.findOne({"store":"第三方渠道"});
db.product.update({"store":"金牌苹果店"},{$set:{"sku.0.price":9500,"sku.1.price":9600,"sku.2.price":12388,"sku.3.price":12588}});
db.product.findOne({"store":"金牌苹果店"});

db.product.find({"sku.price":{$gt:9850,$lt:10000}});
按照道理来说,应该查询不出任何数据的,因为9850-10000这个区间之内,根本没有任何一个数组的元素可以匹配上。但是事实上mo'nMongoDB返回了以下的数据:

{ 
    "_id" : ObjectId("59e5a20f7dfc75087c893b50"), 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "description" : "一台贵到666的手机", 
    "product_number" : "9088816371", 
    "brand" : "Apple", 
    "size" : "143.6 X 70.9", 
    "newest_commment" : {
        "comment_content" : "this is newtest comment at that place!", 
        "user_id" : 109382.0, 
        "create_date" : ISODate("2017-10-17T01:15:20.466+0000")
    }, 
    "sku" : [
        {
            "capacity" : "64G", 
            "style" : "silver", 
            "price" : 9600.0
        }, 
        {
            "capacity" : "64G", 
            "style" : "gray", 
            "price" : 9800.0
        }, 
        {
            "capacity" : "256G", 
            "style" : "silver", 
            "price" : 12388.0
        }, 
        {
            "capacity" : "256G", 
            "style" : "gray", 
            "price" : 12699.0
        }
    ], 
    "popular_comment" : [
        {
            "content" : "Comment A content !", 
            "user_id" : 399.0, 
            "popularity_degree" : 89.0
        }, 
        {
            "content" : "Comment C content !", 
            "user_id" : 119.0, 
            "popularity_degree" : 108.0
        }, 
        {
            "content" : "Comment E content !", 
            "user_id" : 893.0, 
            "popularity_degree" : 103.0
        }
    ], 
    "keyword" : [
        "苹果", 
        "iphone", 
        "apple"
    ], 
    "store" : "第三方渠道", 
    "create_date" : ISODate("2017-11-10T00:00:00.000+0000")
}
{ 
    "_id" : ObjectId("59e9c5872d68f77114ec4fb8"), 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "description" : "一台贵到666的手机", 
    "product_number" : "9088816371", 
    "brand" : "Apple", 
    "size" : "143.6 X 70.9", 
    "newest_commment" : {
        "comment_content" : "this is newtest comment at that place!", 
        "user_id" : 109382.0, 
        "create_date" : ISODate("2017-10-17T01:15:20.466+0000")
    }, 
    "sku" : [
        {
            "capacity" : "64G", 
            "style" : "silver", 
            "price" : 9500.0
        }, 
        {
            "capacity" : "64G", 
            "style" : "gray", 
            "price" : 9600.0
        }, 
        {
            "capacity" : "256G", 
            "style" : "silver", 
            "price" : 12388.0
        }, 
        {
            "capacity" : "256G", 
            "style" : "gray", 
            "price" : 12588.0
        }
    ], 
    "popular_comment" : [
        {
            "content" : "Comment A content !", 
            "user_id" : 399.0, 
            "popularity_degree" : 89.0
        }, 
        {
            "content" : "Comment C content !", 
            "user_id" : 119.0, 
            "popularity_degree" : 108.0
        }, 
        {
            "content" : "Comment E content !", 
            "user_id" : 893.0, 
            "popularity_degree" : 103.0
        }
    ], 
    "keyword" : [
        "iphone"
    ], 
    "store" : "金牌苹果店", 
    "create_date" : ISODate("2017-11-10T00:00:00.000+0000")
}
可以看到的是,有两条数据。因为MongoDB会查询文档中sku数组中的其中有元素大于9850,并且其中有元素小于10000即可。但是必须有一个元素满足大于9850和必须有一个元素小于10000,那这里就会匹配以上的两条数据。但是官方自营店的数据却匹配不上去了。因为官方自营店无法满足有元素大约9850这个匹配条件。

所以我们可以使用$elemMatch这个操作符进行组合条件匹配操作,但是需要注意的是,$elemMatch无法匹配非数组的key

db.product.find({"sku.price":{$elemMatch:{$gt:9850,$lt:10000}}});
此时查询就会返回空


11、$where查询

$where 查询是一种非常消耗资源的查询方法,当所有操作符无法达成你的查询要求时,就可以使用$where。但是$where 不走索引,查询速度会怎么样大家应该也很清楚了。所以如无必要就不用使用它。以下我们通过以下查询演示一下$where的查询方法。

db.product.find({$where:function(){
	if(this.store == "官方自营店"){
		return true;
	}
	return false;
}});
查询与db.product.find({store:"官方自营店"})等价,我们可以看见$where查询非常灵活,因为查询是通过JavaScript进行的,所以方便直接。但是查询速度是个大问题,所以一般情况下,我们会先用普通的操作符进行初步筛选,然后再通过$where进行二次筛选,这样的话查询速度会比较可控。关于这种查询会在之后的文章中介绍。


12、分页与排序

在SQL当中排序和分页查询应该算是使用率比较高的一个操作了,在MongoDB当中,分页查询也是相当的重要。下面通过skip limit sort方法去实现分页排序查询。

db.product.find().skip(1).limit(2).sort({"sku.0.price":-1});
查询一开始先通过skip跳开第一行数据,然后限制返回2条数据,查询通过商品的第一个sku的价格进行倒列排序(如果我们需要正序排序将-1改成1即可),返回的结果如下:

{ 
    "_id" : ObjectId("59e9c5872d68f77114ec4fb8"), 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "description" : "一台贵到666的手机", 
    "product_number" : "9088816371", 
    "brand" : "Apple", 
    "size" : "143.6 X 70.9", 
    "newest_commment" : {
        "comment_content" : "this is newtest comment at that place!", 
        "user_id" : 109382.0, 
        "create_date" : ISODate("2017-10-17T01:15:20.466+0000")
    }, 
    "sku" : [
        {
            "capacity" : "64G", 
            "style" : "silver", 
            "price" : 9500.0
        }, 
        {
            "capacity" : "64G", 
            "style" : "gray", 
            "price" : 9600.0
        }, 
        {
            "capacity" : "256G", 
            "style" : "silver", 
            "price" : 12388.0
        }, 
        {
            "capacity" : "256G", 
            "style" : "gray", 
            "price" : 12588.0
        }
    ], 
    "popular_comment" : [
        {
            "content" : "Comment A content !", 
            "user_id" : 399.0, 
            "popularity_degree" : 89.0
        }, 
        {
            "content" : "Comment C content !", 
            "user_id" : 119.0, 
            "popularity_degree" : 108.0
        }, 
        {
            "content" : "Comment E content !", 
            "user_id" : 893.0, 
            "popularity_degree" : 103.0
        }
    ], 
    "keyword" : [
        "iphone"
    ], 
    "store" : "金牌苹果店", 
    "create_date" : ISODate("2017-11-10T00:00:00.000+0000")
}
{ 
    "_id" : ObjectId("59e47fdb203e071a1b02e544"), 
    "product_name" : "iPhoneX", 
    "price" : 8699.0, 
    "description" : "一台贵到666的手机", 
    "product_number" : "9088816371", 
    "brand" : "Apple", 
    "size" : "143.6 X 70.9", 
    "newest_commment" : {
        "comment_content" : "this is newtest comment at that place!", 
        "user_id" : 109382.0, 
        "create_date" : ISODate("2017-10-17T01:15:20.466+0000")
    }, 
    "sku" : [
        {
            "capacity" : "64G", 
            "style" : "silver", 
            "price" : 8388.0
        }, 
        {
            "capacity" : "64G", 
            "style" : "gray", 
            "price" : 8388.0
        }, 
        {
            "capacity" : "256G", 
            "style" : "silver", 
            "price" : 9688.0
        }, 
        {
            "capacity" : "256G", 
            "style" : "gray", 
            "price" : 9688.0
        }
    ], 
    "popular_comment" : [
        {
            "content" : "Comment A content !", 
            "user_id" : 399.0, 
            "popularity_degree" : 89.0
        }, 
        {
            "content" : "Comment C content !", 
            "user_id" : 119.0, 
            "popularity_degree" : 108.0
        }, 
        {
            "content" : "Comment E content !", 
            "user_id" : 893.0, 
            "popularity_degree" : 103.0
        }
    ], 
    "keyword" : [
        "苹果", 
        "iphone", 
        "apple"
    ], 
    "store" : "官方自营店", 
    "create_date" : ISODate("2017-10-27T00:00:00.000+0000")
}

13、不使用skip进行分页查询

其实使用skip进行分页查询是最为方便的一个做法,但是事实上skip的查询效率上并不高。skip需要先找到需要跳过的数据文档,然后进行丢弃,这个已经足够低效了。如果数据量大的话通过skip进行查询将不会是一个好的策略。我们可以利用一些关键的key,然后通过$gt或者$lt的方式进行跳过的操作。

举个例子

var pageOne = db.product.find().limit(1).sort({"sku.0.price":1});
var firstItem = pageOne.next();
db.product.find({"sku.0.price":{$gt:firstItem.sku[0].price}}).limit(1).sort({"sku.0.price":1});
可以看见我们是通过游标pageOne,获得第一页最后一个对象,然后通过改对象的价格属性进行$gt条件查询,再通过limit进行限制。重点是两条查询语句必须要使用价格属性进行排序。如果是有两个同样的价格这样就不太奏效了,所以也不是万能的一个方式。


14、游标

其实第13点已经使用了游标了,就是那个pageOne。游标跟我们以前SQL的游标其实差不多,MongoDB的游标在find方法执行完之后会返回,但是不会产生查询,当第一个游标的第一个next方法被出发后默认先会查询100条或者4M大小的数据(选择两者这间的最小者)。当游标已经存在的数据已经耗尽,游标会再去忽的100条数据或者4M大小的数据。所以不必担心游标每次next都会去查询。

var cursor = db.product.find();
cursor.forEach(function(item){
	print(item.product_name);
});

15、随机查询

在之前我写过关于SQL反模式的文章,其中说过不要用MySQL的random查询,可以通过random得出一个值然后,通过这个值查询主键大于这个值的数据,limit为一条数据,这样我们就可以获得一条随机的数据了。同样的MongoDB也可以这样去做,不过MongoDB当中主键不是一个自动增长的数,所以之前MySQL中的随机查询我们就用不上了。

我们可以通过为文档添加相应的random属性:

var cursor = db.product.find();
cursor.forEach(function(item){
	item.random = Math.random();
	db.product.save(item);
});
db.product.find();
添加完之后,我们可以通过以下这种方式去获得随机的文档:

db.product.find({"random":{$lt:Math.random()}}).limit(1);
当然如果以上的查询查不到数据就可以通过$gt来进行查询,如果两个都查询不到任何数据就可以直接返回空了。

16、快照查询

快照查询是一种安全的查询操作,MongoDB在文档修改后导致原位置无法扩展,会将文档移动到末尾然后进行修改扩展。所以以下这种情况就可能会出现问题

var cursor = db.product.find();
cursor.forEach(function(item){
	item.keyword.push("New");
});
如果在添加新的关键字时,发现当前位置没有足够的空间存储新的关键字,MongoDB会将这个文档移动到末尾,然后添加新的关键字。但是这样会导致cursor会在末尾获得到同样的文档。基于解决这种问题,我们可以使用快照查询:

var cursor = db.product.find().snapshot();
cursor.forEach(function(item){
	item.keyword.push("New");
});
使用snapshot之后会,按照_id索引去遍历执行。但是虽然这样会安全一点,但是事实上查询效率会有所降低。如非必要也不会使用快照查询。


17、数据库命令

其实在更新的文档当中我已经写过一些书库命令了,以下是一些补充:

通常我们会使用db.collection_name.drop()这种方式去删除一个collection

其中我们可以直接使用数据库命令:

db.runCommand({"drop":"product_pv"});

返回的结果如下:

{ 
    "ns" : "product_service.product_pv", 
    "nIndexesWas" : 1.0, 
    "ok" : 1.0
}


获得最后一次执行的结果:

db.runCommand({"getLastError":1});
返回结果如下:

{ 
    "connectionId" : 10.0, 
    "n" : 0.0, 
    "syncMillis" : 0.0, 
    "writtenTo" : null, 
    "err" : null, 
    "ok" : 1.0
}


有些操作只能够是管理员执行,所以不能使用runCommand了,而是adminCommand。例如关闭MongoDB的服务:

db.adminCommand({"shutdown":1});






你可能感兴趣的:(MongoDB)