MongoDB: заполнение массивов во вложенном документе с $ lookup в агрегации - PullRequest
0 голосов
/ 17 апреля 2020

У меня есть эти три коллекции, и я пытаюсь полностью заполнить поле messages коллекции messages_lists путем «объединения» данных из двух других коллекций.

1. списки сообщений:

{ 
    "_id" : ObjectId("5e96207ea4143b3204373534"), 
    "name" : "6bq32q8TcP", 
    "messages" : [
        {
            "inserted" : ISODate("2020-04-14T20:43:42.086+0000"), 
            "srcId" : "DNWLMSNW217480-20200414T194933Z"
        }, 
        {
            "inserted" : ISODate("2020-04-16T17:40:22.585+0000"), 
            "srcId" : "DNWLMSNW217737-20200416T163914Z"
        }
    ]
}

2. сообщения:

{ 
    "_id" : ObjectId("5e9615205bbcbe360ad40fb7"),
    "srcId" : "DNWLMSNW217480-20200414T194933Z", 
    "status" : "expired",  
    "versions" : [
        {
            "direction" : NumberInt(-1), 
            "points" : [ 50080.028, 50080.029 ]  
        }, 
        {
            "direction" : NumberInt(1), 
            "points" : [ 50080.028, 50080.029 ] 
        }
    ]
},
{ 
    "_id" : ObjectId("5e988b95ab20413033dbe9fb"), 
    "status" : "expired", 
    "srcId" : "DNWLMSNW217737-20200416T163914Z",  
    "versions" : [
        {
            "direction" : NumberInt(1), 
            "points" : [ 50060.096, 50060.097 ] 
        }, 
        {
            "direction" : NumberInt(-1), 
            "points" : [ 50060.096, 50060.097 ]
        }
    ]
}

3. loc_points:

{ 
    "locId" : 50060.096, 
    "road" : "A1", 
    "locCity" : NumberInt(445), 
    "locState" : NumberInt(260) 
},
{  
    "locId" : 50060.097, 
    "road" : "A1", 
    "locCity" : NumberInt(445), 
    "locState" : NumberInt(260) 
},
{ 
    "locId" : 50080.028, 
    "road" : "A2", 
    "locCity" : NumberInt(690), 
    "locState" : NumberInt(260)
},
{ 
    "locId" : 50080.029, 
    "road" : "A2", 
    "locCity" : NumberInt(690), 
    "locState" : NumberInt(260), 
}

Моя версия mongoDB - 3.4.4, и это моя попытка решить проблему:

Шаг 1: Я смог успешно завершите первую стадию $ lookup с этим запросом:

db.getCollection("message_lists").aggregate([
    { $match: { name: '6bq32q8TcP' } },
    { $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
    { $lookup: {
        from: 'messages',
        localField: 'messages.srcId',
        foreignField: 'srcId',
        as: 'messages'
    }},
    { $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
    { $group: { 
        _id: null, 
        messages: { $push: '$messages' }
    }},
])

Я полностью удовлетворен результатом, который я получаю от вышеуказанного запроса. Выглядит это так, и в основном удалось заменить ссылку messages.srcId на коллекцию messages в исходной коллекции messages_list полными данными сообщений, содержащимися в коллекции messages:

{ 
    "_id" : null, 
    "messages" : [
        {
            "_id" : ObjectId("5e9615205bbcbe360ad40fb7"), 
            "srcId" : "DNWLMSNW217480-20200414T194933Z",
            "status" : "expired", 
            "versions" : [
                {
                    "direction" : NumberInt(-1), 
                    "points" : [ 50080.028, 50080.029 ] 
                }, 
                {
                    "direction" : NumberInt(1), 
                    "points" : [ 50080.028, 50080.029 ] 
                }
            ] 
        }, 
        {
            "_id" : ObjectId("5e988b95ab20413033dbe9fb"), 
            "srcId" : "DNWLMSNW217737-20200416T163914Z",
            "status" : "expired", 
            "versions" : [
                {
                    "direction" : NumberInt(1), 
                    "points" : [ 50060.096, 50060.097 ]
                }, 
                {
                    "direction" : NumberInt(-1), 
                    "points" : [ 50060.096, 50060.097 ] 

                }
            ] 
        }
    ]
}

Шаг 2: Цель этого шага - заполнить массивы messages.versions.points данными из коллекции loc_points, чтобы получить конечный результат, который должен выглядеть следующим образом:

Это то, что мне нужно, чтобы результат выглядел как :

{ 
    "_id" : null, 
    "messages" : [
        {
            "_id" : ObjectId("5e9615205bbcbe360ad40fb7"), 
            "srcId" : "DNWLMSNW217480-20200414T194933Z",
            "status" : "expired", 
            "versions" : [
                {
                    "direction" : NumberInt(-1), 
                    "points" : [ 
                      { 
                          "locId" : 50080.028, 
                          "road" : "A2", 
                          "locCity" : NumberInt(690), 
                          "locState" : NumberInt(260)
                      },
                      { 
                          "locId" : 50080.029, 
                          "road" : "A2", 
                          "locCity" : NumberInt(690), 
                          "locState" : NumberInt(260), 
                      }
                    ] 
                }, 
                {
                    "direction" : NumberInt(1), 
                    "points" : [ 
                      { 
                          "locId" : 50080.028, 
                          "road" : "A2", 
                          "locCity" : NumberInt(690), 
                          "locState" : NumberInt(260)
                      },
                      { 
                          "locId" : 50080.029, 
                          "road" : "A2", 
                          "locCity" : NumberInt(690), 
                          "locState" : NumberInt(260), 
                      }
                    ] 
                }
            ] 
        }, 
        {
            "_id" : ObjectId("5e988b95ab20413033dbe9fb"), 
            "srcId" : "DNWLMSNW217737-20200416T163914Z",
            "status" : "expired", 
            "versions" : [
                {
                    "direction" : NumberInt(1), 
                    "points" : [ 
                      { 
                          "locId" : 50060.096, 
                          "road" : "A1", 
                          "locCity" : NumberInt(445), 
                          "locState" : NumberInt(260) 
                      },
                      {  
                          "locId" : 50060.097, 
                          "road" : "A1", 
                          "locCity" : NumberInt(445), 
                          "locState" : NumberInt(260) 
                      }
                    ]
                }, 
                {
                    "direction" : NumberInt(-1), 
                    "points" : [ 
                      { 
                          "locId" : 50060.096, 
                          "road" : "A1", 
                          "locCity" : NumberInt(445), 
                          "locState" : NumberInt(260) 
                      },
                      {  
                          "locId" : 50060.097, 
                          "road" : "A1", 
                          "locCity" : NumberInt(445), 
                          "locState" : NumberInt(260) 
                      }
                    ] 
                }
            ] 
        }
    ]
}

Чтобы понять, мне нужно выполнить дополнительный поиск в $, например:

{ $lookup: {
    from: 'loc_points',
    localField: 'messages.versions.points',
    foreignField: 'locId',
    as: 'messages.versions.points',
}},
{ $unwind: { path: '$messages.versions.points', preserveNullAndEmptyArrays: true }}

Однако, после целого дня исследований и прочтения по крайней мере пяти других потоков stackoverflow, я не смог найти решение, напоминающее мою глубоко вложенную структуру документа.

Я был бы очень благодарен если кто-то, кто более знаком с платформой агрегации mongoDB, мог бы помочь мне здесь! Заранее спасибо !!

Редактировать: После того, как Джо посоветовался ниже, я добавил второй поиск $ перед заключительным оператором $ group. Итак, теперь мой общий запрос выглядит так:

db.getCollection("message_lists").aggregate([
    { $match: { name: '6bq32q8TcP' } },
    { $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
    { $lookup: {
        from: 'messages',
        localField: 'messages.srcId',
        foreignField: 'srcId',
        as: 'messages'
    }},
    { $unwind: { path: '$messages', preserveNullAndEmptyArrays: true }},
    { $lookup: {
        from: 'loc_points',
        localField: 'messages.versions.points',
        foreignField: 'locId',
        as: 'messages.versions.points',
    }},
    { $group: { 
        _id: null, 
        messages: { $push: '$messages' }
    }}
])

Однако в результате messages.versions.points возвращается как пустой массив, все остальные поля version отсутствуют, а messages.versions даже не является массив больше:

{ 
    "_id" : null, 
    "messages" : [
        {
            "_id" : ObjectId("5e9615205bbcbe360ad40fb7"), 
            "srcId" : "DNWLMSNW217480-20200414T194933Z",
            "status" : "expired", 
            "versions" : {
                "points" : [
                ]
            },  
        }, 
        {
            "_id" : ObjectId("5e988b95ab20413033dbe9fb"), 
            "srcId" : "DNWLMSNW217737-20200416T163914Z",
            "status" : "expired", 
            "versions" : {
                "points" : [
                ]
            },  
        }
    ]
}

Я просто не могу понять, как мне нужно изменить оператор $ group, чтобы правильно заполнить messages.versions.points. Любая помощь приветствуется! (Я знаю, что из мира MySQL я показываю, что еще не полностью осознал концепцию агрегации, но в то же время я изо всех сил пытаюсь найти учебник, который соответствует моему сценарию)

Ответы [ 2 ]

0 голосов
/ 19 апреля 2020

Попробуйте это

db.message_lists.aggregate([
    {
        $unwind: '$messages'
    },
    {
        $lookup: {
            from: 'messages',
            localField: 'messages.srcId',
            foreignField: 'srcId',
            as: 'messages'
        }
    },
    {
        $project: {
            _id: 1,
            name: 1,
            message: { $arrayElemAt: ["$messages", 0] },
        }
    },
    {
        $unwind: '$message.versions'
    },
    {
        $unwind: '$message.versions.points'
    },
    {
        $lookup: {
            from: 'loc_point',
            localField: 'message.versions.points',
            foreignField: 'locId',
            as: 'points'
        }
    },
    {
        $unwind: '$points'
    },
    {
        $project: {
            _id: 1,
            name: 1,
            message: {
                _id: '$message._id',
                srcId: '$message.srcId',
                status: '$message.status',
                versions: {
                    direction: "$message.versions.direction",
                    points: '$points'
                }
            }
        }
    },
    {
        $group: {
            _id: { message_id: '$message._id', direction: '$message.versions.direction' },
            name: { $first: '$name' },
            srcId: { $first: '$message.srcId' },
            status: { $first: '$message.status' },
            points: {
                $push: '$message.versions.points'
            }
        }
    },
    {
        $group: {
            _id: '$_id',
            name: { $first: '$name' },
            srcId: { $first: 'srcId' },
            status: { $first: '$status' },
            versions: {
                $push: {
                    direction: '$_id.direction',
                    points: '$points'
                }
            }
        }
    },
    {
        $project: {
            _id: "$_id.message_id",
            name: 1,
            srcId: 1,
            status: 1,
            versions: 1
        }
    },
    {
        $unwind: '$versions'
    },
    {
        $group: {
            _id: '$_id',
            status: { $first: '$status' },
            srcId: { $first: '$srcId' },
            versions: { $push: '$versions' }

        }
    },
    {
        $group: {
            _id: null,
            messages: { $push: '$$ROOT' }
        }
    }
])
0 голосов
/ 17 апреля 2020

Этап $lookup возвращает один массив для каждого исходного документа в конвейере.

Если вы использовали дополнительный этап $lookup, который вы отметили после существующего конвейера, он добавит один массив, который будет содержать набор точек, на которые ссылается любая версия любого из сообщений.

Чтобы набрать очки для каждой версии каждого сообщения, их нужно будет развернуть. У вас уже были сообщения, поэтому вам нужно развернуть версии и запустить дополнительный поиск, затем сгруппировать сообщение, чтобы собрать версию обратно в массив, а затем существующую группу, чтобы собрать сообщения.

...