2016-04-25 16 views
15

vorrei trovare il modo di farlo con MongoDBOrdina documento (ordinamento aka naturali, l'ordinamento per gli esseri umani) in ordine alfabetico in MongoDB

ho documenti con nomi come "file1", "file2", "file22 "," file11 "(il nome può essere qualsiasi cosa, non esiste uno schema particolare) Ho eseguito la query per ottenere tutti i documenti ordinati per nome e il risultato non è come previsto.

> 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" } 

Cosa ci si aspetta è (alfabetico/naturale ordine)

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

Come per la mia scoperta, ci sono altri modi per ordinare come l'utilizzo di aggregate + $project e $meta: "textScore", ma io non sono riusciti così lontano.

UPDATE: un'applicazione di questo problema: ordinare le cartelle/file di nomi Windows Explorer, Folders sorted by Name

+0

fa tutti loro hanno 'file' comune in loro ?? – kryshna

+0

No, @kryshna, questo è un insieme di dati semplificato. – 6220119

+4

Non sono sicuro di come 'file1

risposta

8

MongoDB non fornire un modo per fare questo, fuori dalla scatola, ma avete ancora due opzioni:

Il primo è un'elaborazione lato client che utilizza il metodo Array.prototype.sort per ordinare il risultato dell'array.

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

Il secondo che è quello che vi consiglio di fare è normalizzare i documenti con un campo aggiuntivo che contengono i le cifre nel campo "Nome" come intero e ordinare i documenti utilizzando quel valore. Ciò significa che sarà necessario aggiornare i documenti per aggiungere quel campo e il modo migliore per farlo è utilizzare l'operatore di aggiornamento $set e "bulk operations" per la massima efficienza. Detto questo, dal server MongoDB versione 3.2 è necessario utilizzare il metodo collection.bulkWrite per ottenere ciò.

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

Da server di MongoDB versione 2.6 è necessario utilizzare l'ormai deprecato 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(); 

Da versione server MongoDB 2.4 in poi è necessario un approccio diverso.

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

Dopo qualsiasi di questa operazione, i documenti ora simile a questa:

{ "_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 } 

Ora, si può facilmente risolvere i documenti utilizzando il metodo .sort.

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

che produce il seguente risultato:

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

È bello capire di più sulle operazioni di aggiornamento di massa. L'introduzione di campi aggiuntivi è la strada da percorrere se riusciamo a scoprire il modello del valore ordinato. Tuttavia, in questo caso, il valore può essere qualsiasi cosa, come stringhe normali, indirizzo IP, ... Per quanto riguarda l'approccio lato client, sarà un problema se si esegue l'impaginazione. A meno che non sia possibile restituire tutti i dati al lato client (che potrebbero creare problemi di prestazioni), questo approccio non produrrà risultati previsti. – 6220119

+0

@ 6220119 Come ho già detto, la normalizzazione è sicuramente la strada da percorrere perché l'operazione sul lato client causerà un calo delle prestazioni nell'applicazione. Anche trovare un pattern dovrebbe essere un problema perché il "nome" contiene il valore dello stesso tipo.Ad esempio con una stringa normale, è possibile ordinare i documenti in base alla lunghezza della stringa in ordine crescente e in ordine alfabetico, il che significa che il campo aggiuntivo terrà la lunghezza. Ma questa è sicuramente la soluzione al tuo problema. – styvane

+0

L'ordinamento in base alla lunghezza e quindi in ordine alfabetico non produce il risultato corretto. Vedi http://imgur.com/wPR39Mw per qualche ispirazione. E sembra che questa sia diventata un'altra domanda? Come usare mongoDB per memorizzare il valore di stringa per l'operazione di ordinamento performante? – 6220119

Problemi correlati