2015-02-27 12 views
6

Mam następujący dokument MongoDB:MongoDB: Aktualizacja średnio w dokumencie z 2 zagnieżdżonych tablic

{ 
    _id: ObjectId(), 
    company_name: "Name", 
    registered: 2/21/2015 2:00, 
    trucks: [ 
     { 
      truck_id: "TEB7622", 
      weight: 88.33, 
      capacity: 273.333, 
      length: 378.333, 
      width: 377.383, 
      average_grade: 2.5, 
      grades: [ 
       { 
        grade_number: 4, 
        timestamp: 2/21/2015 2:00 
       } 
      ] 
     }, 
     { 
      truck_id: "TEB5572", 
      weight: 854.33, 
      capacity: 2735.333, 
      length: 378.333, 
      width: 37.383, 
      average_grade: 3.8, 
      grades: [ 
       { 
        grade_number: 4, 
        timestamp: 2/21/2015 2:00 
       } 
      ] 
     } 
    ] 

} 

Chcę zaktualizować każdy wózek na average_grade przez dodanie wszystkich grade_numbers. Problem, który mam, polega na tym, że próbuję dodać grade_numbers w tablicy w tablicy. Próbowałem używać $unwind, aby odwijać zarówno tablice ciężarówek, jak i tablice ocen.

to zapytanie Próbowałem, używając:

db.col.aggregate([ 
    {$unwind: "$trucks"}, 
    {$unwind: "$trucks.grades"}, 
    { $project: { 
     "_id": "$trucks.truck_id", 
     "trucks.average_grade": { $avg: { $sum: "trucks.grades.grade_number"} } 
     } 
    }]) 

muszę dodać coś więcej do mojego zapytania? Chcę zaktualizować WSZYSTKIE trucks.average_grades, ponieważ jest ich dużo w dokumencie, który próbuję zaktualizować.

+0

Nie można używać agregacji do aktualizacji dokumentu – styvane

+0

@ Michael9 Więc muszę przeprowadzić dwa kwerendy w prawo? Jeden, aby uzyskać średnie i jeden, aby zaktualizować średnie? Czy muszę przechowywać średnie na innej "zmiennej"? – suecarmol

+0

Podwójne tablice zagnieżdżone nie są dobrym pomysłem, jeśli potrzebujesz zapytać o dane w nich zawarte. Są w porządku, jeśli dane są tylko do wyświetlenia. Być może powinieneś ponownie rozważyć model danych? Może każda ciężarówka powinna być własnym dokumentem, z polem "company_id"? Lub każdy ocenia swój własny dokument? Nie chcesz polegać na podwójnym rozwijaniu potoku agregacji dla normalnego zapytania, więc nie polecałbym poniższego rozwiązania, chyba że zapytanie jest rzadkie. – wdberkeley

Odpowiedz

6

Nie można użyć aggregation, aby zaktualizować dokument, ale można go zdecydowanie użyć, aby uzyskać dane, których chcesz użyć do aktualizacji. Po pierwsze, zauważyłem, że brakuje około {} wokół twojego obiektu grade wewnątrz tablicy grades. Możesz dokładnie sprawdzić, czy struktura dokumentu jest tak samo napisana. Po drugie, istnieje kilka problemów z zapytaniem o agregację.

  1. Operator $avg działa wewnątrz $group klauzuli, a nie $project.
  2. Podczas korzystania z $avg, nie trzeba używać $sum.
  3. Chcesz średnio trucks.grades.grade.grade_number, która faktycznie zawiera wartość liczbową oceny. To znaczy, że brakuje grade między grades a grade_number.

Jeśli rozwiązać te problemy, masz zapytanie podobny do następującego:

db.col.aggregate([ 
    { "$unwind": "$trucks" }, 
    { "$unwind": "$trucks.grades" }, 
    { "$group": 
     { 
      "_id": "$trucks.truck_id", 
      "average_grade": { "$avg": "$trucks.grades.grade_number" } 
     } 
    } 
]); 

dla dokumentu próbkę, która zwraca:

{ "_id" : "TEB5572", "average_grade" : 4 } 
{ "_id" : "TEB7622", "average_grade" : 4 } 

Teraz można korzystać z tych informacji zaktualizuj pole average_grade. Jeśli używasz MongoDB w wersji 2.6 lub nowszej, metoda aggregate zwróci kursor. Możesz iterować po kursorze i odpowiednio aktualizować dokumenty.

W tym przykładzie szukam dokumentów, które mają konkretną truck_id w swojej macierzy trucks i kontynuuję aktualizację average_grade z wersją obliczoną przez zapytanie agregacyjne. Możesz go zmodyfikować zgodnie ze swoimi potrzebami. W połączeniu z zapytaniem o agregację kod wygląda następująco.

// Get average grade for each truck and assign results to cursor. 
var cur = db.col.aggregate([ 
    { "$unwind": "$trucks" }, 
    { "$unwind": "$trucks.grades" }, 
    { "$group": 
     { 
      "_id": "$trucks.truck_id", 
      "average_grade": { "$avg": "$trucks.grades.grade_number" } 
     } 
    } 
]); 

// Iterate through results and update average grade for each truck. 
while (cur.hasNext()) { 
    var doc = cur.next(); 
    db.col.update({ "trucks.truck_id": doc._id }, 
        { "$set": { "trucks.$.average_grade": doc.average_grade }}, 
        { "multi": true}); 
} 
+0

Tak, to było to. Wielkie dzięki. – suecarmol

+0

Cieszę się, że pomogło! –