2016-06-29 37 views
5

ho qualche items raccolta in questo modo:MongoDB - aggregato e concatenare risultati al gruppo

[ 
    { name: 'item1', description: 'description #1', categories: 'cat_A; cat_B'}, 
    { name: 'item2', description: 'description #2', categories: 'cat_B'}, 
    { name: 'item3', description: 'description #3', categories: 'cat_C; cat_B'}, 
    { name: 'item4', description: 'description #4', categories: 'cat_B; cat_A'}, 
    { name: 'item5', description: 'description #5', categories: 'cat_B'}, 
    { name: 'item6', description: 'description #6', categories: 'cat_D'} 
] 

voglio trovare e filtrare i risultati per categorie. Ho creato interrogazione mongo:

db.getCollection('items') 
    .aggregate([ 
     { 
      $match: { 
       categories: {$in: [/cat_a/i, /cat_b/i]} 
      } 
     }, { 
      $group: { 
       _id: "$categories", 
       items: { $push: { name: "$name", description: '$description' } } 
      } 
     } 
    ]) 

Quindi mi restituisce questo:

result : [ 
    { 
     "_id" : "cat_B; cat_C", 
     "items" : [ 
      { 
       "name" : "item3", 
       "description" : "description #3" 
      } 
     ] 
    }, { 
     "_id" : "cat_B; cat_A", 
     "items" : [ 
      { 
       "name" : "item4", 
       "description" : "description #4" 
      } 
     ] 
    }, { 
     "_id" : "cat_B", 
     "items" : [ 
      { 
       "name" : "item2", 
       "description" : "description #2" 
      }, 
      { 
       "name" : "item5", 
       "description" : "description #5" 
      } 
     ] 
    }, { 
     "_id" : "cat_A; cat_B", 
     "items" : [ 
      { 
       "name" : "item1", 
       "description" : "description #1" 
      } 
     ] 
    } 
] 

Quello che voglio ottenere è:

result : [ 
    { 
     "_id" : "cat_A", 
     "items" : [ 
      { 
       "name" : "item1", 
       "description" : "description #1" 
      }, 
      { 
       "name" : "item4", 
       "description" : "description #4" 
      } 
     ] 
    }, { 
     "_id" : "cat_B", 
     "items" : [ 
      { 
       "name" : "item1", 
       "description" : "description #1" 
      }, 
      { 
       "name" : "item2", 
       "description" : "description #2" 
      }, 
      { 
       "name" : "item3", 
       "description" : "description #3" 
      }, 
      { 
       "name" : "item4", 
       "description" : "description #4" 
      }, 
      { 
       "name" : "item5", 
       "description" : "description #5" 
      } 
     ] 
    } 
] 

Che è possibile nella query Mongo puro?

risposta

2

Con il framework di aggregazione avrai bisogno di un meccanismo per dividere la stringa categories in un set distinto, ma tale operatore non esiste ancora; il più vicino si ottiene è l'operatore substr che richiederebbe conoscere l'indice della posizione dell'indice e il numero specificato di caratteri per la sottostringa da estrarre, il che sarebbe quasi impossibile. Da qui la raccomandazione di memorizzare le categorie come una matrice di nomi distinti di categorie.

- EDIT -

Se volete mantenere il campo categories come è poi vorrei suggerire di creare un campo aggiuntivo che memorizza l'elenco delle categorie, quindi è possibile eseguire la pipeline di aggregazione su quel campo per ottenere il risultato desiderato.

Facciamo un esempio per dimostrare gli approcci di cui sopra:

Modifica dello schema

a) Se si utilizza MongoDB v3.0 o al di sotto:

var bulk = db.items.initializeOrderedBulkOp(), 
    counter = 0; 

db.items.find({}).forEach(doc) { 
    var categoriesList = doc.categories.replace(/^\s+|\s+$/g,"").split(/\s*;\s*/); 
    bulk.find({ "_id": doc._id }) 
     .updateOne({ 
      "$set": { "categoriesList": categoriesList } 
     }); 
    counter++; 

    if (counter % 1000 == 0) { 
     bulk.execute(); 
     bulk = db.items.initializeOrderedBulkOp(); 
    } 
} 

if (counter % 1000 != 0) bulk.execute(); 

b) Se si utilizza MongoDB v3.2.X o successivo:

var cursor = db.items.find({}), 
    bulkUpdateOps = []; 

cursor.forEach(function(doc){ 
    var categoriesList = doc.categories.replace(/^\s+|\s+$/g,"").split(/\s*;\s*/); 
    bulkUpdateOps.push({ 
     "updateOne": { 
      "filter": { "_id": doc._id }, 
      "update": { "$set": { "categoriesList": categoriesList } } 
     } 
    }); 

    if (bulkUpdateOps.length == 1000) { 
     db.items.bulkWrite(bulkUpdateOps); 
     bulkUpdateOps = []; 
    } 
});   

if (bulkUpdateOps.length > 0) db.items.bulkWrite(bulkUpdateOps); 

esecuzione l'aggregazione sul nuovo schema

db.items.aggregate([ 
    { "$match": { "categoriesList": { "$in": ['cat_A', 'cat_B'] } } }, 
    { "$unwind": "$categoriesList" }, 
    { 
     "$group": { 
      "_id": "$categoriesList", 
      "items": { "$push": { "name": "$name", "description": '$description' } } 
     } 
    } 
]) 
+1

potremmo usare 'split (";")' e 'trim' per una piccola pulizia. – profesor79

+0

Sfortunatamente, non posso modificare alcun campo in questa raccolta e ho bisogno di mantenere 'categories' come stringa. – zucker

+0

Quindi creare un campo aggiuntivo che memorizza il gruppo distinto di categorie e mantenere il campo delle categorie originali così com'è. Quando si esegue la pipeline di aggregazione è possibile quindi '$ unwind' e raggruppare il nuovo campo. – chridam