2016-01-31 6 views
5

Ho due modelli, uno è utentePosso usare il popolamento prima di aggregarlo in mangusta?

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

e l'altro è il punteggio registrato più volte tutti i giorni per tutti gli utenti

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

Vorrei fare un po 'di query/calcoli sulla punteggio per alcuni utenti che soddisfano requisiti specifici, ad esempio vorrei calcolare la media del punteggio per tutti gli utenti maggiore di 20 giorni per giorno.

Il mio pensiero è che in primo luogo fare il popolano sui punteggi per popolare le età dell'utente e quindi fare il aggregata dopo.

Qualcosa di simile

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

E 'ok per utilizzare popolare prima di aggregazione? O prima trovo tutti gli userID che soddisfano i requisiti e li salvano in un array e quindi usano $ in per abbinare il documento del punteggio?

risposta

8

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.

+0

se userList è un array di objectId devi convertirli in stringhe –

+0

sei un ragazzo intelligente ma 'foriegnField' dovrebbe essere' foreignField' penso. nessun grosso problema –

Problemi correlati