最頻出10項目
| やりたいこと | コマンド |
|---|---|
| 接続 | mongosh |
| DBを選択 | use school |
| コレクション一覧 | show collections |
| 1件検索 | db.users.findOne({ _id: id }) |
| 複数検索 | db.users.find({ active: true }) |
| 1件追加 | db.users.insertOne({ name: "Ada" }) |
| 一部を更新 | db.users.updateOne(filter, { $set: update }) |
| 1件削除 | db.users.deleteOne(filter) |
| インデックス作成 | db.users.createIndex({ email: 1 }) |
| 集計 | db.orders.aggregate(pipeline) |
更新・削除の前には、同じ条件で find() と countDocuments() を実行し、対象を確認します。初めは updateOne()・deleteOne() を優先してください。
接続と確認
mongosh
mongosh "mongodb://localhost:27017/school"
Atlasなどへ接続するときは、ユーザー名やパスワードをコマンドへ直書きしないでください。接続文字列がシェル履歴やプロセス一覧へ残る可能性があります。環境変数や秘密情報管理サービスを使い、認証情報をリポジトリへ保存しません。
show dbs
use school
db
show collections
db.version()
use school だけではDBは作成されません。最初のデータが保存された時点で作成されます。
検索
db.users.findOne({ email: "student@example.com" });
db.users.find(
{ active: true, age: { $gte: 18 } },
{ name: 1, email: 1, _id: 0 },
);
db.users
.find({ role: { $in: ["student", "mentor"] } })
.sort({ createdAt: -1 })
.limit(20);
| 演算子 | 意味 |
|---|---|
$eq / $ne | 等しい / 等しくない |
$gt / $gte | より大きい / 以上 |
$lt / $lte | より小さい / 以下 |
$in / $nin | 一覧に含まれる / 含まれない |
$or / $and | いずれか / すべて |
$exists | フィールドが存在するか |
$elemMatch | 配列要素が複数条件を満たすか |
ユーザー入力から正規表現を作る場合は、特殊文字と計算量に注意してください。先頭一致できる検索や専用の検索機能を検討します。
ドキュメントと配列を検索する
db.students.find({ "profile.city": "Tokyo" });
db.students.find({ tags: "javascript" });
db.students.find({
scores: { $elemMatch: { subject: "math", value: { $gte: 80 } } },
});
ドット記法で入れ子のフィールドを指定できます。配列に単一の値を指定すると、その要素を含むドキュメントに一致します。同じ配列要素が複数条件を満たす必要がある場合は $elemMatch を使います。
存在しないフィールドと null は検索条件によって同時に一致することがあります。区別したい場合は $exists と型の条件を組み合わせてください。
射影・並べ替え・ページング
db.users.find(
{ active: true },
{ name: 1, email: 1 },
);
db.users
.find({ active: true })
.sort({ createdAt: -1, _id: -1 })
.limit(20);
射影は必要なフィールドだけを返し、通信量や不用意なデータ露出を減らします。通常は包含指定と除外指定を混在できませんが、_id: 0 は包含指定と併用できます。
大きなページ番号での skip() は読み飛ばすコストが増えます。大量データでは、並べ替えキーと前回の最後の値を使う方式を検討します。
挿入
db.users.insertOne({
name: "Ada",
email: "student@example.com",
active: true,
createdAt: new Date(),
});
db.users.insertMany([
{ name: "Aki", active: true },
{ name: "Sora", active: false },
]);
insertMany() は途中で失敗した場合の扱いがオプションで変わります。大量投入では、重複や再実行時の結果を事前に決めてください。
更新
const filter = { email: "student@example.com" };
db.users.find(filter);
db.users.countDocuments(filter);
db.users.updateOne(filter, {
$set: { active: true },
$inc: { loginCount: 1 },
});
| 演算子 | 用途 |
|---|---|
$set | 指定フィールドを設定 |
$unset | 指定フィールドを削除 |
$inc | 数値を加算 |
$push | 配列へ追加 |
$addToSet | 重複を避けて配列へ追加 |
$pull | 条件に一致する配列要素を削除 |
db.users.updateOne(
{ email: "student@example.com" },
{ $set: { name: "Ada" } },
{ upsert: true },
);
upsert: true は一致するデータがなければ挿入します。検索条件が不十分だと意図しない新規データが作られるため、一意インデックスと合わせて使います。
replaceOne() はドキュメント全体を置換します。フィールドの消失を避けたい通常の更新では $set を使います。
更新結果の matchedCount と modifiedCount も確認します。
const result = db.users.updateOne(
{ _id: userId, active: false },
{ $set: { active: true, updatedAt: new Date() } },
);
printjson({
matched: result.matchedCount,
modified: result.modifiedCount,
});
matchedCount が0なら条件に一致していません。modifiedCount が0でも、保存済みの値と同じ値を設定しただけの場合があります。
削除
const filter = { _id: ObjectId("507f1f77bcf86cd799439011") };
db.users.find(filter);
db.users.countDocuments(filter);
db.users.deleteOne(filter);
deleteMany({}) は全件を削除します。drop() はコレクション、dropDatabase() は現在のDBを削除するため、バックアップ・接続先・対象件数を確認せず実行しないでください。本番操作では権限を最小化し、復元手順を用意します。
件数と重複しない値
db.users.countDocuments({ active: true });
db.users.estimatedDocumentCount();
db.users.distinct("role");
条件付きの正確な件数には countDocuments()、コレクション全体のおおよその件数を素早く知る用途には estimatedDocumentCount() を使います。
集計パイプライン
db.orders.aggregate([
{ $match: { status: "paid" } },
{
$group: {
_id: "$userId",
total: { $sum: "$amount" },
count: { $sum: 1 },
},
},
{ $sort: { total: -1 } },
{ $limit: 10 },
]);
| ステージ | 用途 |
|---|---|
$match | 条件で絞る |
$project | 出力するフィールドを選ぶ・計算する |
$group | グループごとに集計 |
$sort | 並べ替える |
$limit | 件数を制限 |
$unwind | 配列を1要素ずつ展開 |
$lookup | 別コレクションと結合 |
なるべく早い段階で $match して処理量を減らします。$out と $merge は結果をコレクションへ書き込むため、参照だけの集計と同じ感覚で実行しないでください。
必要なフィールドだけを返す例です。
db.orders.aggregate([
{ $match: { status: "paid" } },
{
$project: {
_id: 0,
orderId: "$_id",
amount: 1,
taxIncluded: { $multiply: ["$amount", 1.1] },
},
},
{ $limit: 20 },
]);
配列を展開して集計するときは $unwind を使います。
db.orders.aggregate([
{ $unwind: "$items" },
{
$group: {
_id: "$items.productId",
quantity: { $sum: "$items.quantity" },
},
},
]);
空配列や対象フィールドがないドキュメントを残す必要がある場合は、$unwind の preserveNullAndEmptyArrays を確認します。
インデックス
db.users.createIndex({ email: 1 }, { unique: true });
db.orders.createIndex({ userId: 1, createdAt: -1 });
db.users.getIndexes();
検索の実行計画を確認します。
db.orders
.find({ userId: userId })
.sort({ createdAt: -1 })
.explain("executionStats");
executionStats の totalDocsExamined、totalKeysExamined、nReturned などを比較します。インデックスは検索を速くする一方、書き込みコストと保存容量を増やします。
db.users.dropIndex("email_1");
インデックス削除は本番性能や一意性に影響します。名前と利用状況を確認し、負荷の低い時間帯や運用手順に従って実行してください。
複合インデックスはフィールドの順序が重要です。実際の絞り込みと並べ替えに合わせ、実行計画で確認します。
db.orders.createIndex({ status: 1, createdAt: -1 });
db.orders.getIndexes();
一意インデックスを既存データへ追加する場合、重複データがあると作成に失敗します。先に重複を調査し、修正方針を決めてください。
コレクションの基本操作
db.createCollection("users");
db.users.countDocuments({});
db.users.estimatedDocumentCount();
明示的な作成は、バリデーションなどのオプションを先に設定したい場合に使います。通常は最初の書き込み時にもコレクションが作られます。
db.createCollection("profiles", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["userId", "name"],
properties: {
userId: { bsonType: "objectId" },
name: { bsonType: "string" },
},
},
},
});
スキーマ検証はアプリ側の検証を置き換えるものではありません。DB側とアプリ側の両方で、役割に応じた検証を行います。
安全確認の順番
更新・削除・書き込みを伴う集計は、次の順番で確認します。
dbと接続先を確認する- 同じフィルタを
find()で確認する countDocuments()で対象件数を確認する- 可能ならステージング環境やバックアップで試す
- まず1件で実行し、結果を再検索する
- 必要な場合だけ複数件へ広げる
アプリケーションから操作する場合は、入力値を検証し、DBユーザーの権限を必要最小限にします。