MongoDB CRUD操作をマスターする:実践的なコマンドガイド

この実践的なコマンドガイドで、MongoDBの基本的なCRUD操作の力を解き放ちましょう。`insert`、`find`、`update`、`delete`コマンドを使ってデータを効率的に管理する方法を学びます。この記事では、明確な説明、実際の例、そしてMongoDBコレクション内のドキュメントを作成、読み取り、更新、削除するためのベストプラクティスを提供します。開発者や管理者に最適な、MongoDBデータ操作をマスターするための頼りになるリソースです。

MongoDB CRUD操作をマスターする:実践的なコマンドガイド

MongoDBは、その柔軟性、スケーラビリティ、パフォーマンスにより、数多くの現代アプリケーションの基盤を形成する人気のNoSQLドキュメントデータベースです。データベースとやり取りする上で中心となるのは、基本的な作成、読み取り、更新、削除(CRUD)操作です。これらのコマンドを習得することは、新機能を開発する開発者からデータを管理する管理者まで、MongoDBを扱うすべての人にとって不可欠です。

このガイドでは、小さなusersコレクションを使用し、実際の作業で必要となるMongoDB CRUDコマンドを紹介します:サインアップフォームからのレコード追加、サポートチケットの背後にあるドキュメントの検索、ドキュメントの残りを上書きせずにフィールドを変更、そしてコレクション全体を誤って削除せずにデータを削除する方法です。例ではmongoshを使用していますが、同じフィルターと更新演算子はアプリケーションドライバーにも適用されます。

前提条件

コマンドに取り掛かる前に、以下が整っていることを確認してください:

  • MongoDBがインストールされ、実行中であること:公式MongoDBウェブサイトからダウンロードするか、MongoDB Atlasのようなクラウドサービスを使用できます。
  • mongosh(MongoDBシェル)がインストールされていること:これはMongoDBの対話型JavaScriptインターフェースです。

MongoDBへの接続とデータベースの選択

まず、ターミナルまたはコマンドプロンプトを開き、mongoshを使用してMongoDBインスタンスに接続します:

mongosh

接続すると、デフォルトのtestデータベースに移動します。新しいデータベースに切り替えるか作成するには、useコマンドを使用します:

use myDatabase;

myDatabaseが存在しない場合、その中に最初のドキュメントを挿入すると、MongoDBが暗黙的に作成します。

作成操作(挿入)

作成操作では、コレクションに新しいドキュメントを追加します。MongoDBは、単一または複数のドキュメントを挿入するメソッドを提供します。

1. db.collection.insertOne()

このメソッドは、コレクションに単一のドキュメントを挿入します。コレクションが存在しない場合、MongoDBが作成します。

// 'users'というコレクションを選択
db.users.insertOne({
  name: "Alice Smith",
  age: 30,
  city: "New York",
  email: "[email protected]",
  interests: ["reading", "hiking"]
});

出力には、acknowledgedステータスと新しいドキュメントのinsertedIdが表示されます。

2. db.collection.insertMany()

このメソッドを使用すると、1回の操作で複数のドキュメントをコレクションに挿入できます。ドキュメントの配列を受け取ります。

db.users.insertMany([
  {
    name: "Bob Johnson",
    age: 24,
    city: "Los Angeles",
    email: "[email protected]",
    interests: ["coding", "gaming"]
  },
  {
    name: "Charlie Brown",
    age: 35,
    city: "New York",
    email: "[email protected]",
    interests: ["cooking", "photography"]
  },
  {
    name: "Diana Prince",
    age: 29,
    city: "London",
    email: "[email protected]",
    interests: ["fitness", "travel"]
  }
]);

これにより、追加されたすべてのドキュメントのinsertedIdsの配列が返されます。

ヒント:MongoDBは、提供しない場合、各ドキュメントに_idフィールド(一意のObjectId)を自動的に追加します。

読み取り操作(検索)

読み取り操作では、コレクションからドキュメントをクエリします。find()メソッドがそのための主要なツールです。

1. db.collection.find()

a. すべてのドキュメントを検索

コレクション内のすべてのドキュメントを取得するには、引数なしでfind()を呼び出します:

db.users.find();

b. クエリフィルターを使用したドキュメントの検索

find()にクエリドキュメントを渡して、ドキュメントを選択するための条件を指定します。これはSQLのWHERE句として機能します。

// ニューヨークのユーザーを検索
db.users.find({ city: "New York" });

// 25歳以上のユーザーを検索
db.users.find({ age: { $gt: 25 } });

// 年齢が25から35の間(35は含まない)のユーザーを検索
db.users.find({ age: { $gt: 25, $lt: 35 } });

// 興味に'coding'を含むユーザーを検索
db.users.find({ interests: "coding" });

// 興味に'reading'と'hiking'の両方を含むユーザーを検索
db.users.find({ interests: { $all: ["reading", "hiking"] } });

// Alice Smithという名前またはロンドン出身のユーザーを検索
db.users.find({
  $or: [
    { name: "Alice Smith" },
    { city: "London" }
  ]
});

一般的なクエリ演算子:

  • $eq:等しい(演算子が指定されていない場合のデフォルト)
  • $ne:等しくない
  • $gt:より大きい
  • $gte:以上
  • $lt:より小さい
  • $lte:以下
  • $in:配列で指定された値のいずれかに一致
  • $nin:配列で指定された値のいずれにも一致しない
  • $and$or$not$nor:論理演算子

c. 射影:特定のフィールドの選択

フィールドのサブセットのみを返すには、find()の2番目の引数として射影ドキュメントを渡します。値1はフィールドを含め、0は除外します。_idフィールドは、明示的に除外しない限りデフォルトで含まれます。

// 名前とメールのみを返し、_idは除外
db.users.find({ city: "New York" }, { name: 1, email: 1, _id: 0 });

d. 並べ替え、制限、スキップ

メソッドをチェーンすることで、より複雑なクエリが可能になります:

// 年齢で降順に並べ替え(1は昇順、-1は降順)
db.users.find().sort({ age: -1 });

// 結果を2ドキュメントに制限
db.users.find().limit(2);

// 最初の2ドキュメントをスキップして残りを返す
db.users.find().skip(2);

// 操作を組み合わせる:ニューヨークのユーザーを検索、年齢で並べ替え、1スキップ、1制限
db.users.find({ city: "New York" }).sort({ age: 1 }).skip(1).limit(1);

2. db.collection.findOne()

このメソッドは、クエリ条件に一致する単一のドキュメントを返します。複数のドキュメントが一致する場合、最初に見つかったものを返します。

db.users.findOne({ name: "Alice Smith" });

更新操作

更新操作は、コレクション内の既存のドキュメントを変更します。単一のドキュメント、複数のドキュメント、またはドキュメント全体を置き換えることができます。

1. db.collection.updateOne()

フィルター条件に一致する単一のドキュメントを更新します。

// Aliceの都市を'San Francisco'に更新
db.users.updateOne(
  { name: "Alice Smith" },
  { $set: { city: "San Francisco", lastUpdated: new Date() } }
);

2. db.collection.updateMany()

フィルター条件に一致するすべてのドキュメントを更新します。

// ニューヨークのすべてのユーザーの年齢を1増やす
db.users.updateMany(
  { city: "New York" },
  { $inc: { age: 1 } }
);

// まだ持っていないユーザーに新しい興味'reading'を追加
db.users.updateMany(
  {}, // すべてのドキュメントに適用
  { $addToSet: { interests: "reading" } }
);

// Bob Johnsonの興味から'gaming'を削除
db.users.updateOne(
  { name: "Bob Johnson" },
  { $pull: { interests: "gaming" } }
);

一般的な更新演算子:

  • $set:ドキュメント内のフィールドの値を設定します。フィールドが存在しない場合は作成します。
  • $inc:フィールドの値を指定された量だけ増やします。
  • $unset:ドキュメントからフィールドを削除します。
  • $push:配列フィールドに値を追加します。
  • $pull:配列から指定されたクエリに一致する値のすべてのインスタンスを削除します。
  • $addToSet:値がまだ存在しない場合にのみ、配列に値を追加します。

3. db.collection.replaceOne()

フィルター条件に一致する単一のドキュメントを新しいドキュメントで置き換えます。置き換えられるドキュメントの_idフィールドは変更できません。

// Bob Johnsonのドキュメントを完全に置き換え
db.users.replaceOne(
  { name: "Bob Johnson" },
  {
    name: "Robert Johnson",
    occupation: "Software Engineer",
    status: "active",
    email: "[email protected]"
  }
);

Upsertオプション

updateOne()updateMany()の両方がupsertオプションをサポートしています。trueに設定され、フィルターに一致するドキュメントがない場合、MongoDBはクエリと更新操作に基づいて新しいドキュメントを挿入します。

db.users.updateOne(
  { name: "David Lee" },
  { $set: { age: 28, city: "Seattle" } },
  { upsert: true } // David Leeが存在しない場合、作成する
);

削除操作

削除操作は、コレクションからドキュメントを削除します。削除操作は元に戻せないため、細心の注意を払ってください。

1. db.collection.deleteOne()

指定されたフィルターに一致する最大1つのドキュメントを削除します。

// 'Robert Johnson'(以前は'Bob Johnson')という名前のユーザーを削除
db.users.deleteOne({ name: "Robert Johnson" });

2. db.collection.deleteMany()

指定されたフィルターに一致するすべてのドキュメントを削除します。

// ロンドンのすべてのユーザーを削除
db.users.deleteMany({ city: "London" });

警告:コレクション内のすべてのドキュメントを削除するには、空のフィルター{}を使用します。この操作は元に戻せないため、細心の注意を払ってください:

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

3. db.collection.drop()

このメソッドは、データベースからコレクション全体を、そのすべてのドキュメントとインデックスを含めて完全に削除します。

db.users.drop(); // 'users'コレクション全体を削除

警告:コレクションの削除は非常に破壊的な操作です。適切なバックアップがあることを確認するか、このコマンドを実行する前に絶対に確信してください。

MongoDB CRUDのベストプラクティス

  • インデックス作成:頻繁にクエリされるフィールドには、インデックスを作成して読み取り操作を大幅に高速化します。db.collection.createIndex({ fieldName: 1 })
  • 射影:必要なデータのみを取得します。射影({ field: 1 })を使用すると、ネットワーク帯域幅とメモリ使用量が削減されます。
  • バッチ操作:多くのドキュメントを挿入、更新、または削除する場合は、個々の操作ではなくinsertMany()updateMany()deleteMany()を使用してオーバーヘッドを削減します。
  • 演算子を理解する:MongoDBの豊富なクエリおよび更新演算子のセットに精通してください。これらはデータを操作する強力な方法を提供します。
  • エラーハンドリング:本番アプリケーションでは、データベース操作に対して常に堅牢なエラーハンドリングを実装してください。
  • スキーマ設計:MongoDBはスキーマレスですが、効率的なクエリとデータの一貫性には、思慮深いスキーマ設計が重要です。

実際のデータベースでCRUDを練習するより安全な方法

MongoDB CRUDの危険な部分は構文ではありません。特にupdateMany()deleteMany()を使用する場合、間違ったデータベースで広範なフィルターを実行することです。私は、既存のデータに触れる書き込みには、3段階の習慣を使うのが好きです。

まず、フィルターを読み取りとして実行します:

db.users.find(
  { city: "New York", status: "inactive" },
  { name: 1, email: 1, city: 1, status: 1 }
).limit(20);

プレビューが予期しないドキュメントを返した場合、そこで停止します。更新を考える前にフィルターを修正してください。何も返さない場合は、db.getName()で正しいデータベースにいること、およびフィールド名が実際のドキュメントと一致していることを確認してください。

次に、一致するドキュメントをカウントします:

db.users.countDocuments({ city: "New York", status: "inactive" });

これにより、おおよその影響範囲がわかります。12人のユーザーを期待していて、カウントが12,000と表示された場合、コマンドは何かが間違っていることを示しています。カウントはバックアップの代わりにはなりませんが、安価なガードレールです。

3番目に、ジョブに最も適した狭いコマンドで書き込みを実行します。一意のメール、アカウントID、または_idが1つのドキュメントに一致する必要がある場合は、updateOne()を使用します。セグメント全体を変更することが意図されている場合にのみ、updateMany()を使用します。

db.users.updateMany(
  { city: "New York", status: "inactive" },
  {
    $set: {
      marketingEmailEnabled: false,
      updatedBy: "ops-maintenance-2025-11-04"
    }
  }
);

updatedByupdatedAt、またはメンテナンスノートを追加することはMongoDBでは必須ではありませんが、後で誰かがフィールドが変更された理由を尋ねたときに役立ちます。

実際のバグを引き起こす一般的な間違い

最初の間違いは、フィールドを更新するつもりでドキュメントを置き換えることです。このコマンドは無害に見えますが、一致するドキュメント全体を表示されたフィールドのみに置き換えます:

db.users.updateOne(
  { email: "[email protected]" },
  { city: "Boston" }
);

現代のMongoDBは、置換スタイルのメソッドを使用しない限り、更新ドキュメントに更新演算子を期待するため、このパターンはコマンドとバージョンによっては失敗する可能性があります。より安全な考え方は簡単です:フィールドをその場で変更する場合は、$set$unset$inc$push$pull、または別の更新演算子を使用します。

db.users.updateOne(
  { email: "[email protected]" },
  { $set: { city: "Boston" } }
);

2番目の間違いは、配列をクエリするときに順序が常に重要であると想定することです。{ interests: ["reading", "hiking"] }は配列を正確に一致させます。{ interests: "reading" }は、配列にその値が含まれているドキュメントに一致します。{ interests: { $all: ["reading", "hiking"] } }は、順序に関係なく両方の値を含む配列に一致します。これらは3つの異なる質問です。

3番目の間違いは、フィルターが一意でない場合にfindOne()が「正しいもの」を返すと想定することです。次を実行すると:

db.users.findOne({ city: "New York" });

MongoDBは1つの一致するドキュメントを返しますが、並べ替えがない場合、そのドキュメントを最新、最古、最も重要、または最も代表的として扱うべきではありません。順序が重要な場合は、明示的に指定します:

db.users.find({ city: "New York" }).sort({ createdAt: -1 }).limit(1);

4番目の間違いは、アプリがすでに遅くなるまでインデックスをスキップすることです。数千のドキュメントを持つコレクションは、非効率的なクエリを隠すことができます。同じクエリは、コレクションが成長すると苦痛になる可能性があります。アプリケーションが頻繁にemailでユーザーを検索する場合は、データモデルがそれを許可するときに一意のインデックスを作成します:

db.users.createIndex({ email: 1 }, { unique: true });

これにより、パフォーマンスとデータ品質の両方が保護されます。重複したメールがすでに存在する場合、コマンドは失敗します。これは、メールを識別子として使用する前に発見したい種類の問題です。

書き込みが実際に何をしたかを確認する

MongoDBの書き込み結果は読む価値があります。更新後、matchedCountmodifiedCountを確認します。matchedCountはフィルターに一致したドキュメントの数を示します。modifiedCountは実際に変更された数を示します。

matchedCount1modifiedCount0の場合、コマンドはまだ問題ない可能性があります。おそらく、フィールドはすでに要求された値を持っていました。matchedCount0の場合、フィルターは何にも一致しませんでした。これは、_idObjectIdではなく文字列として渡された場合によくあります。

db.users.findOne({ _id: ObjectId("6650f1e59d0a41a37c2d8011") });

削除の場合は、deletedCountを確認します。1つの削除されたドキュメントを期待していて、結果がdeletedCount: 0と表示された場合、すぐにより広範な削除を実行しないでください。データベース、コレクション、およびフィルターを再確認してください。

CRUDだけでは不十分な場合

基本的なCRUDコマンドは、ほとんどの日常的なデータ作業をカバーしますが、一部のタスクにはより強力なツールが必要です。一貫性を保つ必要がある複数のコレクションを更新している場合は、レプリカセットまたはシャーディングされたクラスターでのトランザクションを検討してください。多くのドキュメントにわたってデータを再形成している場合は、集約パイプラインの方が、長い一連のクライアントサイドループよりも明確な場合があります。実稼働データを移行している場合は、ログ記録、ドライランモード、バックアップ、およびロールバック計画を含むスクリプトを使用してください。

mongoshでの1回限りの操作では、コマンドを読みやすく保ちます。巧妙なワンライナーはレビューが難しく、回復も困難です。実稼働環境では、退屈なコマンドの方が通常は優れています。

MongoDB CRUDコマンドは、ドキュメント、フィルター、および更新演算子に慣れれば簡単です。実際の作業で重要なスキルは、意図的に行動することです:フィルターをプレビューし、影響をカウントし、最も狭い書き込みコマンドを選択し、結果を読み取り、次の人が何が変更されたかを理解できるように十分なコンテキストを残すことです。