Make $elemMatch (projection) return all objects th

2019-03-11 04:54发布

I will use the example from here

{
 _id: 1,
 zipcode: 63109,
 students: [
              { name: "john", school: 102, age: 10 },
              { name: "jess", school: 102, age: 11 },
              { name: "jeff", school: 108, age: 15 }
           ]
}
{
 _id: 2,
 zipcode: 63110,
 students: [
              { name: "ajax", school: 100, age: 7 },
              { name: "achilles", school: 100, age: 8 },
           ]
}

{
 _id: 3,
 zipcode: 63109,
 students: [
              { name: "ajax", school: 100, age: 7 },
              { name: "achilles", school: 100, age: 8 },
           ]
}

{
 _id: 4,
 zipcode: 63109,
 students: [
              { name: "barney", school: 102, age: 7 },
           ]
}

If I run

db.schools.find( { zipcode: 63109 },
             { students: { $elemMatch: { school: 102 } } } )

It will give the first result of each array. Naming this:

{ "_id" : 1, "students" : [ { "name" : "john", "school" : 102, "age" : 10 } ] }
{ "_id" : 3 }
{ "_id" : 4, "students" : [ { "name" : "barney", "school" : 102, "age" : 7 } ] }

How can I make it return all the object of the array (and not only the first) that match the criteria? Meaning this:

{
 _id: 1,
 students: [
              { name: "john", school: 102, age: 10 },
              { name: "jess", school: 102, age: 11 }
           ]
}    
{ _id: 3 }
{_id: 4, students: [ { name: "barney", school: 102, age: 7 }]}

2条回答
我命由我不由天
2楼-- · 2019-03-11 05:34

In order to return multiple subdocuments, you're going to need to use the aggregation framework. This will return all of the subdocuments you're looking for:

db.zip.aggregate(
  {$match: {zipcode: 63109}},
  {$unwind: "$students"},
  {$match: {"students.school": 102}}
)

You can do various things to get different output, but this will return:

{
    "result" : [
        {
            "_id" : 1,
            "zipcode" : 63109,
            "students" : {
                "name" : "john",
                "school" : 102,
                "age" : 10
            }
        },
        {
            "_id" : 1,
            "zipcode" : 63109,
            "students" : {
                "name" : "jess",
                "school" : 102,
                "age" : 11
            }
        },
        {
            "_id" : 4,
            "zipcode" : 63109,
            "students" : {
                "name" : "barney",
                "school" : 102,
                "age" : 7
            }
        }
    ],
    "ok" : 1
}
查看更多
Juvenile、少年°
3楼-- · 2019-03-11 05:46

Previous and incorrect answer:

This should work as of today. See https://docs.mongodb.com/v3.2/reference/operator/projection/positional/#array-field-limitations

You should get the correct result when querying using $elemMatch in the query and exposing the sub-document in the projection like following:

db.schools.find( { zipcode: 63109, students: { $elemMatch: { school: 102 } } },
                 { 'students.$': 1 } )


New answer

Limiting the list of sub-documents to those matching the query is as of now not possible using find(). Please take aggregate() instead or take one of the following possibilities:

You could either get all the sub-documents of the matching document by adding the array-property in the projection:

db.schools.find( { zipcode: 63109, students: { $elemMatch: { school: 102 } } }, { 'students': 1 })
> { "_id" : 1, "students" : [ { "name" : "john", "school" : 102, "age" : 10 }, { "name" : "jess", "school" : 102, "age" : 11 }, { "name" : "jeff", "school" : 108, "age" : 15 } ] }
> { "_id" : 4, "students" : [ { "name" : "barney", "school" : 102, "age" : 7 } ] }

Or you can get the first item matching the $elemMatch query on the sub-documents:

db.schools.find( { zipcode: 63109, students: { $elemMatch: { school: 102 } } }, { 'students.$': 1 })
> { "_id" : 1, "students" : [ { "name" : "john", "school" : 102, "age" : 10 } ] }
> { "_id" : 4, "students" : [ { "name" : "barney", "school" : 102, "age" : 7 } ] }
查看更多
登录 后发表回答