No, non è possibile chiamare .populate()
prima .aggregate()
, e c'è una buona ragione per cui non si può. Ma ci sono diversi approcci che puoi adottare.
Il metodo .populate()
funziona "lato client" dove il codice sottostante esegue effettivamente query aggiuntive (o più precisamente una query $in
) per "cercare" gli elementi specificati dalla raccolta di riferimento.
Al contrario, .aggregate()
è un'operazione "lato server", quindi in pratica non è possibile manipolare il contenuto "lato client" e quindi disporre di tali dati per gli stadi della pipeline di aggregazione successivamente. Tutto deve essere presente nella collezione su cui stai operando.
Un approccio migliore qui è disponibile con MongoDB 3.2 e versioni successive, tramite l'operazione di pipeline di aggregazione $lookup
. Probabilmente anche migliore per gestire dalla raccolta User
in questo caso, al fine di restringere la selezione:
User.aggregate(
[
// Filter first
{ "$match": {
"age": { "$gt": 20 }
}},
// Then join
{ "$lookup": {
"from": "scores",
"localField": "userID",
"foriegnField": "userID",
"as": "score"
}},
// More stages
],
function(err,results) {
}
)
Questo è fondamentalmente andando a includere un nuovo campo "segnare" all'interno dell'oggetto User
come un "allineamento" di oggetti che abbinate sul "ricerca" all'altro raccolta:
{
"userID": "abc",
"age": 21,
"score": [{
"userID": "abc",
"score": 42,
// other fields
}]
}
il risultato è sempre un array, come l'uso previsto generale è un "sinistra join" di una possibile "uno a molti" relazione. Se nessun risultato corrisponde, allora è solo un array vuoto.
Per utilizzare il contenuto, è sufficiente lavorare con un array in qualsiasi modo. Ad esempio, è possibile utilizzare l'operatore $arrayElemAt
per ottenere il primo elemento singolo dell'array in qualsiasi operazione futura. E poi puoi semplicemente usare il contenuto come qualsiasi normale campo incorporato:
{ "$project": {
"userID": 1,
"age": 1,
"score": { "$arrayElemAt": [ "$score", 0 ] }
}}
Se non hai MongoDB 3.2 disponibili, quindi l'altra opzione per elaborare una query limitata dalle relazioni di un'altra raccolta è quello di ottenere prima i risultati di tale raccolta e quindi utilizzare $in
per filtrare al secondo:
// 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) {
}
);
});
Così da ottenere l'elenco dei gli utenti validi dall'altra raccolta al client e quindi l'inserimento dell'alimentazione in un'altra query in una query è il modo più semplice per ottenere che ciò accada nelle versioni precedenti.
se userList è un array di objectId devi convertirli in stringhe –
sei un ragazzo intelligente ma 'foriegnField' dovrebbe essere' foreignField' penso. nessun grosso problema –