Как я могу объединить фильтрующие вложенные документы и получить значение из другого поля

У меня есть такая коллекция:

{
  '_id' : ObjectId('6251f8556e75125f9260f333'),
  'name': 'jojo',
  'profile': 'jojo profile',
  'date': ISODate("2022-04-09T21:18:40.473Z"),
  'look': [
    { 'art': 'group-id', 'data': 'alma', 'dt': '1'},
    { 'art': 'called', 'data': 'central', 'dt': '1'},
    { 'art': 'access-time', 'data': 108000, 'dt': '1'}
  ]
  'answer': [
    { 'art': 'rate-id', 'data': 'limit1', 'dt': '1'},
    { 'art': 'protocol', 'data': 'tcp', 'dt': '1'}
  ]
},
{
  '_id' : ObjectId('6251f8306e75125f9260f332'),
  'name': 'dodo',
  'profile': 'dodo profile',
  'date': ISODate("2022-04-09T15:20:58.562Z"),
  'look': [
    { 'art': 'group-id', 'data': 'alma', 'dt': '1'},
    { 'art': 'called', 'data': 'central', 'dt': '1'},
  ]
  'answer': [
    { 'art': 'rate-id', 'data': 'limit1', 'dt': '1'},
  ]
},
{
  '_id' : ObjectId('6251a5113700ba4a0a59c48f'),
  'name': 'kaka',
  'profile': 'kaka profile',
  'date': ISODate("2022-04-09T15:22:25.816Z"),
  'look': [
    { 'art': 'access-time', 'data': 50400, 'dt': '1'}
  ]
  'answer': [
    { 'art': 'protocol', 'data': 'tcp', 'dt': '1'}
  ]
}

и я ожидал такого вывода:

{
  '_id' : ObjectId('6251f8556e75125f9260f333'),
  'name': 'jojo',
  'profile': 'jojo profile',
  'date': ISODate("2022-04-09T21:18:40.473Z"),
  'goup': 'alma',        // filter by 'group-id' and put value of data field
  'called': 'central',   // filter by 'called' and put value of data field
  'accessTime': 108000,  // filter by 'access-time' and put value of data field
  'rate': 'limi1',       // filter by 'rate-id' and put value of data field
  'protocol': 'tcp',     // filter by 'protocol' and put value of data field
},
{
  '_id' : ObjectId('6251f8306e75125f9260f332'),
  'name': 'dodo',
  'profile': 'dodo profile',
  'date': ISODate("2022-04-09T15:20:58.562Z"),
  'goup': 'alma',
  'called': 'central',
  'accessTime': '',     // set blank data if not exist
  'rate': 'limi1',
  'protocol': '',       // set blank data if not exist
},
{
  '_id' : ObjectId('6251a5113700ba4a0a59c48f'),
  'name': 'kaka',
  'profile': 'kaka profile',
  'date': ISODate("2022-04-09T15:22:25.816Z"),
  'goup': '',          // set blank data if not exist
  'called': '',        // set blank data if not exist
  'accessTime': 50400,
  'rate': '',          // set blank data if not exist
  'protocol': 'tcp',
}

Я искал здесь, но не смог найти ответ, соответствующий проблеме, с которой я столкнулся, вероятно, из-за неправильных ключевых слов. Поскольку я новичок в mongodb, я не понимаю, как решить нужный запрос. Как я могу этого добиться? Помогите пожалуйста мне...

Использование JavaScript и MongoDB
Использование JavaScript и MongoDB
Сегодня я собираюсь вкратце рассказать о прототипах в JavaScript, а также представить и объяснить вам работу с базой данных MongoDB.
2
0
26
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Для этого вы должны использовать aggregation framework из mongo db, потому что потребуются сложные операции для получения данных в нужной вам форме.

https://www.mongodb.com/docs/manual/aggregation/

Каждая агрегация представляет собой набор этапов, и каждый этап делает что-то конкретное.

Я использовал следующие этапы:

  1. addFields: позволяет добавлять новые поля в ответ каждого документа, поэтому, если у вас нет группы в документе, это добавит или заменит ее.
  2. проект: позволяет удалить некоторые поля документа. На этапе прогнозирования, если вы установите атрибут равным 0, этот атрибут будет удален из ответа.

Также я использовал некоторые операторы:

  1. фильтр: это позволяет вам фильтровать данные элемента, который является массивом
  2. arrayElemenAt: получает массив и возвращает указанную позицию

Трубопровод:

[
   {
      "$addFields":{
         "group":{
            "$arrayElemAt":[
               {
                  "$filter":{
                     "input":"$look",
                     "as":"item",
                     "cond":{
                        "$eq":[
                           "$$item.art",
                           "group-id"
                        ]
                     }
                  }
               },
               0
            ]
         },
         "called":{
            "$arrayElemAt":[
               {
                  "$filter":{
                     "input":"$look",
                     "as":"item",
                     "cond":{
                        "$eq":[
                           "$$item.art",
                           "called"
                        ]
                     }
                  }
               },
               0
            ]
         },
         "accessTime":{
            "$arrayElemAt":[
               {
                  "$filter":{
                     "input":"$look",
                     "as":"item",
                     "cond":{
                        "$eq":[
                           "$$item.art",
                           "access-time"
                        ]
                     }
                  }
               },
               0
            ]
         },
         "rate":{
            "$arrayElemAt":[
               {
                  "$filter":{
                     "input":"$answer",
                     "as":"item",
                     "cond":{
                        "$eq":[
                           "$$item.art",
                           "rate-id"
                        ]
                     }
                  }
               },
               0
            ]
         },
         "protocol":{
            "$arrayElemAt":[
               {
                  "$filter":{
                     "input":"$answer",
                     "as":"item",
                     "cond":{
                        "$eq":[
                           "$$item.art",
                           "protocol"
                        ]
                     }
                  }
               },
               0
            ]
         }
      }
   },
   {
      "$addFields":{
         "group":"$group.data",
         "called":"$called.data",
         "accessTime":"$accessTime.data",
         "rate":"$rate.data",
         "protocol":"$protocol.data"
      }
   },
   {
      "$project":{
         "look":0,
         "answer":0
      }
   }
]

Это довольно громоздко с текущей структурой, так как для каждого поля вы должны преобразовать объект в массив, отфильтровать его, а затем преобразовать обратно, вот как это выглядит:

db.collection.aggregate([
  {
    $replaceRoot: {
      newRoot: {
        "$mergeObjects": [
          {
            _id: "$_id",
            name: "$name",
            profile: "$profile",
            date: "$date",
            
          },
          {
            "$arrayToObject": {
              $map: {
                input: {
                  $filter: {
                    input: {
                      $objectToArray: {
                        $ifNull: [
                          {
                            "$arrayElemAt": [
                              {
                                $filter: {
                                  input: {
                                    $ifNull: [
                                      "$look",
                                      []
                                    ]
                                  },
                                  cond: {
                                    $eq: [
                                      "$$this.art",
                                      "group-id"
                                    ]
                                  }
                                }
                              },
                              0
                            ]
                          },
                          {
                            art: ""
                          }
                        ]
                      }
                    },
                    cond: {
                      $eq: [
                        "$$this.k",
                        "data"
                      ]
                    }
                  }
                },
                in: {
                  k: "goup",
                  v: "$$this.v"
                }
              }
            }
          },
          {
            "$arrayToObject": {
              $map: {
                input: {
                  $filter: {
                    input: {
                      $objectToArray: {
                        $ifNull: [
                          {
                            "$arrayElemAt": [
                              {
                                $filter: {
                                  input: {
                                    $ifNull: [
                                      "$look",
                                      []
                                    ]
                                  },
                                  cond: {
                                    $eq: [
                                      "$$this.art",
                                      "called"
                                    ]
                                  }
                                }
                              },
                              0
                            ]
                          },
                          {
                            art: ""
                          }
                        ]
                      }
                    },
                    cond: {
                      $eq: [
                        "$$this.k",
                        "data"
                      ]
                    }
                  }
                },
                in: {
                  k: "called",
                  v: "$$this.v"
                }
              }
            }
          },
          {
            "$arrayToObject": {
              $map: {
                input: {
                  $filter: {
                    input: {
                      $objectToArray: {
                        $ifNull: [
                          {
                            "$arrayElemAt": [
                              {
                                $filter: {
                                  input: {
                                    $ifNull: [
                                      "$look",
                                      []
                                    ]
                                  },
                                  cond: {
                                    $eq: [
                                      "$$this.art",
                                      "access-time"
                                    ]
                                  }
                                }
                              },
                              0
                            ]
                          },
                          {
                            art: ""
                          }
                        ]
                      }
                    },
                    cond: {
                      $eq: [
                        "$$this.k",
                        "data"
                      ]
                    }
                  }
                },
                in: {
                  k: "access-time",
                  v: "$$this.v"
                }
              }
            }
          },
          {
            "$arrayToObject": {
              $map: {
                input: {
                  $filter: {
                    input: {
                      $objectToArray: {
                        $ifNull: [
                          {
                            "$arrayElemAt": [
                              {
                                $filter: {
                                  input: {
                                    $ifNull: [
                                      "$answer",
                                      []
                                    ]
                                  },
                                  cond: {
                                    $eq: [
                                      "$$this.art",
                                      "rate-id"
                                    ]
                                  }
                                }
                              },
                              0
                            ]
                          },
                          {
                            art: ""
                          }
                        ]
                      }
                    },
                    cond: {
                      $eq: [
                        "$$this.k",
                        "data"
                      ]
                    }
                  }
                },
                in: {
                  k: "rate",
                  v: "$$this.v"
                }
              }
            }
          },
          {
            "$arrayToObject": {
              $map: {
                input: {
                  $filter: {
                    input: {
                      $objectToArray: {
                        $ifNull: [
                          {
                            "$arrayElemAt": [
                              {
                                $filter: {
                                  input: {
                                    $ifNull: [
                                      "$answer",
                                      []
                                    ]
                                  },
                                  cond: {
                                    $eq: [
                                      "$$this.art",
                                      "protocol"
                                    ]
                                  }
                                }
                              },
                              0
                            ]
                          },
                          {
                            art: ""
                          }
                        ]
                      }
                    },
                    cond: {
                      $eq: [
                        "$$this.k",
                        "data"
                      ]
                    }
                  }
                },
                in: {
                  k: "protocol",
                  v: "$$this.v"
                }
              }
            }
          }
        ]
      }
    }
  }
])

Игровая площадка Монго

Если вы используете Mongo версии 5+, вы можете использовать $getField, чтобы немного упростить синтаксис, вот как одно поле будет выглядеть в этом синтаксисе:

goup: {
    $getField: {
        field: 'data',
        input: {
            '$arrayElemAt': [
                {
                    $filter: {
                        input: {
                            $ifNull: [
                                '$look',
                                [],
                            ],
                        },
                        cond: {
                            $eq: [
                                '$$this.art',
                                'group-id',
                            ],
                        },
                    },
                },
                0,
            ],
        },
    },
},
Ответ принят как подходящий

Вам потребуется агрегатная операция с конвейером со следующими ключевыми операторами и этапами:

  • $map: оператор для преобразования look и ответа arrays в документы с только что сопоставленными полями k и v, что имеет решающее значение для получения хэш-карты со следующим оператором
  • $arrayToObject: это позволяет сделать вышеописанное возможным, т. е. преобразовать массив в один документ.
  • $mergeObjects: объединить поля верхнего уровня, т. е. _id, date, name, profile, вместе с преобразованными документами выше
  • $replaceWith: этап конвейера для замены корневого документа указанным выше документом

В целом, ваш конвейер должен следовать:

const first = { 
   $first: {
      $split: ['$$this.art', '-']
   }
};
const keyExpression = {
   $cond: [
     { $eq: [first, 'access'] },
     'accessTime',
     first
   ]
};

const pipeline = [
    { $replaceWith: {
        $mergeObjects: [
            {  
                _id: '$_id', 
                date: '$date', 
                name: '$name', 
                profile: '$profile',
                protocol: '',
                group: '',
                called: '',
                rate: '',
                accessTime: '',
            },
            { $arrayToObject: {
                $map: {
                  input: '$look',
                  in: { k: keyExpression, v: '$$this.data' } 
                }
            } },
            { $arrayToObject: {
                $map: {
                  input: '$answer',
                  in: { k: keyExpression, v: '$$this.data' } 
                }
            } }
        ]
    } } 
]

Игровая площадка Монго

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

Другие вопросы по теме