2015-07-01 78 views
1

如何爲通過非唯一字段排序的查詢分頁?對於集合中的例子文檔可(由s分類:1,然後_id:-1):對一個集合進行排序和分頁

{_id: 19, s: 3}, 
{_id: 17, s: 3}, 
{_id: 58, s: 4}, 
// etc... 

有一個微不足道的限制/跳過工作方法......慢。

是否有可能使用類似:

db.collection.find() 
    .sort({s:1, _id:-1}) 
    .min({s:3, _id:17}) // this does not work as wanted! 
    .limit(2); 

檢索

{_id: 17, s: 3}, 
{_id: 58, s: 4} 

回答

3

如果您想通過「頁碼」,那麼你幾乎套牢後,你對你的鍵排序結果應用.limit().skip()方法進行分頁。您可能已經做了一些閱讀,發現它「效率不高」,主要是因爲「跳過」「n」結果以達到某個頁面的成本。

但原理是在你需要它的聲音:

db.collection.find().sort({ "s": -1, "_id": 1 }).skip(<page-1>).limit(<pageSize>) 

只要你只需要在你的頁面「前進」有一個更快的替代方案有,也是「分類」結果的工作。

關鍵是要保留對「s」的「最後看到」值的引用,然後一般列出_id值,直到「s」的值發生變化。所以多帶幾個文件證明,已經被排序用於演示目的:

{ "_id": 1, "s": 3 }, 
{ "_id": 2, "s": 3 }, 
{ "_id": 3, "s": 3 }, 
{ "_id": 4, "s": 2 }, 
{ "_id": 5, "s": 1 }, 
{ "_id": 6, "s": 1 }, 

爲了得到「第一頁」的「兩化」導致你的第一個查詢很簡單:

db.collection.find().sort({ "s": -1, "_id": 1}).limit(2) 

而是遵循通過到處理的文件:

var lastVal = null, 
    lastSeen = []; 

db.collection.find().sort({ "s": -1, "_id": 1}).limit(2).forEach(function(doc) { 
    if (doc.s != lastVal) { // Change when different 
     lastVal = doc.s; 
     lastSeen = []; 
    } 
    lastSeen.push(doc._id);  // Push _id onto array 
    // do other things like output 
}) 

所以對第一次迭代的lastVal值將是3lastSeen將包含文件_id中的數值[1,2]。 這些東西,你會存儲在像等待下一頁請求的用戶會話數據。

根據您的要求進行設置,那麼您發出的下一個頁面如下:

var lastVal = 3, 
    lastSeen = [1,2]; 

db.collection.find({ 
    "_id": { "$nin": lastSeen }, 
    "s": { "$lte": lastVal } 
}).sort({ "s": -1, "_id": 1}).limit(2).forEach(function(doc) { 
    if (doc.s != lastVal) { // Change when different 
     lastVal = doc.s; 
     lastSeen = []; 
    } 
    lastSeen.push(doc._id);  // Push _id onto array 
    // do other things like output 
}) 

,詢問那的「S」兩個選擇需要從一個值「小於或等於」開始(因爲)的lastVal記錄,並且「_id」字段不能包含記錄在lastSeen中的值。

產生的下一個頁面是:

{ "_id": 3, "s": 3 }, 
{ "_id": 4, "s": 2 }, 

但現在,如果你遵循的邏輯lastVal當然2lastSeen現在只有單一的數組元素[4]。由於下一個查詢只需要將2作爲較小或相等的值,因此不需要保留先前看到的其他「_id」值,因爲它們不在該選擇內。

,然後處理只是如下操作:

var lastVal = 2, 
    lastSeen = [2]; 

db.collection.find({ 
    "_id": { "$nin": lastSeen }, 
    "s": { "$lte": lastVal } 
}).sort({ "s": -1, "_id": 1}).limit(2).forEach(function(doc) { 
    if (doc.s != lastVal) { // Change when different 
     lastVal = doc.s; 
     lastSeen = []; 
    } 
    lastSeen.push(doc._id);  // Push _id onto array 
    // do other things like output 
}) 

所以按照邏輯的這種模式,你可以在「商店」從你的「previousc網頁」結果發現,這些信息「前進」在搜索結果中很有效率的。

但是,如果您需要跳轉到「第20頁」或類似的操作類型,那麼你堅持.limit().skip()。這種方式速度較慢,但​​這取決於你能忍受的。

+0

很好的解決方案,@ blakes-seven你能解釋爲什麼當提取第二頁時,你正在使用lastSeen = [2]而不是[4]? –

-1
db.t1.drop() 
db.t1.save({_id:19, s:3}) 
db.t1.save({_id:17, s:3}) 
db.t1.save({_id:58, s:4}) 

db.t1.find().sort({s:1, _id:-1}).skip(1).limit(2) 

--Result 
{ "_id" : 17, "s" : 3 } 
{ "_id" : 58, "s" : 4 } 

- $

相關問題