MongoDB ドキュメントのリンク

MongoDBBeginner
オンラインで実践に進む

はじめに

この実験 (Lab) では、MongoDB でドキュメント間のリレーションシップを確立するための基本を学びます。このテクニックはリファレンシングとして知られており、複雑で整理されたデータベース構造を構築するために不可欠です。コレクションの作成、他のドキュメントへの参照を持つドキュメントの挿入、$lookup アグリゲーションステージを使用した関連データの取得と結合、そしてこれらのリンクされたドキュメントのライフサイクルの管理を実践します。この実験の終わりには、MongoDB データベースで 1 対多のリレーションシップをモデル化および管理する方法について、確かな理解が得られるでしょう。

ドキュメントリレーションシップの確立

最初のステップでは、2 つの別々のコレクションを作成し、それらの間にリレーションシップを確立します。ここでは、一般的なシナリオである、authorsbooks を持つ図書館データベースをモデル化します。各書籍は著者をリファレンスします。

まず、MongoDB Shell を開きます。このインタラクティブなコマンドラインインターフェースを使用すると、MongoDB インスタンスと対話できます。

mongosh

シェルに入ると、test> プロンプトが表示されます。ここで library_database という新しいデータベースに切り替えます。データベースが存在しない場合、MongoDB は最初にデータを保存する際に自動的に作成します。

use library_database

次に、2 つのドキュメントを挿入して authors コレクションを作成します。各ドキュメントには、リファレンスに使用する ObjectId 型のユニークな _id があります。

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 フィールドに authors コレクションの対応する著者の ObjectId が格納されます。これにより、書籍とその著者との間にリンクが作成されます。

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")
  }
}

これで、2 つのコレクションが正常に作成され、books コレクションのドキュメントが authors コレクションのドキュメントにリンクされました。次のステップのために MongoDB シェルを開いたままにしておいてください。

リンクされたドキュメントのクエリ

リレーションシップを確立したところで、次に論理的なステップは、単一のクエリでリンクされたデータを取得することです。MongoDB のアグリゲーションフレームワークは、この目的のために $lookup ステージを提供しており、これは別のコレクションに対して左外部結合を実行します。

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": 出力に追加する新しい配列フィールドの名前を指定します。この配列には、一致した著者ドキュメントが含まれます。

ドキュメントの 1 つに対する出力例:

[
  {
    "_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 コレクションの両方のドキュメントを更新する方法を学びます。リファレンスを使用しているため、著者の情報を 1 つの場所で更新すると、その著者に結合するすべてのクエリが自動的に変更を反映します。

ジェーン・オースティンのドキュメントに生年を追加しましょう。updateOne メソッドと $set オペレーターを使用して、ドキュメント全体を上書きせずに新しいフィールドを追加します。

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

出力例:

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

次に、書籍の詳細を更新しましょう。「高慢と偏見」にジャンルを追加します。

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 コレクションをクエリすると、書籍が 1 つだけ残っていることがわかります。「George Orwell」の authors コレクションのドキュメントは影響を受けません。

次に、より複雑なシナリオを考えます。著者とその関連書籍すべてを削除する場合です。これは、データの整合性を維持するために複数ステップのプロセスを必要とします。

まず、著者の ID を見つけて変数に格納します。「Jane Austen」を削除します。

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

次に、この ID を使用して、その著者に属するすべての書籍を削除します。著者が複数の書籍を持っている場合に備えて、deleteMany コマンドが使用されます。

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

最後に、著者ドキュメント自体を削除します。

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

この手動の複数ステップのプロセスにより、無効な author_id 参照を持つ「孤立した」書籍ドキュメントを残さないようにすることができます。「高慢と偏見」という書籍と「Jane Austen」という著者の両方がそれぞれのコレクションから削除されたことを確認できます。

これで MongoDB シェルを終了できます。

exit

まとめ

この実験では、MongoDB でリンクされたドキュメントを扱うための基本的なテクニックを学びました。まず authors および books コレクションを作成し、ドキュメントリファレンスを使用して 1 対多の関係を確立しました。次に、$lookup 集計ステージを使用して、これらの関連コレクションからデータを取得および結合する方法を練習しました。さらに、リンクを壊さずに個々のドキュメントを更新する方法、そして最終的に、制御された複数ステップのプロセスで関連ドキュメントを削除することにより、データの整合性を維持するために削除を適切に管理する方法を学びました。これらのスキルは、より洗練された相互接続された NoSQL データベースアプリケーションを設計および構築するための強力な基盤となります。