2016-04-25 48 views
15

我想了解如何使用MongoDB的排序文件字母(又稱自然排序,排序爲人類)在MongoDB中

做到這一點

我有一個名稱爲「文件1」,「文件2」,「file22文件「,」file11「(名稱可以是任何東西,沒有特定的模式) 我運行查詢以獲取按名稱排序的所有文檔,並且結果與預期不符。

> db.mydata.find().sort({"name":1});                               
{ "_id" : ObjectId("571e5a787e88d30b20b7857c"), "name" : "file1" }                        
{ "_id" : ObjectId("571e5a8c7e88d30b20b7857d"), "name" : "file11" }                       
{ "_id" : ObjectId("571e5a977e88d30b20b7857f"), "name" : "file2" }                        
{ "_id" : ObjectId("571e5a937e88d30b20b7857e"), "name" : "file22" } 

什麼是預期的是(字母/自然順序)

{ "_id" : ObjectId("571e5a787e88d30b20b7857c"), "name" : "file1" }                        
{ "_id" : ObjectId("571e5a977e88d30b20b7857f"), "name" : "file2" }                       
{ "_id" : ObjectId("571e5a8c7e88d30b20b7857d"), "name" : "file11" } 
{ "_id" : ObjectId("571e5a937e88d30b20b7857e"), "name" : "file22" } 

按我的發現,還有其他的方法來有點像使用aggregate + $project$meta: "textScore",但我沒有那麼成功遠。

UPDATE: 這個問題的一個應用:由名稱文件夾進行排序/文件 Windows Explorer, Folders sorted by Name

+0

不個個都'在他們file'共同?? – kryshna

+0

不,@ kryshna,這是一組簡化的數據。 – 6220119

+4

我不知道如何將file1

回答

8

的MongoDB不提供一種方式來做到這一點開箱即用,但你仍然有兩種選擇:

首先是使用Array.prototype.sort方法對數組結果進行排序的客戶端處理。

db.mydata.find().toArray().sort((a, b) => { 
    var x = Number(a.name.match(/\d+/g)[0]); 
    var y = Number(b.name.match(/\d+/g)[0]); 
    return x === y ? 0 :(x < y ? -1 : 1); 
}) 

第二這是我建議你做的是你的正常化有額外的域,按住數字在「名稱」爲整數,並使用該值你的文檔進行排序文件。這意味着,您需要更新文檔以添加該字段,並且最好的方法是使用$set更新運算符和"bulk operations"以獲得最大效率。也就是說,從MongoDB服務器版本3.2開始,您需要使用collection.bulkWrite方法來完成此操作。

var requests = []; 

db.mydata.find({}, { "name": 1 }).forEach(doc => { 
    var fileId = Number(doc.name.match(/\d+/g)[0]); // return number from "name" value 
    requests.push({ 
     "updateOne": { 
      "filter": { "_id": doc._id }, 
      "update": { "$set": { "fileId": fileId } } 
     } 
    }); 
    // Execute per 1000 operations and re-init the requests queue 
    if(requests.length === 1000) 
     db.mydata.bulkWrite(requests); 
}) 

// Clean up queues 
if (requests.length > 0) 
    db.mydata.bulkWrite(requests); 

MongoDB的服務器版本2.6您需要使用現在已經過時Bulk API。

var bulk = db.mydata.initializeUnorderedBulkOp(); 
var count = 0; 

db.collection.find({}, { "name": 1 }).forEach(function(doc) { 
    var fileId = Number(doc.name.match(/\d+/g)[0]); 
    bulk.find({"_id": doc._id}).updateOne({ 
     "$set": { "fileId": fileId } 
    }); 
    count++; 
    if (count % 1000 === 0) { 
     bulk.execute(); 
     bulk = db.mydata.initializeUnorderedBulkOp(); 
    } 
}) 

if (count > 0) 
    bulk.execute(); 

MongoDB的服務器版本2.4起,你需要不同的方法。

db.collection.find({}, { "name": 1 }).forEach(function(doc) { 
    var fileId = Number(doc.name.match(/\d+/g)[0]); 
    db.collection.update(
     { "_id": doc._id }, 
     {"$set": { "fileId": fileId } } 
    ); 
}) 

之後的任何這種操作的,您的文檔現在看起來是這樣的:

{ "_id" : ObjectId("571e5a787e88d30b20b7857c"), "name" : "file1", "fileId" : 1 } 
{ "_id" : ObjectId("571e5a8c7e88d30b20b7857d"), "name" : "file11", "fileId" : 11 } 
{ "_id" : ObjectId("571e5a977e88d30b20b7857f"), "name" : "file2", "fileId" : 2 } 
{ "_id" : ObjectId("571e5a937e88d30b20b7857e"), "name" : "file22", "fileId" : 22 } 

現在,您可以輕鬆地排序使用.sort方法您的文檔。

db.mydata.find({}, { "name": 1 }).sort({ "fileId": 1 }) 

產生以下結果:

{ "_id" : ObjectId("571e5a787e88d30b20b7857c"), "name" : "file1" } 
{ "_id" : ObjectId("571e5a977e88d30b20b7857f"), "name" : "file2" } 
{ "_id" : ObjectId("571e5a8c7e88d30b20b7857d"), "name" : "file11" } 
{ "_id" : ObjectId("571e5a937e88d30b20b7857e"), "name" : "file22" } 
+0

瞭解有關批量更新操作的更多信息。 如果我們能夠找出排序值的模式,引入額外字段是一種方法。但是,在這種情況下,該值可以是任何內容,如普通字符串,IP地址,... 關於客戶端方法,如果我們執行分頁,將會出現問題。除非您可以將所有數據返回給客戶端(這可能會產生性能問題),否則這種方法不會產生預期結果。 – 6220119

+0

@ 6220119正如我所說的正常化肯定是要走的路,因爲客戶端操作會導致應用程序的性能下降。找到一個模式應該是一個問題,因爲「名稱」保存了相同類型的值。例如對於常規字符串,您可以按照字符串的長度按字母順序對文檔進行排序,這意味着您的額外字段將保存長度。但這絕對是解決您的問題的方法。 – styvane

+0

按長度排序,然後按字母順序排列不會產生正確的結果。見http://imgur.com/wPR39Mw獲取一些靈感。 而這個樣子又成了另一個問題?如何使用mongoDB存儲字符串值以進行高性能的排序操作? – 6220119