2015-01-01 9 views
5

Alcuni campi nei miei documenti MongoDB simile a questa:Come posso appiattire i doppi array in mongoDB?

{ 
... 
Countries: [["Spain", "France"]] 
... 
} 

O questo:

{ 
... 
Countries: [["Spain"],["Russia", "Egypt"]] 
... 
} 

Quello che voglio fare è quello di trasformare [["Spain", "France"]] in ["Spain", "France"] e [["Spain"],["Russia", "Egypt"]] in ["Spain", "Russia", "Egypt"], simile ad usare il flatten metodo in Ruby.

C'è un modo per appiattire gli array in mongoDB? Ho bisogno di appiattire gli array in tutti i documenti dell'intera collezione, non solo in un singolo documento, se questo è importante, anche i valori e la loro quantità in array variano tra i documenti.

Sto anche usando Ruby come driver per mongo, quindi un metodo che utilizza un driver Ruby sarebbe anche utile per me.

risposta

0

I dati relativi ai Paesi non sono in un buon formato, pertanto è possibile considerare di convertirli. Questo è uno script per appiattire la matrice in campo Paesi e salvarla i documenti di origine che è possibile eseguire in un guscio di Mongo:

function flattenArray(inArr) { 
    var ret = []; 
    inArr.forEach(function(arr) { 
     if (arr.constructor.toString().indexOf("Array") > -1) { 
      ret = ret.concat(flattenArray(arr)); 
     } else { 
      ret.push(arr);     
     } 
    }); 
    return ret; 
} 


db.collection.find({ 
    'Countries': { 
    '$exists': true 
    } 
}).forEach(function(doc){ 
    doc.Countries = flattenArray(doc.Countries); 
    db.collection.save(doc); 
}); 
3

Prova questo:

db.test2.aggregate([ 
    {"$unwind" : "$Countries"}, 
    {"$unwind" : "$Countries"}, 
    {$group : { _id : '$_id', Countries: { $addToSet: "$Countries" }}}, 
]).result 
3

È necessario eseguire un'operazione di aggregazione con due unwind tappe e un singolo palco. La regola base è che ti rilassi tante volte quanto il livello di profondità del nido. Qui il livello di nidificazione è 2, quindi ci rilassiamo due volte.

collection.aggregate([ 
{$unwind => "$Countries"}, 
{$unwind => "$Countries"}, 
{$group => {"_id":"$_id","Countries":{$push => "$Countries"}}} 
]) 

La prima $unwind fase produce il risultato:

{ 
     "_id" : ObjectId("54a32e0fc2eaf05fc77a5ea4"), 
     "Countries" : [ 
       "Spain", 
       "France" 
     ] 
} 
{ 
     "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"), 
     "Countries" : [ 
       "Spain" 
     ] 
} 
{ 
     "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"), 
     "Countries" : [ 
       "Russia", 
       "Egypt" 
     ] 
} 

La seconda $unwind fase appiattisce ulteriormente la Countries matrice:

{ "_id" : ObjectId("54a32e0fc2eaf05fc77a5ea4"), "Countries" : "Spain" } 
{ "_id" : ObjectId("54a32e0fc2eaf05fc77a5ea4"), "Countries" : "France" } 
{ "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"), "Countries" : "Spain" } 
{ "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"), "Countries" : "Russia" } 
{ "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"), "Countries" : "Egypt" } 

Ora finali $group gruppi stadio i record in base al _id e accumula i nomi dei paesi in un singolo array.

{ 
     "_id" : ObjectId("54a32e4ec2eaf05fc77a5ea5"), 
     "Countries" : [ 
       "Spain", 
       "Russia", 
       "Egypt" 
     ] 
} 
{ 
     "_id" : ObjectId("54a32e0fc2eaf05fc77a5ea4"), 
     "Countries" : [ 
       "Spain", 
       "France" 
     ] 
} 

Se si desidera mantenere altri campi nel documento, allora è necessario specificare esplicitamente i nomi dei campi diversi dal campo del paese, (campo1, campo2, ecc ..), utilizzando l'operatore $first. È possibile scrivere/sovrascrivere una raccolta specificando il nome della raccolta nella fase $out.

collection.aggregate([ 
{$unwind => "$Countries"}, 
{$unwind => "$Countries"}, 
{$group => {"_id":"$_id","Countries":{$push => "$Countries"}, 
      "field1":{$first => "$field1"}}}, 
{$out => "collection"} 
]) 

è necessario specificare in modo esplicito i campi in modo che non si ottiene un ridondante Countries campo.

È possibile utilizzare la variabile di sistema $$ROOT per memorizzare l'intero documento, ma che renderebbero il redundant.One Countries campo al di fuori del doc e uno all'interno della doc.

collection.aggregate([ 
{$unwind => "$Countries"}, 
{$unwind => "$Countries"}, 
{$group => {"_id":"$_id","Countries":{$push => "$Countries"}, 
      "doc":{$first => "$$ROOT"}}}, 
{$out => "collection"} 
]) 
+0

ho scritto il risultato 3 secondi prima di quanto si;), era solo interessante. comunque +1 – Disposer

+0

@Disposer Sì, mi hai battuto.Ci è voluto tempo per formattare i risultati :), +1 per la tua aggregazione. :) – BatScream

+0

Grazie. Posso chiarire ancora due domande. Ho notato che questo operatore di gruppo restituisce solo i campi specificati (id e paesi). È possibile includere tutti gli altri campi senza specificare manualmente ciascuno di essi. Poiché ci sono molti altri campi, e il db non è ancora ottimizzato, ci sono anche campi unici condivisi solo da una porzione molto piccola di documenti e sarebbe dannatamente difficile cercare e specificarli tutti. E in secondo luogo, forse una domanda stupida: come sovrascrivo la raccolta originale con l'output aggregato? – Anton