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

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

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

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

Дано:

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

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

Студенческая коллекция:

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

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

person Tony Sepia    schedule 26.12.2016    source источник


Ответы (1)


Вы можете использовать:

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

  • $match, чтобы соответствовать только тем классам, в которых есть оценка blue.

  • $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 коллекции, выполните $lookup и сопоставьте те, которые не пусты. Добавьте следующее в запрос агрегации:

{
    $lookup: {
        from: "classes",
        localField: "class_id",
        foreignField: "class_id",
        as: "class"
    }
}, {
    $match: {
        "class": { $not: { $size: 0 } }
    }
}
person Bertrand Martel    schedule 26.12.2016