2013-04-27 3 views
9

Sto avendo seguente documento in MongoDBcampi corrispondenti internamente MongoDB

{ 
     "_id" : ObjectId("517b88decd483543a8bdd95b"), 
     "studentId" : 23, 
     "students" : [ 
      { 
      "id" : 23, 
      "class" : "a" 
      }, 
      { 
      "id" : 55, 
      "class" : "b" 
      } 
     ] 
    } 
    { 
     "_id" : ObjectId("517b9d05254e385a07fc4e71"), 
     "studentId" : 55, 
     "students" : [ 
      { 
      "id" : 33, 
      "class" : "c" 
      } 
     ] 
    } 

Note: non un dati effettivi, ma lo schema è esattamente lo stesso.

Requirement: Trovare il documento che corrisponde al studentId e students.id (id all'interno della matrice studenti utilizzando singola query.

Ho provato il codice come sotto

db.data.aggregate({$match:{"students.id":"$studentId"}},{$group:{_id:"$student"}}); 

Result: Array vuoto, se i sostituire {"students.id": "$ studentId"} a {"students.id": 33} si sta restituendo il secondo documento nell'invisibile sopra indicato

È possibile ottenere i documenti per questo scenario utilizzando una singola query?

risposta

13

Se possibile, suggerisco di impostare la condizione durante l'archiviazione dei dati in modo da poter eseguire un controllo rapido della verità (isInStudentsList). Sarebbe super veloce per fare quel tipo di query.

In caso contrario, c'è un modo relativamente complesso di utilizzare il gasdotto quadro Aggregazione di fare ciò che si desidera in una singola query:

db.students.aggregate( 
    {$project: 
     {studentId: 1, studentIdComp: "$students.id"}}, 
    {$unwind: "$studentIdComp"}, 
    {$project : { studentId : 1, 
     isStudentEqual: { $eq : [ "$studentId", "$studentIdComp" ] }}}, 
    {$match: {isStudentEqual: true}}) 

Dato il vostro esempio ingresso l'uscita sarebbe:

{ 
    "result" : [ 
     { 
      "_id" : ObjectId("517b88decd483543a8bdd95b"), 
      "studentId" : 23, 
      "isStudentEqual" : true 
     } 
    ], 
    "ok" : 1 
} 

Una breve spiegazione dei passaggi:

  1. Creare una proiezione del documento con solo studentId e un nuovo campo con un array contenente solo il id (quindi il primo documento conterrebbe [23, 55].
  2. Utilizzando tale struttura, $unwind. Ciò crea un nuovo documento temporaneo per ogni elemento dell'array studentIdComp.
  3. Ora, prendi quei documenti e crea una nuova proiezione di documento, che continua ad avere il studentId e aggiunge un nuovo campo chiamato isStudentEqual che confronta l'uguaglianza di due campi, studentId e studentIdComp. Ricorda che a questo punto c'è un singolo documento temporaneo che contiene quei due campi.
  4. Infine, controllare che il valore di confronto isStudentEqual sia vero e restituire quei documenti (che conterranno il documento originale _id e lo studentId.
  5. Se lo studente era nell'elenco più volte, potrebbe essere necessario raggruppare i risultati su studentId o _id per evitare duplicati (ma non so che ne avresti bisogno).
+0

Funziona bene, grazie per la spiegazione. +1 – karthick

-1

db.data.find({students: {$elemMatch: {id: 23}} , studentId: 23});

+0

Credo che la domanda è come trovare tutti i documenti in cui la condizione è vera (selezionare documento in cui gli studenti elenco contiene StudentID.) – WiredPrairie

0

Purtroppo è impossibile; (

per risolvere questo problema è necessario utilizzare un $ dove dichiarazione (esempio: Finding embeded document in mongodb?),

ma $ dove è limitato dall'utilizzo con framework di aggregazione

+0

@Asya grazie;) –

+0

nota che $ dove non è necessario con aggregazione quadro anche se (come La risposta di WiredPrairie mostra che puoi usare $ project per creare un nuovo campo e quindi usare $ match) –

+0

esattamente @AsyaKamsky, ma in molte domande, osservo la situazione classica: confronta un campo e un altro campo in una gerarchia. lo so, quel problema in genere in un modello di dati cattivo. molti sviluppatori creano il modello di archiviazione dei dati direttamente copia e incolla da archivi relazionali;) –