Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

matching fields internally in mongodb

I am having following document in 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: Not an actual data but schema is exactly same.

Requirement: Finding the document which matches the studentId and students.id(id inside the students array using single query.

I have tried the code like below

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

Result: Empty Array, If i replace {"students.id":"$studentId"} to {"students.id":33} it is returning the second document in the above shown json.

Is it possible to get the documents for this scenario using single query?

like image 534
karthick Avatar asked Apr 27 '13 10:04

karthick


1 Answers

If possible, I'd suggest that you set the condition while storing the data so that you can do a quick truth check (isInStudentsList). It would be super fast to do that type of query.

Otherwise, there is a relatively complex way of using the Aggregation framework pipeline to do what you want in a single query:

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

Given your input example the output would be:

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

A brief explanation of the steps:

  1. Build a projection of the document with just studentId and a new field with an array containing just the id (so the first document it would contain [23, 55].
  2. Using that structure, $unwind. That creates a new temporary document for each array element in the studentIdComp array.
  3. Now, take those documents, and create a new document projection, which continues to have the studentId and adds a new field called isStudentEqual that compares the equality of two fields, the studentId and studentIdComp. Remember that at this point there is a single temporary document that contains those two fields.
  4. Finally, check that the comparison value isStudentEqual is true and return those documents (which will contain the original document _id and the studentId.
  5. If the student was in the list multiple times, you might need to group the results on studentId or _id to prevent duplicates (but I don't know that you'd need that).
like image 150
WiredPrairie Avatar answered Nov 11 '22 16:11

WiredPrairie