プログラミング言語を整える
中級MongoDB を整える。
= 表ではなくドキュメントで持つ MongoDB を、find と aggregate を動かしながら学ぶ
MongoDB が初めての方は、先に そもそも MongoDB とは・どんな場面で使うか をどうぞ。
MongoDBとは
データを表の行ではなく JSON に似た「ドキュメント」として保存するデータベースで、Web やアプリのバックエンドでよく使われています。
| 誕生 | 2009年(10gen 社が開発を始め、同社は2013年に社名を MongoDB 社に改めました) |
|---|---|
| 作った人・組織 | 10gen 社(Dwight Merriman、Eliot Horowitz ら)。現在は MongoDB 社が開発しています |
| 種類 | ドキュメント指向データベース(NoSQL) |
| ライセンス | SSPL(Server Side Public License)。2018年から。それ以前は AGPL(GNU Affero General Public License)でした。SSPL は OSI が認めたオープンソースライセンスではありません |
| 得意なこと | 入れ子のオブジェクトや配列を持つデータを1件のドキュメントとしてそのまま保存でき、決まった表の形(スキーマ)を先に作らなくても書き込めることです。 |
| 手元で動かすには | mongosh |
日本での使われ方
- Web サービスやスマートフォンアプリのバックエンドで、アプリケーションのデータを保存する先として使われています。
- 項目が増えたり減ったりしやすいデータを、表の定義を変えずに保存したい場面で選ばれます。
- アクセスログや操作の記録(イベント)のように、形が少しずつ違うデータを大量にためる用途にも使われます。
- センサーや機器から送られてくる IoT のデータを、機器ごとの違いを残したまま保存する例もあります。
使えるクラウドサービス
- MongoDB Atlas:MongoDB 社自身が提供するクラウドサービスで、AWS・Google Cloud・Microsoft Azure の上に MongoDB を作れます。
- Amazon DocumentDB(MongoDB 互換):AWS が提供するドキュメントデータベースです。MongoDB の API と互換がありますが、MongoDB そのものではなく、対応していない機能もあります。
- Azure Cosmos DB for MongoDB:Microsoft Azure の Cosmos DB を、MongoDB 互換の API で使えるようにしたものです。こちらも MongoDB そのものではありません。
研修・学習での位置づけ
MongoDB は、RDB の次に「表を使わないデータベース(NoSQL)」の考え方を学ぶ題材として使われます。問い合わせは SQL ではなく、JavaScript のオブジェクトに似た書き方で条件や集計の手順(パイプライン)を書きます。このページの練習場は MongoDB 本体ではなく、問い合わせを JavaScript で再現したものです。本物の MongoDB では、フィールドの並びや数値の型(整数か小数か)の表示など、細かいところが違うことがあります。
関わる資格
- MongoDB Associate Developer(MongoDB University。アプリケーションから MongoDB を使う開発者向けの認定)
- MongoDB Associate Database Administrator(MongoDB University。MongoDB の運用管理の認定)
ブラウザでMongoDBを動かす
mongosh に打つ db.コレクション.メソッド(…) を、このページの中で再現して動かします。本物の MongoDB ではありません。書いたコードは端末の外には出ません。
// 準備のデータ(products と orders)が入った状態で流れます
db.products.find({ price: { $gte: 100 } }, { _id: 0, name: 1, price: 1 }).sort({ price: -1 })
db.products.insertMany([
{ _id: 1, name: 'りんご', category: '果物', price: 150 },
{ _id: 2, name: 'みかん', category: '果物', price: 80 },
{ _id: 3, name: '牛乳', category: '飲み物', price: 200 },
{ _id: 4, name: 'お茶', category: '飲み物', price: 120 },
{ _id: 5, name: '食パン', category: 'パン', price: 250 }
])
db.orders.insertMany([
{ _id: 1, product_id: 1, qty: 3, ordered_on: ISODate('2026-10-01') },
{ _id: 2, product_id: 3, qty: 1, ordered_on: ISODate('2026-10-01') },
{ _id: 3, product_id: 2, qty: 5, ordered_on: ISODate('2026-10-02') },
{ _id: 4, product_id: 1, qty: 2, ordered_on: ISODate('2026-10-03') },
{ _id: 5, product_id: 4, qty: 4, ordered_on: ISODate('2026-10-03') }
]) 結果
「実行」を押すと、準備のデータ(下の「準備(全お題共通)」)を入れてから、エディタのコードを流します。
この練習場でできること・できないこと
- 本物の MongoDB ではなく、問い合わせ(find の条件・射影・並べ替え)と集計のパイプライン(aggregate)を mingo で再現したものです。
- 使えるのは insertOne・insertMany・find・findOne・countDocuments・distinct・updateOne・updateMany・replaceOne・deleteOne・deleteMany・aggregate・drop・createIndex です。インデックスは作った名前を返すだけで、速さは変わりません。
- 表示は mongosh に寄せていますが、1件を1行で出すなど細部は本物と違います。フィールドの並びや数値の型(int と double の区別)も本物と同じとは限りません。
- JavaScript としては実行しません(const や関数、ループは使えません)。データはこのページを開いている間だけ続きます。
そもそも MongoDB とは
表ではなく、入れ子の文書(ドキュメント)をそのまま保存するデータベース。
MongoDB(モンゴディービー)は、SQL を使わないNoSQLのうち、ドキュメント指向と呼ばれるデータベースです。JSON に近い形のデータ(内部的には BSON という形式)を、一件ずつの「ドキュメント」として保存します。表の列をあらかじめ細かく決めなくてもよく、データの形が変わりやすいアプリで使われます。
リレーショナルデータベース(RDB)は、データを複数の表に分けて、JOIN で結び付けます。MongoDB は、関連するデータを入れ子にして一か所にまとめて持つ考え方です。Redis のようにキーと値だけを素早く扱うデータベースとも違います。
どこで・どんな場面で使うか
-
Web・モバイルアプリの裏側
ユーザーごとに項目が違う設定や、頻繁に仕様が変わるデータの保存に使われます。
-
商品カタログ・コンテンツ
商品ごとに属性が違うデータを、一件のドキュメントにまとめて持てます。
-
ログ・イベントの保存
形がそろっていない記録を、そのまま溜めておく用途にも使われます。
-
クラウドのサービス
MongoDB 社の MongoDB Atlas をはじめ、運用を任せられるサービスがあります。
-
求人での位置づけ
Node.js などの Web 開発の募集で、使える技術として名前が出ます。
向いている場面・向いていない場面
- 向いている場面:データの形が変わりやすい、入れ子の構造をそのまま保存したい、まず素早く作り始めたいアプリ。
- 向いていない場面:複数の表を何度も結び付けて集計する用途。こちらは RDB と SQL のほうが書きやすいことが多いです。
- 使い分けが大事。NoSQL は RDB の代わりではなく、場面に応じて選ぶ別の道具です。
上の練習場では、MongoDB の問い合わせの動きをブラウザの中で再現して試せます(本物の MongoDB ではありません)。下のお題のコマンドを流して、結果を確かめてみてください。
6つのお題をMongoDBで書く
PostgreSQL・MySQL・DuckDB・MongoDB・Redis の5ページで、同じ商品と注文のデータに同じ6つのお題を並べています。SQL の方言の違いや、SQL を使わないデータベースでの考え方の違いがそのまま比べられます。
上の練習場は本物の MongoDB ではなく、問い合わせの動きを mingo(MongoDB の問い合わせ言語の JavaScript 実装)で再現したものです。この環境では MongoDB 本体を動かせなかったため、表示している結果は再現の出力で、mongosh の表示に寄せた形です。フィールドの並びや数値の型などの細部は、本物と違うことがあります。
準備(全お題共通)データを入れるコード。お題2〜6・クイズ・エラーの例は、これを流した後に続けて流した結果です。
db.products.insertMany([
{ _id: 1, name: 'りんご', category: '果物', price: 150 },
{ _id: 2, name: 'みかん', category: '果物', price: 80 },
{ _id: 3, name: '牛乳', category: '飲み物', price: 200 },
{ _id: 4, name: 'お茶', category: '飲み物', price: 120 },
{ _id: 5, name: '食パン', category: 'パン', price: 250 }
])
db.orders.insertMany([
{ _id: 1, product_id: 1, qty: 3, ordered_on: ISODate('2026-10-01') },
{ _id: 2, product_id: 3, qty: 1, ordered_on: ISODate('2026-10-01') },
{ _id: 3, product_id: 2, qty: 5, ordered_on: ISODate('2026-10-02') },
{ _id: 4, product_id: 1, qty: 2, ordered_on: ISODate('2026-10-03') },
{ _id: 5, product_id: 4, qty: 4, ordered_on: ISODate('2026-10-03') }
]) mongosh
db.products.insertMany([
{ _id: 1, name: 'りんご', category: '果物', price: 150 },
{ _id: 2, name: 'みかん', category: '果物', price: 80 },
{ _id: 3, name: '牛乳', category: '飲み物', price: 200 },
{ _id: 4, name: 'お茶', category: '飲み物', price: 120 },
{ _id: 5, name: '食パン', category: 'パン', price: 250 }
])
db.orders.insertMany([
{ _id: 1, product_id: 1, qty: 3, ordered_on: ISODate('2026-10-01') },
{ _id: 2, product_id: 3, qty: 1, ordered_on: ISODate('2026-10-01') },
{ _id: 3, product_id: 2, qty: 5, ordered_on: ISODate('2026-10-02') },
{ _id: 4, product_id: 1, qty: 2, ordered_on: ISODate('2026-10-03') },
{ _id: 5, product_id: 4, qty: 4, ordered_on: ISODate('2026-10-03') }
])
db.products.find() 結果
{
acknowledged: true,
insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
}
{
acknowledged: true,
insertedIds: { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
}
[
{ _id: 1, name: 'りんご', category: '果物', price: 150 },
{ _id: 2, name: 'みかん', category: '果物', price: 80 },
{ _id: 3, name: '牛乳', category: '飲み物', price: 200 },
{ _id: 4, name: 'お茶', category: '飲み物', price: 120 },
{ _id: 5, name: '食パン', category: 'パン', price: 250 }
]
MongoDB には CREATE TABLE に当たる準備が要らず、コレクション(表に当たるもの)は最初にドキュメントを入れたときに作られます。ドキュメントごとに項目が違っていても入れられます。_id は必ず付く主キーで、書かなければ ObjectId が自動で振られますが、ここでは結果をそろえるため番号を自分で入れています。
同じお題をほかのデータベースで: PostgreSQL・MySQL・DuckDB・Redis
mongosh
db.products.find(
{ price: { $gte: 100 }, category: { $in: ['果物', '飲み物'] } },
{ _id: 0, name: 1, price: 1 }
).sort({ price: -1 }) 結果
[
{ name: '牛乳', price: 200 },
{ name: 'りんご', price: 150 },
{ name: 'お茶', price: 120 }
]
find の1つ目の引数が条件で、$gte(以上)や $in(どれかに一致)のような演算子を使います。複数の条件を並べると「かつ」になります。2つ目の引数は返す項目の指定(射影)で、_id は書かないと必ず付くため、要らないときは _id: 0 と書きます。sort の -1 は大きい順です。
同じお題をほかのデータベースで: PostgreSQL・MySQL・DuckDB・Redis
mongosh
db.products.aggregate([
{ $group: { _id: '$category', count: { $sum: 1 }, avg_price: { $avg: '$price' } } },
{ $sort: { _id: 1 } }
]) 結果
[
{ _id: 'パン', count: 1, avg_price: 250 },
{ _id: '果物', count: 2, avg_price: 115 },
{ _id: '飲み物', count: 2, avg_price: 160 }
]
集計は aggregate に処理の段階(ステージ)を配列で並べて書きます。$group の _id にまとめる基準の項目を "$category" のように $ 付きで書き、$sum: 1 で件数、$avg で平均を出します。$avg の結果は、元が整数でも小数(double)になります。$group の出力の順は決まっていないため、$sort で並べています。
同じお題をほかのデータベースで: PostgreSQL・MySQL・DuckDB・Redis
mongosh
db.orders.aggregate([
{ $lookup: { from: 'products', localField: 'product_id', foreignField: '_id', as: 'product' } },
{ $unwind: '$product' },
{ $project: { _id: 1, name: '$product.name', qty: '$qty', amount: { $multiply: ['$qty', '$product.price'] } } },
{ $sort: { _id: 1 } }
]) 結果
[
{ _id: 1, name: 'りんご', qty: 3, amount: 450 },
{ _id: 2, name: '牛乳', qty: 1, amount: 200 },
{ _id: 3, name: 'みかん', qty: 5, amount: 400 },
{ _id: 4, name: 'りんご', qty: 2, amount: 300 },
{ _id: 5, name: 'お茶', qty: 4, amount: 480 }
]
$lookup は、別のコレクションから項目の値が一致するドキュメントを探し、配列として付け加えます(ここでは product)。一致は1件なので $unwind で配列をほどき、$project で名前・個数・金額($multiply で掛け算)を作っています。MongoDB では、よく一緒に読むデータは1つのドキュメントに入れ子で持たせる(埋め込む)設計がよく使われ、$lookup は分けて保存したデータを結びつける必要があるときに使います。
同じお題をほかのデータベースで: PostgreSQL・MySQL・DuckDB・Redis
mongosh
db.products.find({}, { name: 1, price: 1 }).sort({ price: -1 }).skip(2).limit(2) 結果
[
{ _id: 1, name: 'りんご', price: 150 },
{ _id: 4, name: 'お茶', price: 120 }
]
sort で並べ、skip で先頭から飛ばす件数、limit で取る件数を指定します。1ページ2件の2ページ目は skip(2).limit(2) です。書く順にかかわらず、MongoDB は sort、skip、limit の順に適用します。ページが深くなると skip で読み飛ばす量が増えるため、前のページの最後の値より小さいものを条件で探す方法もあります。
同じお題をほかのデータベースで: PostgreSQL・MySQL・DuckDB・Redis
mongosh
db.products.updateOne({ _id: 2 }, { $set: { price: 90 } }, { upsert: true })
db.products.updateOne(
{ _id: 6 },
{ $set: { name: 'バナナ', category: '果物', price: 100 } },
{ upsert: true }
)
db.products.find({ _id: { $in: [2, 6] } }) 結果
{
acknowledged: true,
insertedId: null,
matchedCount: 1,
modifiedCount: 1,
upsertedCount: 0
}
{
acknowledged: true,
insertedId: 6,
matchedCount: 0,
modifiedCount: 0,
upsertedCount: 1
}
[
{ _id: 2, name: 'みかん', category: '果物', price: 90 },
{ _id: 6, name: 'バナナ', category: '果物', price: 100 }
]
updateOne に { upsert: true } を付けると、条件に合うドキュメントがあれば更新し、無ければ新しく作ります。_id: 2 は既にあるので価格だけが変わり(matchedCount: 1)、_id: 6 は無いので、条件の _id と $set の中身から新しいドキュメントが作られました(upsertedCount: 1)。$set は書いた項目だけを変え、ほかの項目はそのまま残します。
同じお題をほかのデータベースで: PostgreSQL・MySQL・DuckDB・Redis
出力当てクイズ
準備のデータを入れた後に流したときの、最後のコマンドの結果を選んでください。選ぶと正解と理由が出ます(答えはどこにも送られず、記録もしません)。
問1
db.products.countDocuments({ category: '果物', price: { $lt: 100 } }) 画面に出るのは?
条件を並べると「かつ」になるため、果物で、しかも価格が 100 未満($lt)のものだけを数えます。りんご(150)は外れ、みかん(80)だけが残るので 1 です。「または」にしたいときは $or: [ … ] を使います。
問2
db.products.findOne({ price: { $lt: 100 } }, { _id: 0, name: 1 }) 画面に出るのは?
findOne は条件に合う最初の1件を、配列ではなくドキュメント1つで返します。射影で name: 1 を指定し、_id: 0 で _id を外しているので、残るのは name だけです。
問3
db.products.updateMany({ category: '飲み物' }, { $inc: { price: 10 } })
db.products.findOne({ _id: 4 }, { _id: 0, price: 1 }) 画面に出るのは?
$inc は数値の項目に指定した値を足します。updateMany なので飲み物の2件(牛乳とお茶)の両方が 10 円上がり、お茶は 120 から 130 になります。値を置き換えたいときは $set を使います。
問4
db.products.distinct('category') 画面に出るのは?
distinct は、指定した項目の値から重複を除いた配列を返します。件数ではなく値そのものが返るので、種類の数が知りたいときはこの配列の長さを見ます。
よくあるエラーと直し方
書き始めの人が出会いやすいエラーです。メッセージは英語のまま出るので、どの言葉を手がかりにすればよいかもあわせて載せています。
同じ _id で入れようとした
エラーになるコード
db.products.insertOne({ _id: 1, name: 'ぶどう', category: '果物', price: 400 }) 画面に出るメッセージ(抜粋)
MongoServerError: E11000 duplicate key error collection: test.products index: _id_ dup key: { _id: 1 } 何が起きているか:_id はコレクションの中で重ならないことが保証される主キーで、自動で一意のインデックス(_id_)が付いています。_id: 1 のりんごが既にあるため、入れられませんでした。
直し方:新しい _id を使うか、_id を書かずに入れて ObjectId を自動で振らせます。既にあるものを書き換えたいときは updateOne や replaceOne を使います。
演算子の綴りを間違えた
エラーになるコード
db.products.find({ price: { $gtt: 100 } }) 画面に出るメッセージ(抜粋)
MongoServerError: unknown operator: $gtt
何が起きているか:$gt(より大きい)を $gtt と書き間違えたため、MongoDB のサーバーが知らない演算子として断りました。$ で始まる名前は演算子として扱われます。
直し方:$gt・$gte・$lt・$lte・$ne・$in のように、正しい綴りの演算子を使います。
パイプラインのステージ名を間違えた
エラーになるコード
db.products.aggregate([{ $groupp: { _id: '$category' } }]) 画面に出るメッセージ(抜粋)
MongoServerError: Unrecognized pipeline stage name: '$groupp'
何が起きているか:aggregate の配列に並べた $groupp が、MongoDB の知っているステージ名ではなかったためです。
直し方:$match・$group・$sort・$project・$lookup のように、正しいステージ名に直します。
メソッド名を間違えた
エラーになるコード
db.products.fnd({ price: 150 }) 画面に出るメッセージ(抜粋)
TypeError: db.products.fnd is not a function
何が起きているか:find を fnd と書き間違えたためです。mongosh では db.products のあとに続く名前が JavaScript のメソッドとして呼ばれるので、存在しない名前だとサーバーに送る前に JavaScript のエラーになります。
直し方:メソッド名の綴りを見直します。mongosh では途中まで打って Tab キーを押すと候補が表示されます。
ほかのデータベースの解説
- PostgreSQL 1986年にカリフォルニア大学バークレー校で始まった研究プロジェクト POSTGRES が元で・リレーショナルデータベース
- MySQL 1995年・リレーショナルデータベース
- DuckDB 2019年・組み込み型の分析用
- Redis 2009年・インメモリのキーバリューストア
5つのデータベースと SQLite を並べた比較は データベースを整える にあります。 書いたコードはどこにも送られません(このページに入力欄はクイズの選択だけで、選んだ答えも記録しません)。
このツールについて
データを表の行ではなく JSON に似た「ドキュメント」として保存するデータベースで、Web やアプリのバックエンドでよく使われています。
MongoDB の概要と、同じ6つのお題をドキュメントの書き方で。find・aggregate・$lookup の動きを再現した練習場つき。
主な機能
- 概要(誕生・作った人・種類・ライセンス)と日本での使われ方・クラウドサービス・研修と資格
- 同じ商品と注文のデータで6つのお題(PostgreSQL・MySQL・DuckDB・Redis の同じお題へ移れる)
- 出力当てクイズ4問(正解と理由)
- よくあるエラー4件(実際のメッセージ・原因・直し方)
- find・aggregate・updateOne(upsert)などの動きを再現した練習場(本物ではない)
- 表示は mongosh に寄せた形・送信ゼロ
よくある質問
Q. 練習場では本物の MongoDB が動いているのですか?
A. 動いていません。MongoDB はブラウザの中で動かせないため、find の条件や aggregate のパイプラインを、MongoDB の問い合わせ言語の JavaScript 実装である mingo で再現しています。表示は mongosh に寄せた形で、フィールドの並びや数値の型などの細部は本物と違うことがあります。
Q. このページの結果は本物の MongoDB で確かめたものですか?
A. いいえ。この環境では MongoDB 本体を動かせなかったため、表示している結果は練習場の再現の出力です。ほかのデータベースのページ(本物で照合)とは確かめ方が違うことを、ページにも書いています。
Q. MongoDB に JOIN はありますか?
A. aggregate の $lookup で、別のコレクションの文書を結びつけられます。ただ、MongoDB では関係の深いデータを1つの文書の中に入れておく(埋め込む)設計がよく使われ、$lookup はそれでは足りないときに使います。
入力値はURLの「#」以降に入るためサーバーには送信されません。リンクを開くと同じ状態を復元します。