2016-05-16 29 views
5

ho collezione in cui i documenti sono come:Restituisce l'ultimo valore "vero" per ogni gruppo

{ 
    _id: ObjectId(), 
    user: ObjectId(), 
    studentName: String, 
    createdAt: Date, 
    isAbondoned: boolean 
} 

esempio di documenti sono:

1-

{ 
    "_id" : ObjectId("56cd2d36a489a5b875902f0e"), 
    "user" : ObjectId("56c4cafabd5f92cd78ae49d4"), 
    "studentName" : "Aman", 
    "createdAt" : ISODate("2016-02-24T04:10:30.486+0000"), 
    "isAbandoned" : true 
} 

2 -

{ 
    "_id" : ObjectId("56cd2dcda489a5b875902fcd"), 
    "user" : ObjectId("56c4cafabd5f92cd78ae49d4"), 
    "studentName" : "Aman", 
    "createdAt" : ISODate("2016-02-24T04:13:01.932+0000"), 
    "isAbandoned" : false 
} 

3-

{ 
    "_id" : ObjectId("56cee51503b7cb7b0eda9c4c"), 
    "user" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
    "studentName" : "Rajeev", 
    "createdAt" : ISODate("2016-02-25T11:27:17.281+0000"), 
    "isAbandoned" : true, 
} 

Ora voglio trovare l'elenco degli studenti per i quali la loro 'isAbandoned' vale per il loro ultimo documento 'createdAt'.

uscita Richiesto per esempio di cui sopra è:

{ 
    "user" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
    "studentName" : "Rajeev" 
} 

perché per studentName "Aman" max (createdAt) è 2 ° documento e 'isAbandoned' è falso per questo.

risposta

3

Il modo migliore per farlo è utilizzare il framework di aggregazione. È necessario il $group i tuoi documenti da "utente" e restituire l'ultimo documento per ogni utente utilizzando l'operatore accumulatore $last ma per far funzionare tutto ciò, è necessaria una fase di classificazione preliminare utilizzando l'operatore di pipeline di aggregazione $sort. Per ordinare i tuoi documenti, devi considerare sia il campo "createdAt" che il campo "user".

L'ultima fase del pipeline è la fase $match in cui si selezionano solo gli ultimi documenti in cui "isAbandoned" è uguale a true.

db.students.aggregate([ 
    { "$sort": { "user": 1, "createdAt": 1 } }, 
    { "$group": { 
     "_id": "$user", 
     "last": { "$last": "$$ROOT" } 
    }}, 
    { "$match": { "last.isAbandoned": true } } 
]) 

che restituisce qualcosa di simile:

{ 
    "_id" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
    "last" : { 
     "_id" : ObjectId("56cee51503b7cb7b0eda9c4c"), 
     "user" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
     "studentName" : "Rajeev", 
     "createdAt" : ISODate("2016-02-25T11:27:17.281Z"), 
     "isAbandoned" : true 
    } 
} 

per ottenere il risultato atteso, abbiamo bisogno di utilizzare l'operatore $replaceRoot gasdotto a partire dal verion 3.4 per promuovere il documento incorporato al livello superiore

{ 
    $replaceRoot: { newRoot: "$last" } 
} 

Nella versione precedente, è necessario utilizzare l'operazione di pipeline di aggregazione $project per ridisegnare i nostri documenti.Quindi, se estendiamo la nostra pipeline con la fase successiva:

{ 
    "$project": { 
     "_id": "$last._id", 
     "user": "$last.user", 
     "studentName": "$last.studentName", 
     "createdAt": "$last.createdAt", 
     "isAbandoned": "$last.isAbandoned" 
}} 

produce i risultati attesi:

{ 
    "_id" : ObjectId("56cee51503b7cb7b0eda9c4c"), 
    "user" : ObjectId("56c85244bd5f92cd78ae4bc1"), 
    "studentName" : "Rajeev", 
    "createdAt" : ISODate("2016-02-25T11:27:17.281Z"), 
    "isAbandoned" : true 
} 
1

Questo è un buon esempio di necessità di raggruppare i dati per file specifici (createdAt) e quindi confrontare i criteri di corrispondenza dei set di risultati.

  1. ritrovamento max da studente id,
  2. partita solo le voci di entrata max = createdAt
  3. controllo qualora transitino criteri
  4. rimodellare documento

Codice:

db.student.aggregate([{ 
    $group : { 
     _id : "$user", 
     created : { 
      $max : "$createdAt" 
     }, 
     documents : { 
      $push : "$$ROOT" 
     } 
    } 
}, { 
$project : { 
    _id : 0, 
    documents : { 
     $filter : { 
     input : "$documents", 
     as : "item", 
      cond : { 
       $eq : ["$$item.createdAt", "$created"] 
      } 
     } 
    }} 
}, { 
$match : { 
    "documents.isAbandoned" : true 
}}, 
{ $unwind : "$documents" }, 
{ 
$project : { 
    _id : "$documents._id", 
    user : "$documents.user", 
    studentName : "$documents.studentName", 
    createdAt : "$documents.createdAt", 
    isAbandoned : "$documents.isAbandoned", 
}} 
]) 
+0

Questo funziona, ma è davvero, davvero non efficiente, davvero non c'è bisogno di passare attraverso tutti questo problema – styvane

+0

Anche il tuo codice fallirà se OP sta usando la vecchia versione di MongoDB perché '$ filter' è nuovo nella versione 3.2 – styvane

+1

@ user3100115 - la versione di db non è stata specificata :-), così che il mio è stato assunto, btw bella soluzione pulita! – profesor79