Связывание документов MongoDB

MongoDBBeginner
Практиковаться сейчас

Введение

В этой лабораторной работе вы изучите основы установления связей между документами в MongoDB. Этот метод, известный как ссылочная целостность (referencing), необходим для построения сложных и организованных структур баз данных. Вы научитесь создавать коллекции, вставлять документы со ссылками на другие документы, извлекать и объединять связанные данные с помощью этапа агрегации $lookup, а также управлять жизненным циклом этих связанных документов. К концу этой лабораторной работы вы получите твердое понимание того, как моделировать и управлять отношениями "один ко многим" (one-to-many) в вашей базе данных MongoDB.

Установление связей между документами

На этом первом этапе вы создадите две отдельные коллекции и установите между ними связь. Мы смоделируем распространенный сценарий: базу данных библиотеки с коллекциями authors (авторы) и books (книги). Каждая книга будет ссылаться на своего автора.

Сначала откройте MongoDB Shell. Этот интерактивный интерфейс командной строки позволяет вам взаимодействовать с вашим экземпляром MongoDB.

mongosh

Оказавшись в оболочке, вы увидите приглашение test>. Давайте переключимся на новую базу данных с именем library_database. Если база данных не существует, MongoDB создаст ее для вас при первом сохранении данных.

use library_database

Теперь давайте создадим коллекцию authors, вставив два документа. Каждый документ имеет уникальный _id типа ObjectId, который мы будем использовать для ссылок.

db.authors.insertMany([
  {
    _id: ObjectId("660a1f5c9b8f8b1234567890"),
    name: "Jane Austen",
    nationality: "British"
  },
  {
    _id: ObjectId("660a1f5c9b8f8b1234567891"),
    name: "George Orwell",
    nationality: "British"
  }
]);

Вы должны увидеть подтверждение успешной вставки документов.

Пример вывода:

{
  "acknowledged": true,
  "insertedIds": {
    "0": ObjectId("660a1f5c9b8f8b1234567890"),
    "1": ObjectId("660a1f5c9b8f8b1234567891")
  }
}

Далее создайте коллекцию books. В каждом документе книги поле author_id будет хранить ObjectId соответствующего автора из коллекции authors. Это создает связь между книгой и ее автором.

db.books.insertMany([
  {
    title: "Pride and Prejudice",
    author_id: ObjectId("660a1f5c9b8f8b1234567890"),
    year: 1813
  },
  {
    title: "1984",
    author_id: ObjectId("660a1f5c9b8f8b1234567891"),
    year: 1949
  }
]);

Пример вывода:

{
  "acknowledged": true,
  "insertedIds": {
    "0": ObjectId("660b2a1c9b8f8b1234567892"),
    "1": ObjectId("660b2a1c9b8f8b1234567893")
  }
}

Теперь вы успешно создали две коллекции и связали документы в коллекции books с документами в коллекции authors. Оставьте MongoDB shell открытым для следующего шага.

Запрос связанных документов

Теперь, когда вы установили связи, следующим логичным шагом является получение связанных данных в одном запросе. Фреймворк агрегации MongoDB предоставляет для этой цели этап $lookup, который выполняет левое внешнее соединение (left outer join) с другой коллекцией.

Убедитесь, что вы все еще находитесь в оболочке mongosh и используете library_database.

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

db.books.aggregate([
  {
    $lookup: {
      from: "authors",
      localField: "author_id",
      foreignField: "_id",
      as: "author_details"
    }
  }
]);

Этап $lookup объединяет коллекцию books с коллекцией authors. Давайте рассмотрим его параметры:

  • from: "authors": Указывает коллекцию для объединения.
  • localField: "author_id": Указывает поле из входных документов (из коллекции books).
  • foreignField: "_id": Указывает поле из документов в коллекции "from" (коллекция authors).
  • as: "author_details": Указывает имя нового поля-массива, которое будет добавлено к выводу. Этот массив будет содержать соответствующие документы авторов.

Пример вывода для одного из документов:

[
  {
    "_id": ObjectId("..."),
    "title": "Pride and Prejudice",
    "author_id": ObjectId("660a1f5c9b8f8b1234567890"),
    "year": 1813,
    "author_details": [
      {
        "_id": ObjectId("660a1f5c9b8f8b1234567890"),
        "name": "Jane Austen",
        "nationality": "British"
      }
    ]
  },
  ...
]

Как вы можете видеть, поле author_details представляет собой массив, содержащий полный документ автора. Эта мощная функция позволяет получать исчерпывающие данные без необходимости выполнять несколько запросов из вашего приложения.

Обновление связанных документов

Данные в базе данных редко бывают статичными. На этом шаге вы узнаете, как обновлять документы как в коллекции authors, так и в коллекции books. Поскольку мы используем ссылки (references), вы можете обновить информацию об авторе в одном месте, и все запросы, которые объединяются с этим автором, автоматически отразят это изменение.

Давайте добавим год рождения в документ Джейн Остин. Мы будем использовать метод updateOne с оператором $set для добавления нового поля без перезаписи всего документа.

db.authors.updateOne(
  { name: "Jane Austen" },
  {
    $set: {
      birth_year: 1775
    }
  }
);

Пример вывода:

{
  "acknowledged": true,
  "insertedId": null,
  "matchedCount": 1,
  "modifiedCount": 1,
  "upsertedCount": 0
}

Теперь давайте обновим детали книги. Мы добавим genre (жанр) к "Pride and Prejudice".

db.books.updateOne(
  { title: "Pride and Prejudice" },
  {
    $set: {
      genre: "Romance"
    }
  }
);

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

Сначала проверьте автора:

db.authors.findOne({ name: "Jane Austen" });

Затем проверьте книгу:

db.books.findOne({ title: "Pride and Prejudice" });

Вы увидите новые поля birth_year и genre в соответствующих документах. Ссылка author_id в документе книги останется неизменной, сохраняя связь.

Управление и удаление связей документов

Последняя часть управления отношениями — обработка удалений. При удалении документа необходимо учитывать, что происходит с документами, которые на него ссылаются. MongoDB не обеспечивает целостность ссылок автоматически, поэтому эту задачу приходится решать на уровне приложения.

Сначала давайте удалим книгу, сохранив ее автора. Мы удалим книгу "1984".

db.books.deleteOne({ title: "1984" });

Пример вывода:

{ "acknowledged": true, "deletedCount": 1 }

Если теперь вы выполните запрос к коллекции books, вы увидите, что осталась только одна книга. Документ "George Orwell" в коллекции authors остается без изменений.

Теперь рассмотрим более сложный сценарий: удаление автора и всех связанных с ним книг. Это требует многошагового процесса для поддержания целостности данных.

Сначала найдите идентификатор автора и сохраните его в переменной. Мы удалим "Jane Austen".

const authorId = db.authors.findOne({ name: "Jane Austen" })._id;

Далее используйте этот идентификатор для удаления всех книг, связанных с этим автором. Команда deleteMany используется на случай, если у автора несколько книг.

db.books.deleteMany({ author_id: authorId });

Наконец, удалите сам документ автора.

db.authors.deleteOne({ _id: authorId });

Этот ручной многошаговый процесс гарантирует, что вы не оставите "осиротевших" документов книг с недействительными ссылками author_id. Вы можете убедиться, что и книга "Pride and Prejudice", и автор "Jane Austen" были удалены из соответствующих коллекций.

Теперь вы можете выйти из оболочки MongoDB.

exit

Резюме

В этой лабораторной работе вы освоили основные методы работы со связанными документами в MongoDB. Вы начали с создания коллекций authors и books и установления отношения "один ко многим" с использованием ссылок на документы. Затем вы научились извлекать и объединять данные из этих связанных коллекций с помощью этапа агрегации $lookup. Кроме того, вы узнали, как обновлять отдельные документы, не нарушая их связи, и, наконец, как правильно управлять удалениями для поддержания целостности данных путем контролируемого многошагового процесса удаления связанных документов. Эти навыки составляют прочную основу для проектирования и создания более сложных и взаимосвязанных приложений баз данных NoSQL.