2016-04-25 39 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