これ一本でわかる!MongoDBの使い方完全ガイド

はい、承知いたしました。「これ一本でわかる!MongoDBの使い方完全ガイド」と題した、約5000語の詳細な記事を作成します。


これ一本でわかる!MongoDBの使い方完全ガイド

はじめに

現代のWebアプリケーション開発やデータ分析において、データベースは不可欠な要素です。中でも、リレーショナルデータベース(RDBMS)に加えて、近年注目を集めているのがNoSQLデータベースです。NoSQLデータベースは、データの柔軟性、スケーラビリティ、高性能といった特徴を持ち、様々なユースケースで採用されています。

数あるNoSQLデータベースの中でも、特に人気が高く、広く利用されているのがMongoDBです。MongoDBは、ドキュメント指向のデータベースであり、JSONライクな形式でデータを扱うことができます。これにより、アプリケーション開発者は、データの構造を柔軟に変更しながら開発を進めることが可能になります。

この記事は、「これ一本でわかる!」を目標に、MongoDBの基礎から応用までを網羅的に解説する完全ガイドです。MongoDBを初めて使う方から、基本的な操作はできるけれどさらに理解を深めたい方まで、幅広い読者を対象としています。

この記事を通じて、あなたは以下のことを習得できます。

  • MongoDBの基本概念とRDBMSとの違い
  • MongoDBのインストール方法と環境構築
  • MongoDBの基本的な操作(CRUD: 作成、読み取り、更新、削除)
  • 強力なクエリ機能とインデックスによるパフォーマンス最適化
  • アグリゲーションフレームワークを使った高度なデータ集計
  • トランザクションとデータモデリングの考え方
  • MongoDBの管理と運用に関する基礎知識
  • プログラミング言語からのMongoDB利用方法

さあ、MongoDBの世界へ一緒に踏み込みましょう!

MongoDBの基本概念

MongoDBを理解するためには、いくつかの基本的な概念を知っておく必要があります。RDBMSとは異なる用語が出てきますので、それらをしっかりと押さえましょう。

NoSQLとは

NoSQL(Not Only SQL)は、伝統的なRDBMS以外のデータベース管理システムの総称です。NoSQLデータベースは、スケーラビリティ、可用性、柔軟性を重視して設計されており、大量の非構造化データや半構造化データを扱うのに適しています。データモデルによって、キー・バリュー型、ドキュメント指向型、カラム指向型、グラフ型など、様々な種類があります。

MongoDBは、この中でもドキュメント指向型に分類されます。

ドキュメント指向データベースとは

ドキュメント指向データベースは、データを「ドキュメント」という単位で格納します。ドキュメントは、構造化されたデータを表現するための形式であり、MongoDBでは主にBSON (Binary JSON) 形式が使われます。JSON (JavaScript Object Notation) は人間が読みやすいテキスト形式ですが、BSONはJSONに加えて日付型やバイナリデータ型などをサポートし、効率的な格納と操作を可能にしたバイナリ形式です。

ドキュメントの例:

json
{
"_id": ObjectId("60a7b1c8d5f3a4b6c7e8f9a0"),
"name": "山田 太郎",
"age": 30,
"email": "[email protected]",
"address": {
"city": "東京都",
"zip": "100-0001"
},
"interests": ["MongoDB", "プログラミング", "読書"],
"createdAt": ISODate("2023-10-27T10:00:00Z")
}

この例のように、ドキュメントはキーと値のペアの集まりで構成されます。値はプリミティブ型(文字列、数値、真偽値など)、配列、またはネストされたドキュメントにすることができます。これにより、複雑な構造を持つデータも一つのドキュメント内に柔軟に表現できます。

MongoDBの用語説明

MongoDBでは、RDBMSとは異なる独自の用語が使われます。対応関係を理解しておくと、スムーズに学習を進められます。

MongoDB用語 RDBMS用語 説明
ドキュメント (Document) 行 (Row) データの最小単位。キーと値のペアの集まり(BSON形式)。スキーマは柔軟。
コレクション (Collection) テーブル (Table) ドキュメントの集まり。同じ種類のドキュメントを格納する論理的なグループ。スキーマレス(厳密にはスキーマバリデーションは可能)。
データベース (Database) データベース (Database) コレクションの集まり。通常、アプリケーションごとにデータベースを作成する。

つまり、MongoDBのデータ構造は「データベース」の中に「コレクション」があり、その「コレクション」の中に「ドキュメント」が格納されている、という階層構造になります。

スキーマレスの柔軟性

MongoDBの最大の特徴の一つは、「スキーマレス」であることです。RDBMSでは、テーブルを作成する際に厳密なスキーマ(列名、データ型、制約など)を定義し、それに従ってデータを格納する必要があります。一方、MongoDBのコレクションは、格納するドキュメントのスキーマを事前に厳密に定義する必要がありません。同じコレクション内に、異なるフィールドを持つドキュメントを混在させることが可能です(ただし、通常は類似した構造のドキュメントを格納します)。

この柔軟性は、特に開発の初期段階や、頻繁にデータの構造が変わるアジャイル開発において大きなメリットとなります。新しいフィールドを追加したり、既存のフィールドのデータ型を変更したりするのが容易です。

ただし、完全に無秩序なデータ構造はアプリケーション開発を複雑にする可能性があるため、ある程度の規則性を持たせたり、MongoDBのスキーマバリデーション機能を利用したりするのが一般的です。

BSONについて

BSON (Binary JSON) は、JSONをバイナリ形式で表現するための仕様です。JSONのすべてのデータ型をサポートするだけでなく、JSONにはないバイナリデータ型、日付型、ObjectId型などをサポートしています。

なぜBSONを使うのか?
* 効率的な解析: JSONはテキストベースのため解析に時間がかかりますが、BSONはバイナリ形式なのでコンピューターが効率的に解析・生成できます。
* 豊富なデータ型: JSONがサポートしないデータ型(日付、バイナリ、正規表現など)を扱うことができます。MongoDBでドキュメントを一意に識別する _id フィールドに使われる ObjectId もBSON特有の型です。
* メモリ効率: JSONよりもメモリ使用量が少ない場合があります。

MongoDBは内部的にデータをBSON形式で扱いますが、開発者向けツール(mongoshやドライバー)では、JSON形式で入出力することが多いため、JSONに慣れていれば問題なく扱えます。

MongoDBのインストールとセットアップ

MongoDBを使うためには、まずお使いの環境にMongoDBサーバーをインストールする必要があります。ローカル環境にインストールする方法と、クラウドサービスを利用する方法があります。

ローカル環境へのインストール

主要なOS (Windows, macOS, Linux) へのインストール方法を概説します。詳細は公式ドキュメントを参照してください。

Windows:

  1. MongoDB公式サイトからWindows版のインストーラー(MSIファイル)をダウンロードします。
  2. インストーラーを実行します。セットアップタイプは「Complete」または「Custom」を選択します。
  3. インストール先のディレクトリ、データディレクトリ、ログディレクトリを指定します。デフォルトのままでも構いません。
  4. MongoDBをサービスとしてインストールするかを選択します。通常はサービスとしてインストールし、起動時に自動実行されるように設定します。
  5. MongoDB Compass (GUIツール) も一緒にインストールするかを選択します。インストールを推奨します。
  6. インストールが完了したら、WindowsサービスからMongoDBサーバーが起動していることを確認します。
  7. コマンドプロンプトまたはPowerShellを開き、mongosh コマンドを実行してシェルが起動するか確認します。環境変数PATHにMongoDBのbinディレクトリを追加する必要がある場合があります。

macOS:

macOSでは、Homebrewを使ったインストールが最も簡単です。

  1. Homebrewがインストールされていない場合は、公式サイトの手順に従ってインストールします。
  2. ターミナルを開き、以下のコマンドを実行します。
    bash
    brew tap mongodb/brew
    brew install [email protected] # または最新バージョン
  3. MongoDBサーバーを起動します。
    bash
    brew services start mongodb/brew/[email protected]
  4. MongoDBシェルを起動します。
    bash
    mongosh
  5. サーバーが停止しているか確認するには brew services list を、停止するには brew services stop mongodb/brew/[email protected] を使用します。

Linux (Ubuntu/Debian):

aptパッケージマネージャーを使ってインストールする方法を概説します。

  1. MongoDB GPGキーをインポートします。
    bash
    wget -qO - https://www.mongodb.org/static/pgp/server-6.0.asc | sudo apt-key add -

    apt-key は非推奨になりつつあるため、新しい方法については公式ドキュメントを参照してください)
  2. MongoDBリポジトリをリストに追加します。
    bash
    echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu $(lsb_release -cs)/mongodb-org/6.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-6.0.list

    (UbuntuのバージョンやMongoDBのバージョンに合わせて適宜変更してください)
  3. パッケージリストを更新します。
    bash
    sudo apt update
  4. MongoDBパッケージをインストールします。
    bash
    sudo apt install mongodb-org
  5. MongoDBサービスを起動します。
    bash
    sudo systemctl start mongod
  6. 起動時に自動実行されるように設定します。
    bash
    sudo systemctl enable mongod
  7. ステータスを確認します。
    bash
    sudo systemctl status mongod
  8. MongoDBシェルを起動します。
    bash
    mongosh

MongoDB Compassのインストールと使い方

MongoDB Compassは、MongoDBの公式GUIツールです。データベース、コレクション、ドキュメントの表示や操作、スキーマ分析、パフォーマンス分析などが直感的に行えます。インストール時に一緒にインストールできますし、公式サイトから単体でダウンロードしてインストールすることも可能です。

Compassを起動したら、接続情報を入力してMongoDBサーバーに接続します。ローカルでデフォルト設定でインストールした場合、通常は mongodb://localhost:27017 で接続できます。

Compassの主要な機能:

  • Databases: データベースの一覧表示、作成、削除。
  • Collections: コレクションの一覧表示、作成、削除。各コレクション内のドキュメントを閲覧、挿入、更新、削除。
  • Schema: コレクション内のドキュメントのスキーマを分析し、フィールドとそのデータ型の分布などを視覚的に確認。
  • Indexes: コレクションに定義されているインデックスの一覧表示、作成、削除。
  • Explain Plan: クエリの実行計画を表示し、パフォーマンス上のボトルネックを特定。
  • Validation: コレクションにスキーマバリデーションルールを設定。

Compassは、開発中やデータ確認時に非常に役立ちます。ぜひ活用しましょう。

MongoDB Atlas (クラウド) の利用

MongoDB Atlasは、MongoDBが提供するフルマネージドなクラウドデータベースサービスです。サーバーのセットアップや管理の手間を省き、すぐにMongoDBを使い始めることができます。主要なクラウドプロバイダー(AWS, GCP, Azure)上で利用可能です。

Atlasには無料枠(M0クラスター)があり、小規模な開発や学習目的であれば無料で利用できます。

Atlasの利用手順(概要):

  1. MongoDB公式サイトでAtlasアカウントを作成します。
  2. クラスター(MongoDBサーバー群)を作成します。無料枠を選択し、リージョンなどを指定します。
  3. データベースユーザーを作成し、アクセス許可を設定します。
  4. 接続方法を選択します(アプリケーションからの接続、Compassからの接続など)。接続文字列が提供されます。
  5. 提供された接続文字列を使って、アプリケーションやCompassから接続します。

Atlasを利用することで、インフラ管理に時間をかけずにMongoDBの開発に集中できます。

基本的な操作(CRUD)

MongoDBの基本的な操作は、RDBMSと同様にCRUD (Create, Read, Update, Delete) です。これらの操作は、MongoDBシェル (mongosh) またはプログラミング言語のドライバーを使って実行します。ここでは主にmongoshを使った操作を解説します。

mongoshを起動したら、まずはデータベースを選択します。

javascript
use myDatabase

myDatabaseという名前のデータベースが存在しない場合は、このコマンドを実行した後に最初のコレクションを作成したりドキュメントを挿入したりすると、自動的に作成されます。現在使用しているデータベースは db という変数で参照できます。

javascript
db

コレクションを選択するには、db.collectionName の形式で参照します。

ドキュメントの挿入 (Create)

ドキュメントをコレクションに挿入するには、insertOne() または insertMany() メソッドを使用します。

  • insertOne(document): 単一のドキュメントを挿入します。
  • insertMany([document1, document2, ...]): 複数のドキュメントを配列として指定して挿入します。

例:users コレクションにドキュメントを挿入する

“`javascript
// 単一ドキュメントの挿入
db.users.insertOne({
name: “山田 太郎”,
age: 30,
email: “[email protected]
});

// 複数ドキュメントの挿入
db.users.insertMany([
{ name: “佐藤 花子”, age: 25, email: “[email protected]” },
{ name: “田中 一郎”, age: 45, email: “[email protected]” }
]);
“`

insertOne()insertMany() を実行すると、挿入されたドキュメントの _id が返されます。_id フィールドは、ドキュメントを一意に識別するための特別なフィールドです。挿入時に _id を指定しなかった場合、MongoDBが自動的に ObjectId 型の一意な値を生成して割り当てます。

ドキュメントの検索 (Read)

ドキュメントをコレクションから検索するには、find() メソッドを使用します。find() メソッドは、指定した条件に一致するドキュメントをすべて取得します。条件を指定しない場合は、コレクション内のすべてのドキュメントを取得します。

“`javascript
// コレクション内のすべてのドキュメントを取得
db.users.find()

// 整形して表示 (mongoshでのみ有効)
db.users.find().pretty()
“`

find() メソッドには、クエリ条件を引数として指定できます。クエリ条件は、検索したいフィールドと値のペアを持つドキュメント形式で記述します。

“`javascript
// nameが”山田 太郎”のドキュメントを検索
db.users.find({ name: “山田 太郎” })

// ageが30のドキュメントを検索
db.users.find({ age: 30 })

// ageが30かつnameが”山田 太郎”のドキュメントを検索 (AND条件)
db.users.find({ age: 30, name: “山田 太郎” })
“`

ネストされたドキュメントのフィールドを検索するには、ドット記法 (.) を使用します。

例:住所情報を持つドキュメントを挿入し、cityで検索する

“`javascript
db.users.insertOne({
name: “鈴木 次郎”,
address: {
city: “大阪市”,
zip: “530-0001”
}
});

// address.cityが”大阪市”のドキュメントを検索
db.users.find({ “address.city”: “大阪市” })
“`

配列内の要素を検索することも可能です。

例:興味 (interests 配列) に”MongoDB”が含まれるドキュメントを検索

“`javascript
db.users.insertOne({
name: “高橋 三郎”,
interests: [“MongoDB”, “データサイエンス”]
});

db.users.insertOne({
name: “木村 四郎”,
interests: [“JavaScript”, “Node.js”]
});

// interests配列に”MongoDB”を含むドキュメントを検索
db.users.find({ interests: “MongoDB” })
“`

クエリ演算子

より複雑な検索条件を指定するために、クエリ演算子を使用します。演算子はフィールド名の値として指定します。主なクエリ演算子をいくつか紹介します。

  • 比較演算子:
    • $eq: 等しい (デフォルトなので省略可能)
    • $ne: 等しくない
    • $gt: より大きい
    • $gte: より大きいか等しい
    • $lt: より小さい
    • $lte: より小さいか等しい
    • $in: 指定された値のいずれかに一致
    • $nin: 指定された値のいずれにも一致しない

例:

“`javascript
// ageが30より大きいドキュメントを検索
db.users.find({ age: { $gt: 30 } })

// ageが25以上35以下のドキュメントを検索
db.users.find({ age: { $gte: 25, $lte: 35 } }) // 暗黙的にAND条件

// nameが”山田 太郎”または”佐藤 花子”のドキュメントを検索
db.users.find({ name: { $in: [“山田 太郎”, “佐藤 花子”] } })
“`

  • 論理演算子:
    • $and: 全ての条件が真
    • $or: いずれかの条件が真
    • $not: 条件が偽
    • $nor: 全ての条件が偽

例:

“`javascript
// ageが30より大きい かつ nameが”佐藤 花子”でない ドキュメントを検索
db.users.find({ $and: [ { age: { $gt: 30 } }, { name: { $ne: “佐藤 花子” } } ] })

// ageが30以下 または nameが”田中 一郎” のドキュメントを検索
db.users.find({ $or: [ { age: { $lte: 30 } }, { name: “田中 一郎” } ] })
``
複数の条件をカンマで区切って記述すると
$andと同等になりますが、明示的に$and` を使うことで可読性が向上する場合もあります。

  • 要素演算子:
    • $exists: フィールドが存在するかどうか
    • $type: フィールドのデータ型を指定

例:

“`javascript
// emailフィールドが存在するドキュメントを検索
db.users.find({ email: { $exists: true } })

// ageフィールドが数値型 (BSON Type code: 16) のドキュメントを検索
db.users.find({ age: { $type: 16 } })
“`

  • 配列演算子:
    • $all: 配列フィールドが指定されたすべての要素を含む
    • $size: 配列のサイズを指定

例:

“`javascript
db.products.insertOne({ name: “Laptop”, tags: [“electronic”, “computer”, “portable”] });
db.products.insertOne({ name: “Keyboard”, tags: [“electronic”, “computer”] });

// tags配列に”electronic”と”computer”の両方が含まれるドキュメントを検索
db.products.find({ tags: { $all: [“electronic”, “computer”] } });

// tags配列の要素数が3つのドキュメントを検索
db.products.find({ tags: { $size: 3 } });
“`

プロジェクション (特定のフィールドのみ取得)

find() メソッドの第2引数に、取得したいフィールドを指定することで、結果に含まれるフィールドを制限できます。これをプロジェクションと呼びます。

“`javascript
// nameとemailフィールドのみ取得 (_idはデフォルトで含まれる)
db.users.find({}, { name: 1, email: 1 })

// _idフィールドを含めずにnameとemailフィールドのみ取得
db.users.find({}, { name: 1, email: 1, _id: 0 })

// ageフィールド以外のすべてのフィールドを取得
db.users.find({}, { age: 0 })
“`

1 はフィールドを含める、0 はフィールドを含めないを意味します。_id フィールドはデフォルトで含まれるため、除外したい場合は明示的に _id: 0 と指定する必要があります。それ以外のフィールドは、含めるフィールドを一つでも指定すると、指定したフィールドのみが含まれるようになります(_id は例外)。

ソート (Sort)

検索結果を指定したフィールドでソートするには、.sort() メソッドを find() の結果にチェーンします。

“`javascript
// ageフィールドで昇順にソート
db.users.find().sort({ age: 1 })

// ageフィールドで降順にソート
db.users.find().sort({ age: -1 })

// ageで昇順、次にnameで降順にソート
db.users.find().sort({ age: 1, name: -1 })
“`

1 は昇順、-1 は降順を指定します。

スキップ (Skip) とリミット (Limit)

検索結果の一部を取得(ページネーションなど)するには、.skip().limit() メソッドを使用します。

“`javascript
// 最初の5件を取得
db.users.find().limit(5)

// 最初の5件をスキップして、次の5件を取得 (6件目から10件目)
db.users.find().skip(5).limit(5)
“`

これらのメソッドは、大量のデータを少しずつ取得したり、ページング機能を実装したりする際に役立ちます。

ドキュメントの更新 (Update)

ドキュメントを更新するには、updateOne(), updateMany(), または replaceOne() メソッドを使用します。

  • updateOne(filter, update, options): 検索条件に一致する最初のドキュメントを更新します。
  • updateMany(filter, update, options): 検索条件に一致するすべてのドキュメントを更新します。
  • replaceOne(filter, replacement, options): 検索条件に一致する最初のドキュメントを、指定した新しいドキュメントに置き換えます。

更新操作では、特定のフィールドのみを変更するための「更新演算子」をよく使用します。

主な更新演算子:

  • $set: フィールドの値を設定します。フィールドが存在しない場合は追加します。
  • $inc: フィールドの値を指定した量だけ増加(または減少)させます。数値フィールドにのみ有効です。
  • $unset: フィールドを削除します。
  • $push: 配列フィールドに要素を追加します。
  • $addToSet: 配列フィールドに要素を追加しますが、重複する場合は追加しません。
  • $pull: 配列フィールドから指定した値に一致する要素をすべて削除します。
  • $rename: フィールドの名前を変更します。

例:

“`javascript
// nameが”山田 太郎”のドキュメントのageを31に更新
db.users.updateOne(
{ name: “山田 太郎” },
{ $set: { age: 31 } }
)

// ageが30より大きいすべてのドキュメントにstatusフィールドを追加/設定
db.users.updateMany(
{ age: { $gt: 30 } },
{ $set: { status: “active” } }
)

// nameが”佐藤 花子”のドキュメントのageを5増加させる
db.users.updateOne(
{ name: “佐藤 花子” },
{ $inc: { age: 5 } }
)

// nameが”田中 一郎”のドキュメントからemailフィールドを削除
db.users.updateOne(
{ name: “田中 一郎” },
{ $unset: { email: “” } } // 値は何でもよい
)

// nameが”高橋 三郎”のドキュメントのinterests配列に”AI”を追加
db.users.updateOne(
{ name: “高橋 三郎” },
{ $push: { interests: “AI” } }
)

// nameが”高橋 三郎”のドキュメントのinterests配列から”データサイエンス”を削除
db.users.updateOne(
{ name: “高橋 三郎” },
{ $pull: { interests: “データサイエンス” } }
)
“`

replaceOne() は、ドキュメント全体を新しいドキュメントで置き換えます。_id フィールドは変更できません。

javascript
// nameが"鈴木 次郎"のドキュメントを置き換える
db.users.replaceOne(
{ name: "鈴木 次郎" },
{
name: "鈴木 健太",
job: "Engineer"
}
)
// 結果として元のageやaddressフィールドは失われる

ドキュメントの削除 (Delete)

ドキュメントをコレクションから削除するには、deleteOne() または deleteMany() メソッドを使用します。

  • deleteOne(filter): 検索条件に一致する最初のドキュメントを削除します。
  • deleteMany(filter): 検索条件に一致するすべてのドキュメントを削除します。条件を空 {} にすると、コレクション内のすべてのドキュメントを削除します(コレクション自体は残ります)。

例:

“`javascript
// nameが”山田 太郎”のドキュメントを削除
db.users.deleteOne({ name: “山田 太郎” })

// ageが40より大きいすべてのドキュメントを削除
db.users.deleteMany({ age: { $gt: 40 } })

// コレクション内のすべてのドキュメントを削除
db.users.deleteMany({})
“`

コレクション自体を削除するには db.collectionName.drop() を使用します。

javascript
// usersコレクションを削除
db.users.drop()

高度な操作

基本的なCRUD操作を習得したら、より複雑な要件を満たすための高度な機能を見ていきましょう。インデックス、アグリゲーション、トランザクションは、MongoDBを効果的に使う上で非常に重要です。

インデックス (Indexes)

インデックスは、データベースの検索パフォーマンスを大幅に向上させるために使用されます。RDBMSと同様に、MongoDBもインデックスをサポートしています。特定のフィールドで頻繁に検索、ソート、または結合(アグリゲーションでの$lookupなど)を行う場合、そのフィールドにインデックスを作成することを検討すべきです。

インデックスがない場合:
MongoDBは、クエリを満たすドキュメントを見つけるために、コレクション内のすべてのドキュメントを最初からスキャンする必要があります(フルコレクションスキャン)。これは、コレクションが大きくなると非常に遅くなります。

インデックスがある場合:
インデックスは、特定のフィールドの値と、その値を持つドキュメントの場所(または_id)をマッピングしたデータ構造です。これにより、MongoDBはインデックスを高速に検索し、目的のドキュメントに直接アクセスできます。

インデックスの作成

インデックスを作成するには、createIndex() メソッドを使用します。第一引数にインデックスを作成するフィールドとソート順(1:昇順, -1:降順)を指定します。

“`javascript
// nameフィールドに単一フィールドインデックス (昇順) を作成
db.users.createIndex({ name: 1 })

// ageフィールドに単一フィールドインデックス (降順) を作成
db.users.createIndex({ age: -1 })

// nameとageの複合インデックス (name昇順、age降順) を作成
db.users.createIndex({ name: 1, age: -1 })
“`

複合インデックスは、クエリ条件やソート条件に含まれるフィールドの順番が重要です。例えば { name: 1, age: -1 } のインデックスは、{ name: "...", age: ... } のクエリや、{ name: 1, age: -1 }{ name: 1 } のソートに有効ですが、{ age: -1 } のクエリや { age: -1, name: 1 } のソートには有効に使われない場合があります。

インデックスの種類

MongoDBは様々な種類のインデックスをサポートしています。

  • 単一フィールドインデックス: 特定の1つのフィールドに対するインデックス。
  • 複合インデックス: 複数のフィールドに対するインデックス。クエリやソートの条件に含まれるフィールドの組み合わせに応じて作成します。
  • マルチキーインデックス: 配列フィールドにインデックスを作成すると、配列の各要素に対してエントリが作成されるインデックス。配列要素を含むクエリに有効です。
  • テキストインデックス: 文字列フィールドに対する全文検索を可能にするインデックス。$text 演算子と組み合わせて使用します。
  • 地理空間インデックス (2dsphere, 2d): 緯度経度座標などの地理空間データを扱うためのインデックス。位置情報に基づいたクエリ(指定範囲内の地点検索など)に有効です。
  • ユニークインデックス: フィールドの値が一意であることを保証するインデックス。RDBMSのPRIMARY KEYやUNIQUE制約に相当します。
  • 部分インデックス (Partial Indexes): 指定したフィルター条件を満たすドキュメントのみに作成されるインデックス。インデックスのサイズを小さくし、書き込みパフォーマンスを向上させたい場合に利用できます。
  • TTLインデックス: 指定した期間が経過したドキュメントを自動的に削除するためのインデックス。セッション情報やログデータなど、一時的なデータを扱うのに便利です。

例:ユニークインデックスの作成

javascript
// emailフィールドにユニークインデックスを作成
db.users.createIndex({ email: 1 }, { unique: true })

インデックスの確認と削除

コレクションに作成されているインデックスを確認するには getIndexes() を使用します。

javascript
db.users.getIndexes()

インデックスを削除するには dropIndex() を使用します。インデックス名(getIndexes()で確認できる)またはインデックスキーを指定します。

“`javascript
// インデックス名を指定して削除
db.users.dropIndex(“name_1”) // 例: nameフィールド昇順インデックスの名前

// インデックスキーを指定して削除
db.users.dropIndex({ age: -1 })
“`

クエリパフォーマンス分析 (explain())

クエリがどのように実行されているか、どのインデックスが使用されているかなどを分析するには、.explain() メソッドを find() やアグリゲーションパイプラインにチェーンします。

javascript
db.users.find({ age: { $gt: 30 } }).explain("executionStats")

explain() は、クエリの実行計画に関する詳細な情報を返します。特に executionStats モードは、実際にクエリを実行して統計情報(実行時間、スキャンされたドキュメント数、返されたドキュメント数など)を提供するため、パフォーマンスチューニングに非常に役立ちます。効率的なクエリは、スキャンされるドキュメント数が返されるドキュメント数に近い(または少ない)インデックススキャンを示す傾向があります。

アグリゲーション (Aggregation)

アグリゲーションは、MongoDBの強力なデータ集計機能です。複数のドキュメントを処理し、集計結果を計算したり、データを変換したりすることができます。RDBMSのGROUP BYやJOIN、変換操作などに相当する機能を提供します。

アグリゲーションは、「パイプライン (Pipeline)」と呼ばれる一連の「ステージ (Stage)」として表現されます。各ステージはドキュメントのストリームを入力として受け取り、変換や集計処理を行い、新しいドキュメントストリームを出力として次のステージに渡します。

アグリゲーションを実行するには、aggregate() メソッドを使用します。引数として、ステージの配列を指定します。

javascript
db.collection.aggregate([
{ $stage1: { ... } },
{ $stage2: { ... } },
// ...
])

主要なアグリゲーションステージ:

  • $match: クエリ条件に基づいてドキュメントをフィルタリングします。find() と同様のクエリ構文を使用できます。パイプラインの初期段階に置くことで、後続の処理対象ドキュメント数を減らし、パフォーマンスを向上させることが推奨されます。
  • $project: 入力ドキュメントのフィールドを選択、名前変更、計算、または新しいフィールドを追加して、出力ドキュメントの構造を再構築します。取得するフィールドを絞り込むプロジェクションに似ていますが、より高度な変換が可能です。
  • $group: 指定したフィールド(キー)でドキュメントをグループ化し、各グループに対して集計関数を適用します。$sum, $avg, $count, $min, $max, $push などの集計演算子を使用できます。
  • $sort: 指定したフィールドでドキュメントをソートします。sort() メソッドと同様の構文です。
  • $limit: パイプラインを通過するドキュメント数を制限します。
  • $skip: パイプラインを通過する最初のドキュメント数をスキップします。
  • $unwind: 配列フィールドを持つドキュメントを、配列の各要素ごとに分割して複数のドキュメントに出力します。
  • $lookup: 同じデータベース内の別のコレクションと左外部結合 (left outer join) を行います。RDBMSのJOINに相当します。

例:ユーザーの年齢層ごとの人数をカウントする

javascript
db.users.aggregate([
{
$group: { // ageに基づいてグループ化
_id: "$age", // グループ化のキーとしてageフィールドを使用
count: { $sum: 1 } // 各グループのドキュメント数(人数)をカウント
}
},
{
$sort: { _id: 1 } // age (グループキー) で昇順ソート
}
])

このパイプラインは以下の処理を行います。
1. $group ステージで、各ドキュメントの age フィールドの値に基づいてグループを作成します。
2. 各グループに対して、集計演算子 $sum: 1 を適用し、そのグループに含まれるドキュメントの合計数を計算します。この合計数は count という新しいフィールドに格納されます。グループのキー (_id) は、元のドキュメントの age の値になります。
3. $sort ステージで、集計結果を _id (年齢) の昇順にソートします。

出力例:

json
[
{ "_id": 25, "count": 1 },
{ "_id": 30, "count": 1 },
{ "_id": 31, "count": 1 },
{ "_id": 45, "count": 1 }
]

例:特定の都市に住むユーザーの名前とメールアドレスのみを取得する

javascript
db.users.aggregate([
{
$match: { "address.city": "東京都" } // cityが"東京都"のドキュメントのみをフィルタリング
},
{
$project: { // nameとemailフィールドのみを取得
_id: 0, // _idは含めない
name: 1,
email: 1
}
}
])

アグリゲーションパイプラインは非常に柔軟で強力であり、複雑なデータ変換や分析要件に対応できます。各ステージの出力を理解し、それらを組み合わせて目的の結果を得るのが鍵となります。

トランザクション (Transactions)

MongoDB 4.0以降、ドキュメントレベルの ACID (Atomicity, Consistency, Isolation, Durability) トランザクションがサポートされるようになりました。これにより、複数の操作(読み取りおよび書き込み)を単一の論理的な単位として実行し、すべて成功するか、またはすべて失敗(ロールバック)することを保証できます。

トランザクションは、特に複数のドキュメントやコレクションにまたがる操作において、データの一貫性を保つために重要です。例えば、銀行の振込処理のように、「Aさんの口座から減額する」と「Bさんの口座に増額する」という2つの操作を同時に実行し、どちらか一方だけが成功するような状況を防ぐ必要があります。

MongoDBのトランザクションは、レプリカセット環境で利用可能です。シャーディングされたクラスターでも、MongoDB 4.2以降で分散トランザクションがサポートされています。

トランザクションの基本的な流れ:

  1. セッションを開始する。
  2. トランザクションを開始する。
  3. トランザクション内で必要な読み取り・書き込み操作を実行する。
  4. 操作が成功した場合、トランザクションをコミットする。
  5. 操作中にエラーが発生した場合、トランザクションを中止(ロールバック)する。

例(Node.jsドライバーを使用した疑似コード):

“`javascript
const session = client.startSession(); // セッションを開始

try {
session.startTransaction(); // トランザクションを開始

// トランザクション内の操作 (第二引数にセッションを渡す)
await session.client.db(‘myDatabase’).collection(‘accounts’).updateOne(
{ accountId: ‘A’ },
{ $inc: { balance: -100 } },
{ session }
);

await session.client.db(‘myDatabase’).collection(‘accounts’).updateOne(
{ accountId: ‘B’ },
{ $inc: { balance: 100 } },
{ session }
);

await session.commitTransaction(); // コミット
console.log(‘Transaction committed.’);

} catch (error) {
await session.abortTransaction(); // ロールバック
console.error(‘Transaction aborted:’, error);
} finally {
session.endSession(); // セッションを終了
}
“`

トランザクションはオーバーヘッドを伴うため、すべての操作に適用するのではなく、データの一貫性が絶対に必要となるクリティカルな操作に限定して使用するのが一般的です。

データモデリング

MongoDBでアプリケーションを開発する際に、最も重要な考慮事項の一つがデータモデリングです。ドキュメント指向データベースの柔軟性を活かしつつ、アプリケーションのアクセスパターンに適した設計を行うことで、パフォーマンスと開発効率を最大化できます。

MongoDBのデータモデリングには、主に以下の2つのアプローチがあります。

  1. 組み込み (Embedded) モデル: 関連性の高いデータを一つのドキュメント内にネストして格納する方法です。
  2. 参照 (Referenced) モデル: 関連性の高いデータを別々のドキュメントとして格納し、参照(通常は _id)を使って関連付けを行う方法です。

これらのアプローチは、RDBMSにおける正規化・非正規化の考え方と似ていますが、ドキュメントの構造という点で異なります。

組み込み (Embedded) モデル

例:ブログ記事とそのコメント

“`json
// RDBMS風 (別テーブル)
// articles コレクション
{
“_id”: ObjectId(“…”),
“title”: “MongoDBの使い方”,
“content”: “…”
}
// comments コレクション
{
“_id”: ObjectId(“…”),
“article_id”: ObjectId(“…”), // articlesへの参照
“author”: “…”,
“text”: “…”
}

// MongoDB 組み込みモデル
// articles コレクション
{
“_id”: ObjectId(“…”),
“title”: “MongoDBの使い方”,
“content”: “…”,
“comments”: [ // コメントを記事ドキュメント内に組み込み
{
“_id”: ObjectId(“…”), // コメント自体のID
“author”: “…”,
“text”: “…”,
“createdAt”: ISODate(…)
},
{
“_id”: ObjectId(“…”),
“author”: “…”,
“text”: “…”,
“createdAt”: ISODate(…)
}
// …
]
}
“`

組み込みモデルの利点:

  • 読み取りパフォーマンス: 関連データが単一のドキュメントに格納されているため、結合操作なしで一度に取得でき、読み取りパフォーマンスが向上します。ブログ記事とそのコメントを一緒に表示する場合などに適しています。
  • アトミックな書き込み: ドキュメント内の更新操作はアトミックです(MongoDBのバージョンや構成による制限はあります)。記事にコメントを追加するなど、ドキュメント内での更新は単一の操作として処理されます。

組み込みモデルの欠点:

  • ドキュメントサイズの制限: MongoDBのドキュメントサイズは最大16MBです。組み込むデータが大量になる場合は、この制限に注意が必要です。
  • データの重複: 複数のドキュメントに同じデータを組み込む場合、データの重複が発生する可能性があります。
  • 更新時の複雑さ: 組み込まれた配列内の特定の要素のみを更新・削除したり、配列全体を更新したりする操作は、参照モデルに比べて複雑になる場合があります。また、ドキュメントが頻繁に増加・減少すると、ドキュメントの移動によるオーバーヘッドが発生する可能性があります。

組み込みモデルの適したケース:

  • 1対多(ただし「多」の数が限られる)または1対1の関係。
  • 親エンティティと一緒に子エンティティが頻繁に取得される場合。
  • データの変更が主に親エンティティまたはその一部で行われる場合。
  • 子エンティティが単独で意味を持つことが少ない場合。

参照 (Referenced) モデル

例:注文と品目

“`json
// orders コレクション
{
“_id”: ObjectId(“order1”),
“orderNumber”: “ORD001”,
“customer_id”: ObjectId(“customerA”), // customersコレクションへの参照
“orderDate”: ISODate(…),
// 他の注文情報…
}

// lineitems コレクション
{
“_id”: ObjectId(“lineitem1”),
“order_id”: ObjectId(“order1”), // ordersコレクションへの参照
“product_id”: ObjectId(“productX”), // productsコレクションへの参照
“quantity”: 2,
“price”: 1000
},
{
“_id”: ObjectId(“lineitem2”),
“order_id”: ObjectId(“order1”),
“product_id”: ObjectId(“productY”),
“quantity”: 1,
“price”: 2500
}
“`

参照モデルの利点:

  • データの正規化: 重複を避け、データの一貫性を保ちやすくなります。
  • 大量の関連データを扱える: ドキュメントサイズ制限を気にする必要がありません。関連データが非常に多い場合(例:ユーザーと大量の投稿記事)。
  • 関連データ単独でのアクセス: 関連データが単独で頻繁にアクセスされる場合に適しています。

参照モデルの欠点:

  • 読み取り時の複雑さ/パフォーマンス: 関連データを取得するために、複数のクエリを実行したり、アグリゲーションの $lookup ステージを使用したりする必要があり、読み取りパフォーマンスが低下する可能性があります。
  • 参照の整合性: MongoDBはRDBMSのような外部キー制約を持たないため、参照の整合性はアプリケーション側で管理する必要があります(参照先のドキュメントが存在することを確認するなど)。

参照モデルの適したケース:

  • 1対多(「多」の数が多い、または増加が予測できない)または多対多の関係。
  • 関連データが単独で頻繁にアクセスされる場合。
  • 関連データが非常に大きい場合。
  • データの重複を避けたい場合。

データモデリングの考慮事項

どちらのモデルを採用するかは、アプリケーションのアクセスパターン(どのデータをどのように読み書きするか)を考慮して決定することが非常に重要です。

  • 読み取りパターン: どのようなデータを一緒に取得することが多いか? 一緒に取得する頻度が高いデータは、可能な限り組み込むことを検討します。
  • 書き込みパターン: どのデータがどれくらいの頻度で更新されるか? 頻繁に更新されるデータは、更新が容易な構造を検討します。組み込みモデルの場合、配列の要素追加・削除などの操作は、配列の途中要素の変更によってドキュメント内部での再配置が発生し、パフォーマンスに影響を与える可能性があります。
  • データ量: 関連データの量はどれくらいか? ドキュメントサイズ制限を超えないか?
  • データの一貫性: どの程度のデータの一貫性が要求されるか? 組み込みモデルは単一ドキュメント内のアトミックな操作に適しており、参照モデルで強い一貫性を保つにはトランザクションが必要になる場合があります。
  • データの重複 vs 読み取りパフォーマンス: データ重複を許容して読み取りパフォーマンスを優先するか、正規化して書き込み・ストレージ効率や一貫性を優先するか、トレードオフを考慮します。

MongoDBのデータモデリングは、RDBMSのように厳密なスキーマ設計から始めるのではなく、アプリケーションのユースケースとアクセスパターンに基づいて最適なデータ構造を設計するというアプローチを取ります。最初はシンプルなモデルから始め、要件の変化やパフォーマンス要件に応じてモデルをリファクタリングしていくことも可能です。

管理と運用

MongoDBを本番環境で安定して運用するためには、基本的な管理作業や運用に関する知識が必要です。

ユーザー認証と認可

セキュリティはデータベース運用の基本です。MongoDBでは、ユーザー認証と認可(アクセス権限管理)によってデータベースへのアクセスを制御します。

  • 認証 (Authentication): 接続してきたクライアントが誰であるかを確認するプロセスです。MongoDBはパスワードベース認証(SCRAM-SHA-1/256)や外部認証システムとの連携などをサポートしています。
  • 認可 (Authorization): 認証されたユーザーがどのデータベースやコレクションに対して、どのような操作(読み取り、書き込み、削除など)を許可されているかを制御するプロセスです。MongoDBでは「ロール (Role)」を使用して権限を管理します。

ユーザーの作成と権限設定:

まず、管理者ユーザーで接続し、認証を有効化します。
認証を有効化するには、設定ファイル (mongod.conf) を編集し、security.authorization: enabled を設定してmongodプロセスを再起動します。

次に、認証データベース(デフォルトでは admin データベース)に管理ユーザーを作成します。

“`javascript
use admin

// 管理ユーザーの作成
db.createUser(
{
user: “admin”,
pwd: “password”, // 安全なパスワードを使用
roles: [ { role: “root”, db: “admin” } ] // 全てのデータベースに対する管理権限
}
)
“`

アプリケーションが使用するデータベースやコレクションに対して、特定の権限を持つユーザーを作成します。

“`javascript
use myDatabase

// myDatabaseに対する読み書き権限を持つユーザーを作成
db.createUser(
{
user: “myAppUser”,
pwd: “appPassword”, // 安全なパスワードを使用
roles: [ { role: “readWrite”, db: “myDatabase” } ] // myDatabaseに対する読み書き権限
}
)
“`

ユーザー作成後、クライアントは指定されたユーザー名とパスワードで接続する必要があります。mongoshやドライバーからの接続時に認証情報を指定します。

バックアップとリストア

データの損失はビジネスにとって致命的です。定期的なバックアップは必須です。MongoDBにはバックアップ・リストアのためのユーティリティが用意されています。

  • mongodump: MongoDBデータベースのコンテンツをBSONファイルとしてエクスポートします。
  • mongorestore: mongodump でエクスポートしたBSONファイルをMongoDBにインポートします。

例:myDatabase データベースを /data/backup ディレクトリにバックアップ

bash
mongodump --db myDatabase --out /data/backup

例:/data/backup/myDatabase ディレクトリのバックアップをリストア

bash
mongorestore --db myDatabase /data/backup/myDatabase

これらのツールは、データベース全体または特定のコレクションのバックアップ・リストアに使用できます。本番環境では、定期的に自動実行されるようにスケジュール設定することが一般的です。

MongoDB Atlasなどのクラウドサービスを利用している場合は、通常、サービスプロバイダーが提供するバックアップ・リストア機能を利用できます。

監視 (Monitoring)

データベースシステムの健全性とパフォーマンスを維持するためには、継続的な監視が必要です。監視対象としては、サーバーのリソース使用率(CPU、メモリ、ディスクI/O)、MongoDBサーバーの統計情報(接続数、オペレーション数、クエリ実行時間、キャッシュヒット率など)があります。

  • MongoDB Compass: ライブサーバー統計、クエリパフォーマンス分析などの機能を提供します。
  • mongostat / mongotop: mongostatはMongoDBサーバーの様々な統計情報をリアルタイムで表示し、mongotopは最も時間を消費しているオペレーションをレポートします。
  • MongoDB Cloud Manager / Ops Manager: MongoDB Enterprise版やAtlasで利用できる、より高度な監視・管理ツールです。
  • 外部監視ツール: Datadog, Prometheus, Grafanaなどの一般的な監視ツールと連携させることも可能です。

レプリカセット (Replication) の概要

レプリカセットは、データの高可用性と冗長性を提供するためのMongoDBの機能です。複数のMongoDBサーバー(メンバー)がデータの複製を持ち、プライマリメンバーが書き込みを受け付け、セカンダリメンバーに非同期的にデータを複製します。

プライマリメンバーが利用できなくなった場合、レプリカセットは自動的にフェイルオーバーを実行し、セカンダリメンバーの中から新しいプライマリを選出します。これにより、サービスの中断を最小限に抑えることができます。

レプリカセットは、本番環境でのデプロイにおいて必須とされる構成です。読み込み負荷が高い場合、セカンダリメンバーからの読み取りを許可することで、読み込みスケーリングを向上させることも可能です(ただし、データの同期遅延による読み込みの古さに注意が必要です)。

シャーディング (Sharding) の概要

シャーディングは、大規模なデータセットや高いスループット要求に対応するための水平スケーリング機能です。データを複数のサーバー(シャード)に分散して格納・処理することで、単一サーバーの制約を超えた拡張を可能にします。

シャーディングされたクラスターは、以下のコンポーネントで構成されます。

  • シャード (Shards): データを実際に格納するMongoDBレプリカセットです。
  • クエリルーター (mongos): クライアントからのリクエストを受け付け、適切なシャードにルーティングするインターフェイスです。
  • コンフィグサーバー (Config Servers): クラスターのメタデータ(どのデータがどのシャードにあるかなど)を格納します。MongoDB 3.4以降はコンフィグサーバーもレプリカセットとして構成されます。

シャーディングを導入することで、データ量の増加やクエリ負荷の増加に対応できますが、設定や管理はレプリカセットのみの場合よりも複雑になります。

プログラミング言語からの利用

実際のアプリケーション開発では、各種プログラミング言語からMongoDBを操作することになります。MongoDBは主要なプログラミング言語向けに公式ドライバーを提供しています。

  • Node.js (JavaScript)
  • Python
  • Java
  • C# (.NET)
  • PHP
  • Ruby
  • Go
  • …その他多数

ここでは例として、Node.jsドライバーを使った基本的な操作を概説します。

まず、Node.jsプロジェクトにMongoDBドライバーをインストールします。

bash
npm install mongodb

簡単な接続とCRUD操作の例:

“`javascript
const { MongoClient, ServerApiVersion } = require(‘mongodb’);

// ローカルのMongoDBに接続する場合
// const uri = “mongodb://localhost:27017”;

// MongoDB Atlasに接続する場合 (Atlasの接続情報に合わせて変更)
const uri = “mongodb+srv://:@/?retryWrites=true&w=majority”;

const client = new MongoClient(uri, {
serverApi: {
version: ServerApiVersion.v1,
strict: true,
deprecationErrors: true,
}
});

async function run() {
try {
// データベースに接続
await client.connect();
console.log(“MongoDBに接続しました!”);

const database = client.db("myDatabase"); // データベースを選択
const users = database.collection("users"); // コレクションを選択

// --- C: ドキュメントの挿入 ---
const insertResult = await users.insertOne({
  name: "新しいユーザー",
  age: 28,
  email: "[email protected]"
});
console.log("挿入結果:", insertResult);

// --- R: ドキュメントの検索 ---
console.log("\n--- 検索結果 ---");
const findResult = await users.find({ age: { $gt: 25 } }).toArray(); // 25歳より大きいユーザーを検索
console.log(findResult);

// --- U: ドキュメントの更新 ---
const updateResult = await users.updateOne(
  { name: "新しいユーザー" },
  { $set: { age: 29 } }
);
console.log("\n更新結果:", updateResult);

// --- D: ドキュメントの削除 ---
const deleteResult = await users.deleteOne({ name: "新しいユーザー" });
console.log("\n削除結果:", deleteResult);

} finally {
// クライアント接続を閉じる
await client.close();
console.log(“\nMongoDB接続を閉じました。”);
}
}

run().catch(console.error);
“`

各ドライバーのドキュメントには、より詳細な情報や高度な機能(トランザクション、アグリゲーション、ストリームなど)の使い方が記載されていますので、ご使用の言語の公式ドライバーのドキュメントを参照してください。

まとめ

この記事では、MongoDBの基本的な概念から、CRUD操作、インデックス、アグリゲーション、データモデリング、そして管理・運用に関する基礎知識までを網羅的に解説しました。

MongoDBは、その柔軟なドキュメントモデルと水平スケーリング能力により、現代の多様なデータとアプリケーション要件に適応できる強力なデータベースです。特に以下のような場合にMongoDBは適しています。

  • データの構造が頻繁に変化するアジャイル開発: スキーマレスな特性が開発速度向上に寄与します。
  • 大量の非構造化または半構造化データを扱う場合: ドキュメントモデルがデータの格納と取得を容易にします。
  • 高いスケーラビリティが求められる場合: レプリカセットやシャーディングによるスケールアウトが可能です。
  • クラウドネイティブなアプリケーション開発: MongoDB Atlasなどのサービスがクラウド環境での利用を容易にします。
  • 地理空間データやテキストデータなどの特殊なデータ型を扱う場合: 専用のインデックスやクエリ機能が用意されています。

一方で、RDBMSのように厳密なスキーマに基づくデータの整合性保証がデフォルトでは提供されない点や、複雑なリレーションシップを持つデータの扱いは、データモデリングによって考慮が必要となる点に注意が必要です。

MongoDBの学習は、基本的なシェル操作から始め、徐々に高度なクエリ、インデックス、アグリゲーション、データモデリングへと進めていくのが良いでしょう。公式ドキュメントは非常に充実しており、最も正確で詳細な情報源となります。

付録/参考情報

これで、「これ一本でわかる!MongoDBの使い方完全ガイド」は終わりです。この記事が、あなたがMongoDBを理解し、使い始めるための一助となれば幸いです。ぜひ実際に手を動かして、MongoDBの強力さを体験してみてください!


コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール