MongoDB: ドキュメント削除とNode.js入門
最終更新:2026-08-26
ドキュメント削除はデータクリーンアップの核心操作です。本コースではNode.jsとMongooseの実践も紹介します。
deleteOne/deleteManyのマスター、Node.jsとMongooseの連携、スキーマ定義、モデルCRUD操作を学びます。
1. 学習内容
- deleteOne / deleteMany:ドキュメントを削除
- findOneAndDelete:削除したドキュメントを原子的に返却
dropでコレクション、dropDatabaseでデータベースを削除- Node.js + Mongoose:MongoDBへの接続
- Mongooseスキーマの定義とモデルの作成
- Mongoose CRUD操作(作成/読み取り/更新/削除)
2. フルスタックエンジニアの実話
(1) 課題:期限切れデータのクリーンアップに複雑なスクリプトが必要
Bobはeコマースプラットフォームで期限切れの注文と無効化されたユーザーアカウントを定期的にクリーンアップする必要があります:
// ❌ 反例:複数回検索してから削除(遅い + 競合状態)
const expiredOrders = await Order.find({ expiryDate: { $lt: new Date() } });
for (const order of expiredOrders) {
await Order.deleteOne({ _id: order._id });
}
// N回のネットワーク往復、N秒の削除操作
(2) MongoDB + Mongooseでの一括削除による解決策
// ✅ 正例:一括削除
const result = await Order.deleteMany({
expiryDate: { $lt: new Date() }
});
// 1回の操作で全ての期限切れ注文を削除
// Mongooseスキーマ定義
const OrderSchema = new mongoose.Schema({
userId: { type: mongoose.Schema.Types.ObjectId, required: true },
item: [{ sku: String, qty: Number, price: mongoose.Schema.Types.Decimal128 }],
total: { type: mongoose.Schema.Types.Decimal128, required: true },
status: { type: String, enum: ['pending', 'paid', 'shipped', 'delivered'], default: 'pending' },
expiryDate: Date,
createdAt: { type: Date, default: Date.now }
}, { timestamps: true });
const Order = mongoose.model('Order', OrderSchema);
graph LR
A[削除操作] --> B[deleteOne<br/>単一削除]
A --> C[deleteMany<br/>一括削除]
A --> D[findOneAndDelete<br/>原子的返却]
A --> E[drop<br/>コレクション削除]
A --> F[dropDatabase<br/>データベース削除]
style D fill:#d4edda
3. deleteOne:単一ドキュメントを削除
概念説明明: deleteOneはフィルター条件に一致する最初のドキュメントを削除します。MongoDBで最も基本的な削除メソッドです。削除は取り消し不可能です。「ゴミ箱」のような仕組みはなく、削除されたドキュメントを直接復元することはできません(バックアップまたはoplogがない限り)。そのため、本番環境での削除操作は極めて慎重に行う必要があります。
仕組み: deleteOneの実行フローは以下の通りです:マッチングフェーズ(フィルターに基づいて最初のドキュメントを特定)→ 削除フェーズ(コレクションからドキュメントを削除)→ インデックス更新(関連するインデックスエントリを削除)→ 書き込み確認。操作全体が単一ドキュメントに対して原子的です。削除後、ディスクスペースは即座に解放されず、再利用可能なスペースとしてマークされます。
sequenceDiagram
participant App as アプリケーション
participant Mongo as MongoDB
participant WT as WiredTiger
App->>Mongo: deleteOne({ sku: "PHONE-001" })
Mongo->>Mongo: フィルターをマッチ(インデックススキャン)
Mongo->>WT: ドキュメントを削除 + インデックスを更新
WT-->>Mongo: 削除を確認
Mongo-->>App: { acknowledged: true, deletedCount: 1 }
| パラメータ | 型 | 説明 |
|---|---|---|
filter |
ドキュメント | 検索条件(必須) |
options |
ドキュメント | writeConcernなど(任意) |
| 戻り値フィールド | 型 | 説明 |
|---|---|---|
acknowledged |
Boolean | 書き込みが確認されたかどうか |
deletedCount |
Number | 削除されたドキュメント数(0または1) |
// === deleteOne基本使用法 ===
db.products.deleteOne({ sku: 'PHONE-001' });
出力:
TEXT 📖 参照専用{ acknowledged: true, deletedCount: 1 }
| 戻り値フィールド | 意味 |
|---|---|
acknowledged |
確認されたかどうか |
deletedCount |
削除されたドキュメント数(0または1) |
▶ サンプル 1:deleteOneの実践
// === 指定_idのドキュメントを削除 ===
db.users.deleteOne({ _id: ObjectId('507f1f77bcf86cd799439011') });
// === 指定条件に基づいて削除(最初のマッチ)===
db.logs.deleteOne({ level: 'debug' });
// === Mongoose相当の記述 ===
const result = await Product.deleteOne({ sku: 'PHONE-001' });
console.log(result.deletedCount); // 1
出力:
TEXT 📖 参照専用1
4. deleteMany:一括削除
概念説明明: deleteManyはフィルター条件に一致する全ドキュメントを削除します。データの一括クリーンアップにおける主要メソッドです。最初のマッチのみを削除するdeleteOneとは異なり、deleteManyは一度に数万件のドキュメントを削除できます。典型的な使用例:期限切れログのクリーンアップ、無効化されたユーザーデータの削除、テストデータの削除など。
仕組み: deleteManyはまず全マッチドキュメントを取得し、その後1件ずつ削除します。削除プロセスはトランザクションではありません。途中で失敗した場合、既に削除されたドキュメントは復元されません。一括削除を行う際は、バッチ単位での実行を検討し、コレクションが長時間ロックされるのを避けてください。
| 観点 | deleteOne | deleteMany |
|---|---|---|
| マッチ範囲 | 最初の1件 | 全マッチ |
| 削除件数 | 0または1 | 0からN |
| 用途 | 単一削除 | 一括クリーンアップ |
| リスク | 低い | 中程度(操作ミスの影響が大きい) |
// === deleteMany基本使用法 ===
db.products.deleteMany({ category: 'Discontinued' });
// === 全ての期限切れ注文を削除 ===
db.orders.deleteMany({
expiryDate: { $lt: new Date() }
});
// === 特定条件を満たす複数ドキュメントを削除 ===
db.logs.deleteMany({
level: { $in: ['debug', 'info'] },
createdAt: { $lt: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000) }
});
▶ サンプル 2:deleteManyの実践
// === 30日前のエントリをクリーンアップ ===
const thirtyDaysAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000);
const result = await Log.deleteMany({ createdAt: { $lt: thirtyDaysAgo } });
console.log(`Deleted ${result.deletedCount} old logs`);
出力:
TEXT 📖 参照専用Deleted 1523 old logs
// === ログアウトしたユーザーのセッションを削除 ===
await Session.deleteMany({ userId: deletedUserId });
// === コレクション内の全ドキュメントを削除(注意して使用!)===
db.products.deleteMany({});
// ⚠️ これによりproductsコレクション内の全ドキュメントが削除されます
5. findOneAndDelete 原子的返却
概念説明明: findOneAndDeleteは特別な削除メソッドで、ドキュメントを削除すると同時に削除されたドキュメントの内容を返却します。これは「先に検索してから削除」の競合状態を解決します。従来のアプローチでは、まずfindOneでドキュメントを取得してからdeleteOneで削除する必要がありました。この間にドキュメントが他の操作によって変更または削除される可能性があります。findOneAndDeleteは検索と削除を単一の原子的操作に結合します。
仕組み: findOneAndDeleteはドキュメントレベルで原子的操作を実行します:マッチするドキュメントを特定 → ドキュメントの内容を記録 → ドキュメントを削除 → 記録した内容を返却。デフォルトでは削除前のドキュメントの状態を返却します。projectionオプションを使用してどのフィールドを返却するかを制御できます。
graph TB
A[ドキュメントを削除して取得する必要がある] --> B{方法の選択}
B --> C[❌ 先に確認してから削除<br/>findOne + deleteOne<br/>競合状態のリスク]
B --> D[✅ findOneAndDelete<br/>原子的操作<br/>競合のリスクなし]
B --> E[✅ findOneAndDelete + sort<br/>原子的操作 + 順序<br/>FIFOキュー]
style D fill:#d4edda
style E fill:#d4edda
| 利点 | 説明 |
|---|---|
| 原子性 | 検索と削除が単一の操作で実行され、競合状態を回避 |
| ドキュメント返却 | 削除されたドキュメントを直接取得、2回目の検索が不要 |
| ソート対応 | sortオプションと併用で順序付き消費が可能 |
| 用途 | キュータスク、メッセージ消費、在庫引当 |
// === findOneAndDelete原子的操作 ===
const deletedDoc = db.products.findOneAndDelete({ sku: 'PHONE-001' });
出力:
TEXT 📖 参照専用{ _id: ..., sku: 'PHONE-001', title: 'Phone', price: 599, ... }
// 削除されたドキュメントを復元(デフォルトは削除前の状態)
console.log(deletedDoc);
出力:
TEXT 📖 参照専用{ _id: ObjectId('...'), sku: 'PHONE-001', title: 'Smartphone X', price: NumberDecimal('599.99'), ... }
// === 存在しない場合はnullを返却 ===
const result = db.products.findOneAndDelete({ sku: 'NOT_EXIST' });
console.log(result); // null
出力:
TEXT 📖 参照専用null
| 利点 | 説明 |
|---|---|
| 原子性 | 検索と削除が単一の操作で実行され、競合状態を回避 |
| ドキュメント返却 | 削除されたドキュメントを直接取得、2回目の検索が不要 |
| 用途 | キュータスク、メッセージ消費、在庫引当 |
▶ サンプル 3:findOneAndDeleteの実践
// === シーン:メッセージキュー(FIFO)===
const message = await Queue.findOneAndDelete(
{ status: 'pending' },
{ sort: { createdAt: 1 } } // 最も古いものから消費
);
// === シーン:タスクを請求 ===
const task = await Task.findOneAndDelete({
status: 'available',
assignee: null
});
if (task) {
console.log(`Claimed task: ${task._id}`);
}
// === Mongoose相当の記述 ===
const message = await Queue.findOneAndDelete(
{ status: 'pending' },
{ sort: { createdAt: 1 } }
);
6. drop コレクションとdropDatabase
概念説明明: dropとdropDatabaseは最も徹底的な削除操作です。dropはコレクション全体(全ドキュメントとインデックスを含む)を削除し、dropDatabaseはデータベース全体(全コレクションを含む)を削除します。deleteMany({})とは異なり、drop操作はデータを削除するだけでなく、コレクションのメタデータ(インデックス定義、スキーマ検証ルール、capped設定など)も削除します。
比較分析:
| 観点 | deleteMany({}) | drop() | dropDatabase() |
|---|---|---|---|
| 削除範囲 | コレクション内の全ドキュメント | コレクション全体 | データベース全体 |
| インデックス保持 | ✅ 保持 | ❌ 全削除 | ❌ 全削除 |
| capped設定保持 | ✅ 保持 | ❌ 削除 | ❌ 削除 |
| スキーマ検証保持 | ✅ 保持 | ❌ 削除 | ❌ 削除 |
| 速度 | 低速(1件ずつ削除) | 高速(即座にスペース解放) | 高速 |
| 復元可能性 | oplogで復元可能 | 復元極めて困難 | 復元極めて困難 |
// === コレクション削除 ===
db.products.drop();
出力:
TEXT 📖 参照専用true
// === データベース削除 ===
db.dropDatabase();
出力:
TEXT 📖 参照専用{ "dropped" : "shopdb", "ok" : 1 }
// === 注意して使用:コレクション内の全データを削除するがコレクション自体は保持 ===
db.products.deleteMany({});
// 同等だがコレクション構造を保持(インデックス、capped設定)
▶ サンプル 4:dropとdeleteManyの使い分け
// シーン:一時テストコレクションのクリーンアップ
// ✅ 推奨:drop(コレクション+インデックスを削除、クリーン)
db.test_results.drop();
// ✅ コレクション構造を保持:deleteMany(ドキュメントのみクリア)
db.user_sessions.deleteMany({});
7. Node.js + Mongoose:MongoDBへの接続
概念概要: MongooseはNode.jsエコシステムで最も人気のあるMongoDB ODM(Object Document Modeling)で、スキーマ定義、データ検証、ミドルウェア、結合検索などの高度な機能を提供します。本節ではMongoDBへの接続から始め、Mongooseの核心概念を段階的に紹介します。
仕組み: MongooseがMongoDBに接続するプロセスは以下のステップで構成されます:接続インスタンスの作成 → TCP接続の確立 → 認証(必要な場合)→ データベースの選択 → 接続プールの初期化 → connectedイベントのトリガー。デフォルトでMongooseは接続プール(通常5〜100接続)を維持し、TCP接続を頻繁に確立・切断することを避けます。
sequenceDiagram
participant App as Node.jsアプリケーション
participant Mongoose as mongoose
participant Mongo as MongoDB
App->>Mongoose: mongoose.connect(uri)
Mongoose->>Mongo: TCP接続を確立
Mongo-->>Mongoose: 接続確認
Mongoose->>Mongo: 認証(必要な場合)
Mongo-->>Mongoose: 認証成功
Mongoose->>Mongoose: 接続プールを初期化
Mongoose-->>App: 'connected'イベントをトリガー
Note over App,Mongoose: 接続準備完了、CRUD実行可能
| 接続方法 | URI形式 | 用途 |
|---|---|---|
| ローカル単独 | mongodb://localhost:27017/shopdb |
開発・テスト |
| 認証付き | mongodb://user:pass@host:27017/db |
本番環境 |
| レプリカセット | mongodb://host1,host2,host3/db?replicaSet=rs0 |
高可用性 |
| SRV接続 | mongodb+srv://user:pass@cluster/db |
MongoDB Atlas |
(1) インストールと接続
# === インストール ===
npm install mongoose
// === 基本接続 ===
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/shopdb')
.then(() => console.log('Connected to MongoDB'))
.catch(err => console.error('Connection error:', err));
(2) 接続イベントの処理
// === 接続イベント監視 ===
const mongoose = require('mongoose');
mongoose.connection.on('connected', () => {
console.log('Mongoose connected to MongoDB');
});
mongoose.connection.on('error', (err) => {
console.error('Mongoose connection error:', err);
});
mongoose.connection.on('disconnected', () => {
console.log('Mongoose disconnected');
});
// === アプリケーション終了時に接続を閉じる ===
process.on('SIGINT', async () => {
await mongoose.connection.close();
console.log('Mongoose connection closed through app termination');
process.exit(0);
});
(3) 接続オプション
// === 本番環境の接続オプション ===
mongoose.connect('mongodb://localhost:27017/shopdb', {
maxPoolSize: 50, // 接続プールサイズ
minPoolSize: 5,
connectTimeoutMS: 10000, // 接続タイムアウト
socketTimeoutMS: 45000, // ソケットタイムアウト
serverSelectionTimeoutMS: 5000,
heartbeatFrequencyMS: 10000
});
8. Mongooseスキーマとモデル
概念説明明: スキーマはMongooseの核心概念で、ドキュメントの構造、フィールド型、デフォルト値、検証ルール、インデックスなどを定義します。モデルはスキーマから作成され、データベース操作のためのインターフェースを提供します(find、create、updateOne、deleteOneなど)。スキーマは「設計図」、モデルは「工場」です。
仕組み: Mongooseはスキーマ定義を内部でMongoDBのドキュメント構造に変換します。type: StringはBSONの文字列型に、required: trueは挿入時の検証ルールに、default: Date.nowはサーバー側のデフォルト値生成に変換されます。モデルのCRUDメソッドを呼び出すと、Mongooseはまずスキーマ検証を実行し、その後MongoDBネイティブドライバのメソッドに変換してサーバーに送信します。
graph TB
A[Schema スキーマ定義] --> B[Model モデル作成]
B --> C[Document ドキュメントインスタンス]
A --> A1[フィールド型<br/>String/Number/Date/ObjectId]
A --> A2[検証ルール<br/>required/enum/min/max]
A --> A3[デフォルト値<br/>default]
A --> A4[インデックス<br/>index/unique]
B --> B1[CRUDメソッド<br/>find/create/update/delete]
C --> C1[インスタンスメソッド<br/>save/remove]
style A fill:#cce5ff
style B fill:#d4edda
(1) スキーマ定義
// === 基本スキーマ ===
const mongoose = require('mongoose');
const { Schema } = mongoose;
const productSchema = new Schema({
sku: { type: String, required: true, unique: true },
title: { type: String, required: true },
description: { type: String, maxlength: 500 },
price: { type: mongoose.Schema.Types.Decimal128, required: true, min: 0 },
category: { type: String, enum: ['Electronics', 'Books', 'Clothing'] },
stock: { type: Number, default: 0, min: 0 },
isActive: { type: Boolean, default: true },
tags: [{ type: String }],
createdAt: { type: Date, default: Date.now },
updatedAt: { type: Date, default: Date.now }
}, {
timestamps: true, // createdAt/updatedAtを自動管理
versionKey: '__v', // バージョンキー
collection: 'products' // コレクション名
});
// === モデル作成 ===
const Product = mongoose.model('Product', productSchema);
(2) スキーマの型
// === サポートされる型 ===
const exampleSchema = new Schema({
string: String,
number: Number,
date: Date,
buffer: Buffer,
boolean: Boolean,
objectId: mongoose.Schema.Types.ObjectId,
decimal: mongoose.Schema.Types.Decimal128,
array: [String],
nested: {
field1: String,
field2: Number
}
});
(3) 検証とインデックス
// === 検証ルール ===
const userSchema = new Schema({
email: {
type: String,
required: [true, 'Email is required'],
unique: true,
lowercase: true,
trim: true,
match: [/^\S+@\S+\.\S+$/, 'Please use a valid email address']
},
age: {
type: Number,
min: [0, 'Age must be positive'],
max: 120
},
role: {
type: String,
enum: {
values: ['user', 'admin', 'guest'],
message: '{VALUE} is not a valid role'
}
}
});
// === インデックス ===
userSchema.index({ email: 1 }, { unique: true });
userSchema.index({ createdAt: -1 });
▶ サンプル 5:完全なスキーマ定義
const mongoose = require('mongoose');
const { Schema } = mongoose;
const orderSchema = new Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
items: [{
sku: { type: String, required: true },
productName: { type: String, required: true },
qty: { type: Number, required: true, min: 1 },
price: { type: mongoose.Schema.Types.Decimal128, required: true }
}],
total: {
type: mongoose.Schema.Types.Decimal128,
required: true,
min: 0
},
status: {
type: String,
enum: ['pending', 'paid', 'shipped', 'delivered', 'cancelled'],
default: 'pending'
},
shippingAddress: {
street: String,
city: String,
state: String,
zip: String,
country: { type: String, default: 'Japan' }
},
notes: { type: String, maxlength: 500 }
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
});
// バーチャルフィールド
orderSchema.virtual('itemCount').get(function() {
return this.items.reduce((sum, item) => sum + item.qty, 0);
});
// ミドルウェア
orderSchema.pre('save', function(next) {
if (this.isModified('status') && this.status === 'delivered') {
this.deliveredAt = new Date();
}
next();
});
const Order = mongoose.model('Order', orderSchema);
9. Mongoose CRUD操作
概念説明明: MongooseのCRUDメソッドはMongoDBネイティブドライバよりも高レベルな抽象化を提供します。save()メソッドはスキーマ検証、ミドルウェア、デフォルト値の生成を自動的に処理します。find()メソッドは結果をMongooseドキュメントオブジェクトに変換し、インスタンスメソッドやバーチャルフィールドへのアクセスを可能にします。
仕組み: MongooseのCRUDメソッドは実際にはMongoDBネイティブドライバを内部的に呼び出していますが、追加のロジックが挟まれています:1) スキーマ検証(設定で無効化可能)、2) ミドルウェア(pre/postフック)、3) 型変換(Decimal128など)、4) キャスト(文字列をObjectIdに自動変換など)、5) ドキュメントハイドレート(プレーンオブジェクトをMongooseドキュメントに変換)。lean()を使用するとハイドレート処理をスキップでき、パフォーマンスが3〜5倍向上します。
(1) 作成
// === 新規ドキュメント作成 ===
const product = new Product({
sku: 'PHONE-001',
title: 'Smartphone X',
price: mongoose.Types.Decimal128.fromString('599.99'),
category: 'Electronics'
});
await product.save();
// === create()メソッド ===
const product = await Product.create({
sku: 'PHONE-002',
title: 'Smartphone Y',
price: mongoose.Types.Decimal128.fromString('499.99')
});
// === 一括作成 ===
const products = await Product.insertMany([
{ sku: 'PHONE-003', title: 'Phone A', price: 299.99 },
{ sku: 'PHONE-004', title: 'Phone B', price: 399.99 }
]);
(2) 読み取り
// === 全ドキュメント検索 ===
const products = await Product.find();
// === 条件付き検索 ===
const electronics = await Product.find({ category: 'Electronics' });
// === 単一ドキュメント検索 ===
const product = await Product.findOne({ sku: 'PHONE-001' });
// === IDで検索 ===
const product = await Product.findById('507f1f77bcf86cd799439011');
// === プロジェクション + ソート + ページネーション ===
const products = await Product.find({ category: 'Electronics' })
.select('sku title price')
.sort({ price: -1 })
.skip(0)
.limit(20)
.lean(); // パフォーマンス最適化
(3) 更新
// === updateOne ===
const result = await Product.updateOne(
{ sku: 'PHONE-001' },
{ $set: { price: mongoose.Types.Decimal128.fromString('549.99') } }
);
// === findByIdAndUpdate ===
const product = await Product.findByIdAndUpdate(
'507f1f77bcf86cd799439011',
{ $inc: { stock: -1 } },
{ new: true } // 更新後のドキュメントを返却
);
// === ドキュメントsave()で更新 ===
const product = await Product.findOne({ sku: 'PHONE-001' });
product.price = mongoose.Types.Decimal128.fromString('499.99');
product.lastUpdated = new Date();
await product.save();
(4) 削除
// === deleteOne ===
const result = await Product.deleteOne({ sku: 'PHONE-001' });
// === deleteMany ===
const result = await Product.deleteMany({ category: 'Discontinued' });
// === findByIdAndDelete ===
const product = await Product.findByIdAndDelete('507f1f77bcf86cd799439011');
// === ドキュメントremove()で削除 ===
const product = await Product.findOne({ sku: 'PHONE-001' });
await product.remove();
▶ サンプル 6:Mongoose CRUD実践
// === シーン:eコマース注文処理 ===
async function createOrder(userId, items) {
const session = await mongoose.startSession();
session.startTransaction();
try {
// 1. 在庫チェック
for (const item of items) {
const product = await Product.findOne({ sku: item.sku }).session(session);
if (!product || product.stock < item.qty) {
throw new Error(`Insufficient stock for ${item.sku}`);
}
}
// 2. 注文作成
const order = await Order.create([{
userId,
items: items.map(item => ({
sku: item.sku,
productName: item.productName,
qty: item.qty,
price: item.price
})),
total: items.reduce((sum, item) => sum + item.qty * item.price, 0),
status: 'pending'
}], { session });
// 3. 在庫引当
for (const item of items) {
await Product.updateOne(
{ sku: item.sku, stock: { $gte: item.qty } },
{ $inc: { stock: -item.qty } }
).session(session);
}
await session.commitTransaction();
return order[0];
} catch (error) {
await session.abortTransaction();
throw error;
} finally {
session.endSession();
}
}
❓ よくある質問
isActive: falseを設定)を使用してください。deleteOneを使用してください。mongoose.connect()の完了を待つか、bufferCommands: falseオプションでバッファリングを無効化してください。save()はスキーマ検証、ミドルウェア、デフォルト値の生成を実行しますが、ドキュメント全体を書き込む必要があります。updateOne()は特定のフィールドのみを更新できますが、検証とミドルウェアはスキップします。検証が必要な場合はsave()を、パフォーマンス重視の場合はupdateOne()を使用してください。drop()を使用してください。コレクション構造(インデックス、capped設定)を保持したい場合はdeleteMany({})を使用してください。drop()の方が高速です。📖 まとめ
- deleteOne:単一ドキュメント削除、findOneAndDelete:原子的返却付き削除
- deleteMany:一括削除、データクリーンアップに最適
- drop:コレクション削除、dropDatabase:データベース削除(注意して使用)
- Mongoose:Node.js + MongoDBの定番ODM、スキーマ定義と検証を提供
- スキーマ:フィールド型、検証ルール、インデックス、ミドルウェアを定義
- モデル:スキーマから作成、CRUDメソッドを提供
- CRUD:create/save、find/findOne、updateOne、deleteOne/deleteMany
📝 練習問題
- 基礎問題(⭐):
deleteOneを使用してSKU「PHONE-001」の商品を削除し、戻り値のdeletedCountを確認してください。 - 基礎問題(⭐):
deleteManyを使用して30日以上前のログレコードを一括削除してください。 - 応用問題(⭐⭐):
findOneAndDeleteを使用してメッセージキューを実装し、最も古いpendingメッセージを消費してください。 - 応用問題(⭐⭐):Mongooseで商品スキーマを定義し(必須フィールド、検証ルール、インデックスを含む)、CRUD操作を実装してください。
- チャレンジ(⭐⭐⭐):トランザクション付きの注文処理APIを実装(在庫チェック、注文作成、在庫引当を原子的に実行)し、完全なエラー処理とロールバックを含めてください。