Nie, nie możesz wywołać .populate()
przed .aggregate()
i jest bardzo dobry powód, dla którego nie możesz. Ale można zastosować różne podejścia.
.populate()
metoda działa "po stronie klienta", gdzie bazowy kod faktycznie wykonuje dodatkowe zapytania (a dokładniej $in
query ), aby „wyszukać” określone elementy z kolekcji, do której się odwołuje.
Natomiast .aggregate()
jest operacją „po stronie serwera”, więc w zasadzie nie można manipulować treścią „po stronie klienta”, a następnie udostępnić te dane etapom potoku agregacji. Wszystko musi być obecne w kolekcji, na której operujesz.
Lepsze podejście jest dostępne w MongoDB 3.2 i nowszych, za pośrednictwem $lookup
działanie potoku agregacji. Również prawdopodobnie najlepiej obsługiwać z User
w tym przypadku w celu zawężenia wyboru:
User.aggregate(
[
// Filter first
{ "$match": {
"age": { "$gt": 20 }
}},
// Then join
{ "$lookup": {
"from": "scores",
"localField": "userID",
"foriegnField": "userID",
"as": "score"
}},
// More stages
],
function(err,results) {
}
)
Zasadniczo będzie to zawierać nowe pole "score" w User
obiekt jako „tablica” elementów, które zostały dopasowane podczas „wyszukiwania” do innej kolekcji:
{
"userID": "abc",
"age": 21,
"score": [{
"userID": "abc",
"score": 42,
// other fields
}]
}
Wynikiem jest zawsze tablica, ponieważ ogólne oczekiwane użycie to „lewe sprzężenie” możliwej relacji „jeden do wielu”. Jeśli żaden wynik nie zostanie dopasowany, jest to po prostu pusta tablica.
Aby wykorzystać zawartość, po prostu pracuj z tablicą w dowolny sposób. Na przykład możesz użyć $arrayElemAt
operatora, aby w przyszłych operacjach uzyskać tylko pierwszy element tablicy. A potem możesz po prostu użyć treści jak każdego normalnego osadzonego pola:
{ "$project": {
"userID": 1,
"age": 1,
"score": { "$arrayElemAt": [ "$score", 0 ] }
}}
Jeśli nie masz dostępnego MongoDB 3.2, inną opcją przetwarzania zapytania ograniczonego relacjami z innej kolekcji jest najpierw pobranie wyników z tej kolekcji, a następnie użycie $in
filtrować po drugim:
// 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) {
}
);
});
Zatem pobranie listy poprawnych użytkowników z innej kolekcji do klienta, a następnie przekazanie jej do innej kolekcji w zapytaniu jest jedynym sposobem, aby to się stało we wcześniejszych wydaniach.