You could use :
a $group to group by class_id and $push all grade in an array so we can deduce easily in the next step which class "contains" blue. Preserve the current document with $$ROOT because we'll need the students that match the class_id
a $match to match only classes that have grade blue in it
an $unwind to remove the array of students generated by previous $$ROOT
a $project to reorganize your document nicely
Query would be :
db.students.aggregate([{
"$group": {
"_id": "$class_id",
"grades": { "$push": "$grade" },
"students": { "$push": "$$ROOT" }
}
}, {
"$match": {
"grades": { "$all": ["blue","red"] }
}
}, {
"$unwind": "$students"
}, {
"$project": {
"_id": "$students._id",
"class_id": "$students.class_id",
"grade": "$students.grade",
}
}])
If you need to match other color than ["blue","red"] you can add more in the $match aggregation ($in: ["blue","red","yellow"])
For implementing it in PyMongo, it is very straightforward :
from pymongo import MongoClient
import pprint
db = MongoClient().testDB
pipeline = [ <the_aggregation_query_here> ]
pprint.pprint(list(db.students.aggregate(pipeline)))
Additionnaly, to match only students that belong to classes collection, perform a $lookup and match those that are not empty. Add the following at the aggregation query :
{
$lookup: {
from: "classes",
localField: "class_id",
foreignField: "class_id",
as: "class"
}
}, {
$match: {
"class": { $not: { $size: 0 } }
}
}