【MongoDB】aggregate()と$groupで異なるドキュメント内の同じIDを持つ要素を集約する方法
複数のドキュメントにまたがって同じIDを持つ要素をグループ化したい場合には、MongoDBの aggregate() メソッドと $group ステージを組み合わせるのが効果的です。
本記事では、実際にサンプルコレクションを作成しながら、IDごとにドキュメントを集約する手順をわかりやすく解説します。
1. サンプルコレクションの作成
まずは動作確認用のコレクションを作成し、ドキュメントを挿入していきます。ここでは「id」と「Name」というフィールドを持つドキュメントを6件登録します。
> db.demo602.insertOne({id:1,Name:"Chris"});{
"acknowledged" : true, "insertedId" : ObjectId("5e960080ed011c280a0905c9")
}
> db.demo602.insertOne({id:2,Name:"David"});{
"acknowledged" : true, "insertedId" : ObjectId("5e960086ed011c280a0905ca")
}
> db.demo602.insertOne({id:1,Name:"Bob"});{
"acknowledged" : true, "insertedId" : ObjectId("5e96008ced011c280a0905cb")
}
> db.demo602.insertOne({id:3,Name:"Mike"});{
"acknowledged" : true, "insertedId" : ObjectId("5e960092ed011c280a0905cc")
}
> db.demo602.insertOne({id:2,Name:"John"});{
"acknowledged" : true, "insertedId" : ObjectId("5e960099ed011c280a0905cd")
}
> db.demo602.insertOne({id:1,Name:"Sam"});{
"acknowledged" : true, "insertedId" : ObjectId("5e9600a1ed011c280a0905ce")
}2. 挿入したドキュメントの確認
find() メソッドを使うと、コレクション内のすべてのドキュメントを表示できます。
> db.demo602.find();
実行すると、以下のような出力が得られます。
{ "_id" : ObjectId("5e960080ed011c280a0905c9"), "id" : 1, "Name" : "Chris" }
{ "_id" : ObjectId("5e960086ed011c280a0905ca"), "id" : 2, "Name" : "David" }
{ "_id" : ObjectId("5e96008ced011c280a0905cb"), "id" : 1, "Name" : "Bob" }
{ "_id" : ObjectId("5e960092ed011c280a0905cc"), "id" : 3, "Name" : "Mike" }
{ "_id" : ObjectId("5e960099ed011c280a0905cd"), "id" : 2, "Name" : "John" }
{ "_id" : ObjectId("5e9600a1ed011c280a0905ce"), "id" : 1, "Name" : "Sam" }このように、id: 1 のドキュメントが3件、id: 2 が2件、id: 3 が1件存在していることが確認できます。
3. 同じIDの要素を集約する集計クエリ
それでは、異なるドキュメントに散在する同じIDの要素を集約してみましょう。ポイントは、$group を2段階で使うことです。
- 第1段階: 「id + Name」の組み合わせごとに
$sum: 1で出現回数(Frequency)をカウントします。 - 第2段階: idごとに再グループ化し、
$pushを使って名前と出現頻度を配列(ListOfName)としてまとめます。
具体的なクエリは以下の通りです。
> db.demo602.aggregate([
... { $group: {
... '_id': {id:"$id", "Name" : "$Name"},
... "count" : { "$sum": 1 }
... }},
... { $group: {
... '_id': "$_id.id",
... "ListOfName" : {"$push" : {Name:"$_id.Name", Frequency: "$count"}}
... }},
... ],
... {'allowDiskUse': true}
... );なお、オプションの allowDiskUse: true を指定すると、集計処理がメモリ上限(デフォルト100MB)を超えた場合に一時ファイルをディスクへ書き出せるため、大規模データの集計でもエラーを回避できます。
4. 実行結果
上記の集計クエリを実行すると、次のような結果が出力されます。
{ "_id" : 2, "ListOfName" : [
{ "Name" : "John", "Frequency" : 1 },
{ "Name" : "David", "Frequency" : 1 }
] }
{ "_id" : 3, "ListOfName" : [
{ "Name" : "Mike", "Frequency" : 1 }
] }
{ "_id" : 1, "ListOfName" : [
{ "Name" : "Sam", "Frequency" : 1 },
{ "Name" : "Bob", "Frequency" : 1 },
{ "Name" : "Chris", "Frequency" : 1 }
] }まとめ
MongoDBで同一IDの要素を集約するには、$group ステージを活用するのが基本です。$sum で件数を数え、$push で値を配列に蓄積することで、「各IDにどの名前が何回登場したか」といった情報を柔軟に集計できます。データ量が多い場合は allowDiskUse: true オプションも併せて活用しましょう。
-
MongoDBの集計パイプラインで点数が最も少ない2つのドキュメントを取得する方法
点数(Marks)の昇順に並べ替えたリストを取得するには $sort を使用します。さらに $limit: 2 を組み合わせることで、点数が最も少ない2件のドキュメントだけを表示できます。ここでは、実際に動作を確認できるよう、サンプル用のコレクションを作成していきましょう。1. サンプルコレクションの作成まずは insertOne() メソッドを使って、4件のドキュメントを登録します。> db.demo709.insertOne({Name:"John","Marks":75}); { "acknowledged
-
MongoDBの集計パイプラインで複数フィールドをカウントする方法($facet活用)
MongoDBで複数のフィールドごとにドキュメントをカウントしたい場合、$facet演算子を使うのが効果的です。$facetは、同じ入力ドキュメント群に対して、単一のステージ内で複数の集計パイプラインを同時に処理できる強力な機能です。この記事では、実際にコレクションを作成し、$facetを組み合わせた集計クエリで複数フィールドの値をカウントする手順を解説します。1. サンプルコレクションの作成まず、insertOne()メソッドを使ってサンプルドキュメントを挿入します。各ドキュメントにはdetails1・details2・details3という3つのネストされたフィールドを持たせます。>