Задача группировки MongoDB-несколько групп, условия

Моя цель-получить все «студенческие» документы, которые принадлежат «классам»
которые имеют хотя бы одного ученика класса «синий» и хотя бы одного
сорта «Красный».

Я склонен просто выполнять последовательность запросов на Python (pymongo), решая задачу напрямую.

Интересно, есть ли какой-то умный конвейер агрегации, который я мог бы использовать!

С учетом:

Коллекция классов:

{ class_id: 'a' }
{ class_id: 'b' }

Коллекция студентов:

{ class_id: 'a',
grade: 'blue' }

{class_id: 'a',
grade: 'red' }

1 ответ

  1. Вы могли бы использовать :

    • a $groupсгруппировать по class_idи $pushвсе оценки в массиве, так что мы можем легко вывести на следующем шаге, который класс «содержит»blue. Сохранить текущий документ с$$ROOT, Потому что нам нужны студенты, которые соответствуют class_id

    • a$match, чтобы соответствовать только классам, которые имеют класс blueв нем

    • an $unwindдля удаления массива учащихся, созданного предыдущим $$ROOT

    • a $projectчтобы красиво реорганизовать документ

    Запрос был бы :

    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",
        }
    }])
    

    Если вам нужно соответствовать другому цвету, чем [«синий», «красный»], вы можете добавить больше в $matchагрегацию ($in: ["blue","red","yellow"])

    Для его реализации в PyMongo, это очень просто :

    from pymongo import MongoClient
    import pprint
    
    db = MongoClient().testDB
    
    pipeline = [ <the_aggregation_query_here> ]
    
    pprint.pprint(list(db.students.aggregate(pipeline)))
    

    Кроме того, чтобы сопоставить только учащихся, которые принадлежат classesколлекции, выполните a $lookupи сопоставьте те, которые не пусты. Добавьте следующее В запрос агрегации :

    {
        $lookup: {
            from: "classes",
            localField: "class_id",
            foreignField: "class_id",
            as: "class"
        }
    }, {
        $match: {
            "class": { $not: { $size: 0 } }
        }
    }