Как добавить встроенное поле с соответствующими документами

#mongodb #aggregation-framework #pymongo

#mongodb #агрегация-фреймворк #pymongo

Вопрос:

Я использую Python с pymongo для запроса из базы данных.

У меня есть 3 разные коллекции:

1-й:

 # Projects collection
{
    "_id": "A",
},
{
    "_id": "B",
},
{
    "_id": "C"
},
..
  

2-й:

 # Episodes collection
{
    "_id": "A/Episode01",
    "project": "A",
    "name": "Episode01"
},
{
    "_id": "A/Episode02",
    "project": "A",
    "name": "Episode02"
},
{
    "_id": "B/Episode01",
    "project": "B",
    "name": "Episode01"
},
..
  

3-й:

 # Sequences collection
{
    "_id": "A/Episode01/Sequence01",
    "project": "A",
    "episode": "Episode01",
    "name": "Sequence01"
},
{
    "_id": "A/Episode02/Sequence02",
    "project": "A",
    "episode": "Episode02",
    "name": "Sequence02"
},
{
    "_id": "B/Episode01/Sequence01",
    "project": "B",
    "episode": "Episode01",
    "name": "Sequence01"
},
..
  

Я хочу использовать aggregate для запроса проекта A и получить все соответствующие ему эпизоды и последовательности, подобные этому:

 {
    "_id": "A",
    "episodes": 
    [
        {
            "_id": "A/Episode01",
            "project": "A",
            "name": "Episode01",
            "sequences": 
            [
                {
                    "_id": "A/Episode01/Sequence01",
                    "project": "A",
                    "episode": "Episode01",
                    "name": "Sequence01"
                },
            ]
        },
        {
            "_id": "A/Episode02",
            "project": "A",
            "name": "Episode02",
            "sequences":
            [
                {
                    "_id": "A/Episode02/Sequence02",
                    "project": "A",
                    "episode": "Episode02",
                    "name": "Sequence02"
                },
            ]
        },
    ]
}
  

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

Прямо сейчас мой запрос выглядит следующим образом:

 [
    {"$match": {
        "_id": "A"}
    },
    {"$lookup": {
        "from": "episodes",
        "localField": "_id",
        "foreignField": "project",
        "as": "episodes"}
    },
    {"$group": {
        "_id": {
            "_id": "$_id",
            "episodes": "$episodes"}
    }}
]
  

Ответ №1:

Вы можете сделать следующее

  1. используйте $match для сопоставления документа
  2. используйте некоррелированные запросы для объединения двух коллекций. Но обычное объединение также возможно, как вы написали. Это проще, когда мы сталкиваемся с некоторыми сложными ситуациями.

Сценарий Mongo приведен ниже

 [
  {
    "$match": {
      "_id": "A"
    }
  },
  {
    $lookup: {
      from: "Episodes",
      let: {
        id: "$_id"
      },
      pipeline: [
        {
          $match: {
            $expr: {
              $eq: [
                "$project",
                "$$id"
              ]
            }
          }
        },
        {
          $lookup: {
            from: "Sequences",
            let: {
              epi: "$name"
            },
            pipeline: [
              {
                $match: {
                  $expr: {
                    $eq: [
                      "$episode",
                      "$$epi"
                    ]
                  }
                }
              }
            ],
            as: "sequences"
          }
        }
      ],
      as: "episodes"
    }
  }
]
  

Рабочая игровая площадка Mongo


Обновление 01

Использование стандартного поиска

 [
  {
    "$match": {
      "_id": "A"
    }
  },
  {
    "$lookup": {
      "from": "Episodes",
      "localField": "_id",
      "foreignField": "project",
      "as": "episodes"
    }
  },
  {
    $unwind: "$episodes"
  },
  {
    "$lookup": {
      "from": "Sequences",
      "localField": "episodes.name",
      "foreignField": "episode",
      "as": "episodes.sequences"
    }
  },
  {
    $group: {
      _id: "$episodes._id",
      episodes: {
        $addToSet: "$episodes"
      }
    }
  }
]
  

Рабочая игровая площадка Mongo

Комментарии:

1. Я думаю, что моя версия mongo не поддерживает let поиск, так как я на 3.2.3. Прямо сейчас это выдает ошибку $lookup must be strings, let: { id: "$_id" } is type 3

2. Ох. хорошо, тогда у нас будет больше работы со стандартным поиском. Я сделаю и дам вам знать

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

4. НЕТ, нет никакого способа, если вы не используете некоррелированный поиск. Что вы можете сделать, так это просто сопоставить и удалить на другом этапе

5. Будет ли использование $filter на другом этапе способом удаления любых конфликтующих записей?