MongoDb - как вернуть только поле вложенного поддокумента при использовании агрегирования поиска?

Я новичок в MongoDb, поэтому привык к SQL. Сейчас у меня в базе есть две коллекции:

1) Серия (с вложенными вложенными документами)

2) Рецензия (решил сослаться на поддокумент серии, потому что рецензий будет много)

См. Это изображение для лучшего понимания.

введите описание изображения здесь

Теперь я хочу добиться следующего. Для каждого обзора (в данном случае двух) я хочу получить название серии.

Я пробовал следующее:

db.review.aggregate([  
   {  
      $lookup:{  
         from:"series",
         localField:"episode",
         foreignField:"seasons.episodes._id",
         as:"episode_entry"
      }
   }
]).pretty()

Проблема в том, что при этом возвращается (конечно) не только название эпизода, на который есть ссылка, но и весь документ сезона.

На рисунке ниже показан мой текущий результат.

введите описание изображения здесь

Я не знаю, как этого добиться. Помогите, пожалуйста. Я использую Mongo 3.4.9


person Mike Evers    schedule 25.10.2017    source источник
comment
Не могли бы вы опубликовать образцы данных в текстовом формате вместо изображений, чтобы мы могли их скопировать и вставить? Кроме того, какова ваша точная желаемая структура вывода?   -  person dnickless    schedule 25.10.2017


Ответы (1)


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

Это поможет вам напрямую вставлять / обновлять эпизоды.

Что-то вроде

db.series.insertMany([
  {
    "title": "Sherlock Holmes",
    "nr": 1,
    "episodes": [
      {
        "title": "A Study in Pink",
        "nr": 1
      },
      {
        "title": "The Blind Banker",
        "nr": 2
      }
    ]
  },
  {
    "title": "Sherlock Holmes",
    "nr": 2,
    "episodes": [
      {
        "title": "A Scandal in Belgravia",
        "nr": 1
      },
      {
        "title": "The Hounds of Baskerville",
        "nr": 2
      }
    ]
  }
])

Поисковый запрос будет делать что-то вроде этого

episode: { $in: [ episodes._id1, episodes._id2, ... ] }

Из документов

Если поле содержит массив, то оператор $ in выбирает документы, поле которых содержит массив, содержащий хотя бы один элемент, который соответствует значению в указанном массиве (например, и т. Д.)

Таким образом, поиск вернет все эпизоды, когда есть совпадение. Затем вы можете отфильтровать, чтобы оставить только тот, который соответствует эпизоду вашего обзора.

Таким образом, запрос будет выглядеть так

db.review.aggregate([
  {
    "$lookup": {
      "from": "series",
      "localField": "episode",
      "foreignField": "episodes._id",
      "as": "episode_entry"
    }
  },
  {
    "$addFields": {
      "episode_entry": [
        {
          "$arrayElemAt": {
            "$filter": {
              "input": {
                "$let": {
                  "vars": {
                    "season": {
                      "$arrayElemAt": [
                        "$episode_entry",
                        0
                      ]
                    }
                  },
                  "in": "$$season.episodes"
                }
              },
              "as": "result",
              "cond": {
                "$eq": [
                  "$$result._id",
                  "$episode"
                ]
              }
            }
          }
        },
        0
      ]
    }
  }
])
person s7vr    schedule 25.10.2017
comment
Большое спасибо за ваш ответ. Я также вижу, что люди используют только одну коллекцию для этого варианта использования. Как вы относитесь к моей идее отделить отзывы от остальных? Это хорошая практика из-за его объема или вы предпочитаете размещать его как вложенный документ внутри записи эпизода. - person Mike Evers; 25.10.2017
comment
Np. Ограничение на хранение одного документа составляет 16 МБ. Если вы думаете, что достигнете предела с обзорами, встроенными в эпизоды, я бы держал обзоры в отдельной коллекции. Вы также можете смоделировать документ, в котором вы будете хранить сезон, серию и обзоры в одном документе. Старайтесь не встраивать массивы внутрь массивов, так как становится сложно обновлять / вставлять значения. Есть много способов смоделировать документ, но выберите тот, который лучше всего подходит для вашего варианта использования. - person s7vr; 25.10.2017
comment
Ваш $$ result._id неверен, потому что мне нужен идентификатор серии. В настоящее время это _id сезона. Как мне получить _id на один уровень глубже? - person Mike Evers; 26.10.2017
comment
Извините, я должен был это заметить. Обновлен ответ, чтобы включить $let выражение для проецирования эпизодов сезона с агрегированием $filter. - person s7vr; 26.10.2017
comment
Вау, спасибо! Я думаю, что MongoDb намного сложнее, чем просто sql. - person Mike Evers; 26.10.2017