Значение поиска в двух разных полях mongodb + node.js

Я новичок. Но я пытаюсь изучить наиболее логичные способы написания запросов.

Предположим, что у меня есть коллекция, которая равна:

{ 
    "id" : NumberInt(1), 
    "school" : [
        {
            "name" : "george", 
            "code" : "01"
        }, 
        {
            "name" : "michelangelo", 
            "code" : "01"
        }
    ], 
    "enrolledStudents" : [
        {
            "userName" : "elisabeth", 
            "code" : NumberInt(21)
        }
    ]
}
{ 
    "id" : NumberInt(2), 
    "school" : [
        {
            "name" : "leonarda da vinci", 
            "code" : "01"
        }
    ], 
    "enrolledStudents" : [
        {
            "userName" : "michelangelo", 
            "code" : NumberInt(25)
        }
    ]
}

Я хочу перечислить происхождение a key с соответствующими значениями code.

В качестве примера key: michelangelo

Чтобы найти происхождение ключа, я написал два запроса differen aggregation как:

db.test.aggregate([
    {$unwind: "$school"},
    {$match : {"school.name" : "michelangelo"}},
    {$project: {_id: "$id", "key" : "$school.name", "code" : "$school.code"}}
])

и

db.test.aggregate([
    {$unwind: "$enrolledStudents"},
    {$match : {"enrolledStudents.userName" : "michelangelo"}},
    {$project: {_id: "$id", "key" : "$enrolledStudents.userName", "code" : "$enrolledStudents.code"}}
])

результат этих двух запросов возвращает то, что я хочу,

{ "_id" : 1, "key" : "michelangelo", "code" : "01" }
{ "_id" : 2, "key" : "michelangelo", "code" : 25 }

Один из них для поиска в enrolledStudents, другой - в поле school.

Могут ли эти 2 запроса свести к более логическому запросу? Или это единственный способ сделать это?

ps: Я знаю, что структура базы данных не является логической, но я пытался имитировать.

изменить Я пытаюсь написать запрос с помощью find.

db.test.find({$or: [{"enrolledStudents.userName" : "michelangelo"} , {"school.name" : "michelangelo"}]}).pretty()

но это возвращает все документы как:

{
    "id" : 1,
    "school" : [
        {
            "name" : "george",
            "code" : "01"
        },
        {
            "name" : "michelangelo",
            "code" : "01"
        }
    ],
    "enrolledStudents" : [
        {
            "userName" : "elisabeth",
            "code" : 21
        }
    ]
}
{
    "id" : 2,
    "school" : [
        {
            "name" : "leonarda da vinci",
            "code" : "01"
        }
    ],
    "enrolledStudents" : [
        {
            "userName" : "michelangelo",
            "code" : 25
        }
    ]
}

Ответ 1

Mongo 3.4

$match - этот этап сохранит весь массив school и enrolledStudents, где по крайней мере один встроенный документ соответствует как условию запроса

$group - Этот этап объединит все массивы school и enrolledStudents в массив 2d для каждого _id в группе.

$project - Этот этап будет $filter массивом merge для соответствия условиям запроса и $map массива с новыми массивами values.

$unwind - Этот этап сгладит массив.

$addFields и $replaceRoot - Эти этапы добавят поле id и продвинут массив values в начало.

db.collection.aggregate([
    {$match : {$or: [{"enrolledStudents.userName" : "michelangelo"} , {"school.name" : "michelangelo"}]}},
    {$group: {_id: "$id", merge : {$push:{$setUnion:["$school", "$enrolledStudents"]}}}},
    {$project: {
        values: {
              $map:
                 {
                   input: {
                            $filter: {
                                input: {"$arrayElemAt":["$merge",0]},
                                as: "onef",
                                cond: {
                                    $or: [{
                                        $eq: ["$$onef.userName", "michelangelo"]
                                    }, {
                                        $eq: ["$$onef.name", "michelangelo"]
                                    }]
                                }
                            }
                        },
                   as: "onem",
                   in: { 
                         key : { $ifNull: [ "$$onem.userName", "$$onem.name" ] },
                         code : "$$onem.code"}
                 }
            }
        }
    },
    {$unwind: "$values"},
    {$addFields:{"values.id":"$_id"}},
    {$replaceRoot: { newRoot:"$values"}}
])

Пример ответа

{ "_id" : 2, "key" : "michelangelo", "code" : 25 }
{ "_id" : 1, "key" : "michelangelo", "code" : "01" }

Mongo <= 3,2

Заменить последние два этапа выше агрегации на $project, чтобы отформатировать ответ.

{$project: {"_id": 0 , id:"$_id", key:"$values.key", code:"$values.code"}}

Пример ответа

{ "_id" : 2, "key" : "michelangelo", "code" : 25 }
{ "_id" : 1, "key" : "michelangelo", "code" : "01" }

Вы можете использовать $redact вместо $group и match и добавить $project с помощью $map, чтобы отформатировать ответ.

$redact, чтобы пройти уровень документа за раз и выполнить $$DESCEND и $$PRUNE по критериям соответствия.

Единственное, что следует отметить, это использование $ifNull на первом уровне документа для id, чтобы вы могли $$DESCEND на уровень встроенного документа для дальнейшей обработки.

db.collection.aggregate([
    {
        $redact: {
            $cond: [{
                $or: [{
                    $eq: ["$userName", "michelangelo"]
                }, {
                    $eq: ["$name", "michelangelo"]
                }, {
                    $ifNull: ["$id", false]
                }]
            }, "$$DESCEND", "$$PRUNE"]
        }
    },
    {
        $project: {
            id:1,
            values: {
              $map:
                 {
                   input: {$setUnion:["$school", "$enrolledStudents"]},
                   as: "onem",
                   in: { 
                         key : { $ifNull: [ "$$onem.userName", "$$onem.name" ] },
                         code : "$$onem.code"}
                 }
            }
        }
    },
    {$unwind: "$values"},
    {$project: {_id:0,id:"$id", key:"$values.key", code:"$values.code"}}
])