问题
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?
回答1:
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:
- Build a projection of the document with just
studentId
and a new field with an array containing just theid
(so the first document it would contain[23, 55]
. - Using that structure, $unwind. That creates a new temporary document for each array element in the
studentIdComp
array. - Now, take those documents, and create a new document projection, which continues to have the
studentId
and adds a new field calledisStudentEqual
that compares the equality of two fields, thestudentId
andstudentIdComp
. Remember that at this point there is a single temporary document that contains those two fields. - Finally, check that the comparison value
isStudentEqual
is true and return those documents (which will contain the original document_id
and thestudentId
. - 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).
回答2:
Unfortunately it's impossible ;(
to solve this problem it is necessary to use a $where statement (example: Finding embeded document in mongodb?),
but $where is restricted from being used with aggregation framework
回答3:
db.data.find({students: {$elemMatch: {id: 23}} , studentId: 23});
来源:https://stackoverflow.com/questions/16251032/matching-fields-internally-in-mongodb