2016-01-31 9 views
5

mam dwa modele, jeden użytkownikCzy mogę użyć wypełnienia przed agregatem w mangusty?

userSchema = new Schema({ 
    userID: String, 
    age: Number 
}); 

a drugi wynik nagrany kilka razy codziennie dla wszystkich użytkowników

ScoreSchema = new Schema({ 
    userID: {type: String, ref: 'User'}, 
    score: Number, 
    created_date = Date, 
    .... 
}) 

Chcę zrobić kilka zapytań/obliczeń na wynik dla niektórych użytkowników spełniających określone wymagania, na przykład chciałbym obliczyć średnią wyniku dla wszystkich użytkowników, którzy mają więcej niż 20 dni w ciągu dnia.

Moja myśl jest taka, że ​​po pierwsze wykonać zapełnić na żywo, aby wypełnić wiek użytkownika, a następnie wykonaj łączną po tym.

Coś

Score. 
    populate('userID','age'). 
    aggregate([ 
     {$match: {'userID.age': {$gt: 20}}}, 
     {$group: ...}, 
     {$group: ...} 
    ], function(err, data){}); 

to jest ok używać wypełnić przed kruszywa? Czy najpierw znajduję wszystkie ID użytkownika spełniające wymóg i zapisuję je w tablicy, a następnie używam $ in, aby dopasować dokument wynikowy?

Odpowiedz

8

No nie można nazwać .populate() przed .aggregate(), i jest to bardzo dobry powód, dlaczego nie można. Ale są różne podejścia, które możesz podjąć.

Metoda ta działa "po stronie klienta", gdzie podstawowy kod faktycznie wykonuje dodatkowe kwerendy (lub dokładniej kwerendę $in) w celu "wyszukiwania" określonych elementów z przywoływanej kolekcji.

W przeciwieństwie do tego, jest operacją "po stronie serwera", więc zasadniczo nie można manipulować treścią "po stronie klienta", a następnie udostępniać te dane w etapach potoku agregacji później. To wszystko musi być obecne w kolekcji, w której pracujesz.

Lepsza metoda dostępna jest tutaj z MongoDB 3.2 i nowszymi, za pośrednictwem operacji agregacji potoku $lookup. Także chyba najlepiej poradzić z kolekcji User w tym przypadku w celu zawężenia wybór:

User.aggregate(
    [ 
     // Filter first 
     { "$match": { 
      "age": { "$gt": 20 } 
     }}, 
     // Then join 
     { "$lookup": { 
      "from": "scores", 
      "localField": "userID", 
      "foriegnField": "userID", 
      "as": "score" 
     }}, 
     // More stages 
    ], 
    function(err,results) { 

    } 
) 

To jest w zasadzie będzie to nowe pole „gola” w ramach przedmiotu User jako „tablica” przedmiotów które odpowiadają na „odnośnika” do innej kolekcji:

{ 
    "userID": "abc", 
    "age": 21, 
    "score": [{ 
     "userID": "abc", 
     "score": 42, 
     // other fields 
    }] 
} 

wynik jest zawsze tablicą, jako ogólna oczekiwane wykorzystanie jest „left join” ewentualnej „jeden do wielu” relacji. Jeśli nie zostanie dopasowany żaden wynik, jest to po prostu pusta tablica.

Aby użyć treści, po prostu pracuj z tablicą w jakikolwiek sposób. Na przykład możesz użyć operatora $arrayElemAt, aby uzyskać pojedynczy pierwszy element tablicy w przyszłych operacjach. A następnie możesz po prostu użyć zawartości jak normalnego osadzonego pola:

 { "$project": { 
      "userID": 1, 
      "age": 1, 
      "score": { "$arrayElemAt": [ "$score", 0 ] } 
     }} 

Jeśli nie masz MongoDB 3.2 dostępny, wówczas inna opcja przetwarzać zapytania ograniczony przez stosunki innej kolekcji jest, aby najpierw uzyskać wyniki z tej kolekcji, a następnie użyć $in filtrować na drugi:

// Match the user collection 
User.find({ "age": { "$gt": 20 } },function(err,users) { 

    // Get id list  
    userList = users.map(function(user) { 
     return user.userID; 
    }); 

    Score.aggregate(
     [ 
      // use the id list to select items 
      { "$match": { 
       "userId": { "$in": userList } 
      }}, 
      // more stages 
     ], 
     function(err,results) { 

     } 
    ); 

}); 

to poprzez uzyskanie listy poprawni użytkownicy z drugiej kolekcji do klienta, a następnie dostarczenie ich do innej kolekcji w zapytaniu, to jedyny sposób, aby to zrobić we wcześniejszych wersjach.

+0

jeśli userList jest tablicą objectId, czy musisz przekonwertować je na łańcuchy znaków –

+0

jesteś inteligentnym facetem, ale 'foriegnField' powinno być' foreignField', jak sądzę. nie ma sprawy –