2016-06-29 13 views
5

Ich habe einige items Sammlung wie folgt aus:MongoDB - Aggregat und verketten Ergebnisse Gruppe

[ 
    { 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'} 
] 

Ich möchte nach Kategorien finden und Filterergebnisse. Ich habe Mongo query:

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

Also es gibt mir dies:

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

Was will ich erreichen ist:

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

Ist das möglich in reiner Mongo Abfrage?

Antwort

2

Mit dem Aggregation-Framework werden Sie einen Mechanismus benötigen, um die Zeichenfolge categories in eine bestimmte Menge aufzuteilen, aber ein solcher Operator existiert noch nicht; Der nächste Operator ist der Operator substr, bei dem der Index der Indexposition und die angegebene Anzahl von Zeichen für die extrahierte Teilzeichenfolge bekannt sein müssen, was nahezu unmöglich wäre. Daher die Empfehlung, die Kategorien als Array unterschiedlicher Kategorien zu speichern.

- EDIT -

Sollten Sie das categories Feld behalten wollen wie dann würde ich vorschlagen, dass Sie ein zusätzliches Feld, das die Liste der Kategorien speichert, dann können Sie die Aggregation Pipeline laufen auf diesem Feld, um das gewünschte Ergebnis zu erhalten.

Nehmen wir ein Beispiel verwenden, um die Ansätze oben zu zeigen:

Ändern des Schemas

a) Wenn MongoDB v3.0 mit oder unter:

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) Bei Verwendung von MongoDB v3.2.X oder höher:

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

Ausführen der Aggregation auf neue Schema

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

wir 'split (";") verwenden könnte' und 'trim' für eine kleine Reinigung. – profesor79

+0

Leider kann ich kein Feld in dieser Sammlung ändern und ich muss 'categories' als eine Zeichenfolge beibehalten. – zucker

+0

Erstellen Sie dann ein zusätzliches Feld, das den eindeutigen Satz von Kategorien speichert, und Sie können das ursprüngliche Kategorienfeld unverändert lassen. Wenn Sie die Aggregationspipeline ausführen, können Sie '$ abwickeln' und nach dem neuen Feld gruppieren. – chridam

Verwandte Themen