2016-06-29 29 views
5

Mam items kolekcję takiego:MongoDB - kruszywo i złączyć wyniki do grupy

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

Chcę znaleźć i Wyszukane według kategorii. Stworzyłem zapytanie Mongo:

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

Więc to mi zwraca to:

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

Co chcę osiągnąć jest:

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

Czy jest to możliwe w czystej zapytania Mongo?

Odpowiedz

2

W ramach agregacji potrzebny będzie mechanizm podziału ciągu categories na odrębny zestaw, ale taki operator jeszcze nie istnieje; najbliższym, który uzyskasz, jest operator, który wymagałby znajomości indeksu pozycji indeksu i określonej liczby znaków dla ekstrahowanego podciągu, co byłoby prawie niemożliwe. Stąd zalecenie przechowywania kategorii jako tablicy odrębnych nazw kategorii.

- EDIT -

Jeżeli chcesz zachować pole categories jak to proponuję utworzyć dodatkowe pole, które przechowuje listę kategorii, a następnie można uruchomić rurociąg agregacji na tym polu, aby uzyskać pożądany wynik.

Użyjmy przykład wykazać powyższe podejścia:

Zmiana schematu

a) przypadku korzystania MongoDB v3.0 lub poniżej:

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) Jeśli używasz MongoDB v3.2.X lub nowszego:

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

Running agregację na nowego schematu

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

możemy użyć 'split (";")' i 'trim' dla małego czyszczenia. – profesor79

+0

Niestety, nie mogę zmodyfikować żadnego pola w tej kolekcji i muszę zachować 'categories' jako ciąg znaków. – zucker

+0

Następnie utwórz dodatkowe pole, które przechowuje odrębny zestaw kategorii i możesz zachować oryginalne kategorie w niezmienionym stanie. Po uruchomieniu potoku agregacji można wtedy "$ unwind" i grupować według nowego pola. – chridam