學習MongoDB--(4-3):MongoDB查詢(遊標使用)

來源:互聯網
上載者:User

MongoDB中find()函數返回一個遊標,用戶端通過對遊標進行一些設定就能對查詢結果進行有效地控制,如可以限制查詢得到的結果數量、跳過部分結果、或對結果集按任意鍵進行排序等。我們之前在Shell中進行操作,都是直接使用find()函數,並沒有使用其傳回值,如:

> for(var i=0; i<100; i++){... db.coll.insert({"x" : i});... }> db.coll.find();{ "_id" : ObjectId("5023997e1ed370450fbdcf89"), "x" : 25 }{ "_id" : ObjectId("5023997e1ed370450fbdcf8a"), "x" : 26 }{ "_id" : ObjectId("5023997e1ed370450fbdcf8b"), "x" : 27 }{ "_id" : ObjectId("5023997e1ed370450fbdcf8c"), "x" : 28 }{ "_id" : ObjectId("5023997e1ed370450fbdcf8d"), "x" : 29 }{ "_id" : ObjectId("5023997e1ed370450fbdcf8e"), "x" : 30 }{ "_id" : ObjectId("5023997e1ed370450fbdcf8f"), "x" : 31 }{ "_id" : ObjectId("5023997e1ed370450fbdcf90"), "x" : 32 }{ "_id" : ObjectId("5023997e1ed370450fbdcf91"), "x" : 33 }{ "_id" : ObjectId("5023997e1ed370450fbdcf92"), "x" : 34 }{ "_id" : ObjectId("5023997e1ed370450fbdcf93"), "x" : 35 }{ "_id" : ObjectId("5023997e1ed370450fbdcf94"), "x" : 36 }{ "_id" : ObjectId("5023997e1ed370450fbdcf95"), "x" : 37 }{ "_id" : ObjectId("5023997e1ed370450fbdcf96"), "x" : 38 }{ "_id" : ObjectId("5023997e1ed370450fbdcf97"), "x" : 39 }{ "_id" : ObjectId("5023997e1ed370450fbdcf98"), "x" : 40 }{ "_id" : ObjectId("5023997e1ed370450fbdcf99"), "x" : 41 }{ "_id" : ObjectId("5023997e1ed370450fbdcf9a"), "x" : 42 }{ "_id" : ObjectId("5023997e1ed370450fbdcf9b"), "x" : 43 }{ "_id" : ObjectId("5023997e1ed370450fbdcf9c"), "x" : 44 }has more>

我們先通過javascript指令碼向集合中填充100條文檔,然後直接調用find函數。其會自動遞迴find返回的遊標,將前20條資料展示在shell中。如果我們通過變數保留find函數的傳回值,其不會自動進行遍曆顯示操作:

> var cursor = db.coll.find();>

這樣做,實際發生的是,調用完find後,此時Shell並不會去真正地訪問資料庫,而是等待開始要求獲得結果的時候才向資料庫發送查詢請求。我們此時可以對這個遊標進行各種設定,然後調用遊標的hashNext()或next()方法,這樣就會真正訪問資料庫,這是一個懶載入的過程。如下:

> var cursor = db.coll.find();> while(cursor.hasNext()){... var doc = cursor.next();... // do stuff with doc... };>

上述代碼中,當調用cursor.hasNext()時,查詢被發往資料庫,預設會返回前100條文檔或者前4M的資料(兩者之中較小的),這樣下次next或hasNext都是本地調用了。當這組資料被遍曆完畢,hasNext會導致再次去訪問資料庫,直到所有結果被返回。

【遊標的操作】

上面提到了,當獲得遊標後,我們可以先對遊標進行處理後,再讓訪問資料庫的動作按照我們的意願發生。這裡有3個函數可以在處理遊標時使用:limit、skip、sort。limit是限制遊標返回的數量,指定了上限;skip是忽略前面的部分文檔,如果文檔總數量小於忽略的數量,則返回空集合;sort對得到的子集合進行排序,可以按照多個鍵進行正反排序。對遊標的操作有一個技巧就是,操作遊標的函數返回的都是遊標,所以可以組成方法鏈調用,如下:

> db.fruitprice.find();{ "_id" : ObjectId("50226b4c3becfacce6a22a5b"), "apple" : 10, "banana" : 6, "pear" : 3 }{ "_id" : ObjectId("50226ba63becfacce6a22a5c"), "apple" : 10, "watermelon" : 3, "pear" : 3 }{ "_id" : ObjectId("5023a1db7dceac1a6dacb0b7"), "apple" : 8, "orange" : 4, "tomato" : 3 }{ "_id" : ObjectId("5023a1eb7dceac1a6dacb0b8"), "apple" : 9, "orange" : 5, "grape" : 12 }{ "_id" : ObjectId("5023a2037dceac1a6dacb0b9"), "melon" : 7, "orange" : 3, "grape" : 11 }> db.fruitprice.find().sort({"apple":1, "banana":-1});{ "_id" : ObjectId("5023a2037dceac1a6dacb0b9"), "melon" : 7, "orange" : 3, "grape" : 11 }{ "_id" : ObjectId("5023a1db7dceac1a6dacb0b7"), "apple" : 8, "orange" : 4, "tomato" : 3 }{ "_id" : ObjectId("5023a1eb7dceac1a6dacb0b8"), "apple" : 9, "orange" : 5, "grape" : 12 }{ "_id" : ObjectId("50226b4c3becfacce6a22a5b"), "apple" : 10, "banana" : 6, "pear" : 3 }{ "_id" : ObjectId("50226ba63becfacce6a22a5c"), "apple" : 10, "watermelon" : 3, "pear" : 3 }> db.fruitprice.find().skip(1).limit(3).sort({"apple":1, "banana":-1});{ "_id" : ObjectId("5023a1db7dceac1a6dacb0b7"), "apple" : 8, "orange" : 4, "tomato" : 3 }{ "_id" : ObjectId("5023a1eb7dceac1a6dacb0b8"), "apple" : 9, "orange" : 5, "grape" : 12 }{ "_id" : ObjectId("50226b4c3becfacce6a22a5b"), "apple" : 10, "banana" : 6, "pear" : 3 }>

上述,共執行了3次查詢:

第一次只執行了find函數,返回了一個集合,沒有順序。

第二次,我們讓其按照鍵"apple" 鍵"banana"排序,鍵"apple"升序(>0的數字),鍵“banana”降序(<0的數字),即先按找鍵“apple”升序排,對於鍵“apple”相等的文檔,則按照鍵“banana”降序排。我們看到,按照這種方式排,沒有鍵“apple”(即鍵“apple”值為null)的文檔排在了第一位,這是在MongoDB中,針對相同鍵不同類型值有一個預設順序,我們後面會提到。

第三次,我們使用了三個函數來設定遊標。這三個函數的關係是,在資料庫伺服器端,先執行sort,然後再排好序的文檔上執行skip,最後按照limit設定的最大數量返迴文檔子集即可。

【相同鍵不同類型值的比較順序】

按照鍵排序時,MongoDB中對於鍵並不會強制其值是什麼類型,我們在實際中也會遇到同一個鍵,一個文檔中為串在另一個文檔中為數字,這種文檔在排序時是任何進行的呢。MongoDB中,有一個預先定義的順序,從小到大,依次為:

(1):最小值

(2):null

(3):數字(整型,長整型,雙精確度)

(4):字串

(5):對象/文檔

(6):數組

(7):位元據

(8):對象ID

(9):布爾值

(10):日期型

(11):時間戳記

(12):Regex

(13):最大值

在上述第二種查詢中,我們按鍵“apple”查,在一個文檔中缺少這個鍵,即在這個文檔中這個鍵的值為null,在其他文檔中該鍵的值都是數字,按照上述順序,缺少這個鍵的文檔按升序排理應排在前面。

【避免使用skip略過大量結果】

使用skip略過少量文檔效率不會有什麼影響,如果略過大量結果,則可能會產生效能瓶頸。對於skip,我們通常的應用可能是在分頁時。對於分頁,我們有兩種方式來應對:

1. 將分頁的處理放在應用程式層,即將資料全部查出,然後在應用程式層處理分頁顯示。這就是通常所說的偽分頁。

2. 如果分頁必須在資料庫端進行,這通常是資料量太大的情況。這時,我們先嘗試使用skip操作,如果出現效能瓶頸,我們只能根據一個排序鍵,在擷取下頁資料時,首先根據上一頁最後一個文檔中該鍵的值來查詢文檔,最後排序截取即可。這樣就可以避免使用skip。

【進階查詢選項】

查詢分為普通查詢和封裝查詢,我們上面示範的各種查詢方式都是普通查詢, 如下我們再示範一個:

> db.fruitprice.find({"apple":10}).sort({"banana":1});{ "_id" : ObjectId("50226ba63becfacce6a22a5c"), "apple" : 10, "watermelon" : 3, "pear" : 3 }{ "_id" : ObjectId("50226b4c3becfacce6a22a5b"), "apple" : 10, "banana" : 6, "pear" : 3 }>

上述查詢我們轉換為封裝形式的寫法是:

> db.fruitprice.find({"$query" : {"apple" : 10}, "$orderby" : {"banana" : 1}});{ "_id" : ObjectId("50226ba63becfacce6a22a5c"), "apple" : 10, "watermelon" : 3, "pear" : 3 }{ "_id" : ObjectId("50226b4c3becfacce6a22a5b"), "apple" : 10, "banana" : 6, "pear" : 3 }>

我們的所有查詢在發送到資料庫端時,都被提前轉換成了封裝形式。封裝形式就是額外使用了一些鍵,如上述的"$query","$orderby"。我們還有如下一些有用的鍵可用:

1. $maxscan : integer  指定查詢時最多掃描文檔的數量

2. $min : document   查詢的開始條件

3. $max : document   查詢的結束條件

4. $hint : document  指定伺服器使用哪些索引進行查詢

5. $explain : boolean 擷取查詢細節,如用到的索引,結果數量,耗時等,類似於關聯式資料庫這邊查看執行計畫。並不會真正執行查詢

6. $snapshot : boolean 確保查詢的結果是在查詢執行那一刻的一致快照。這個在後面還會提到。

【擷取一致結果】

我們從MongoDB中擷取到資料後,通常會執行這種操作:對文檔進行處理後,即時地更新到資料庫中。這時對於大量文檔的情況有可能產生一個問題,我來描述一下:前面提到了,我們從資料庫端調用遊標的hasNext時,資料庫預設會返回100條文檔給我們,我們開始操作。假設對於一個文檔,我們增大了其大小,並且超高了MongoDB為文檔設定的預留地區,這時我們將這條文檔更新到資料庫中,資料庫沒法將其放置在其原始位置上,只能將其移動,通常會移動到集合末尾。這樣我們再次擷取文檔時,有可能又得到這條以被修改的文檔。。

應對這個問題,我們的方法就是對查詢結果進行快照。如果使用了上面提到的“$snapshop”選項,查詢就是針對不變的集合視圖啟動並執行。這點我們只是描述一下,實際情況中可以不用擔心了,因為MongoDB中,所有返回一組的查詢實際都進行了快照。

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.