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});






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章