MongoDBのdistinct()でサブドキュメントのフィールド値を重複なく取得する方法
MongoDBでは、ネストされたサブドキュメント(配列内のオブジェクトなど)のフィールド値から、重複を除いた個別のリストを取得したいケースがよくあります。そんなときに役立つのが distinct() メソッドとドット記法(.)の組み合わせです。この記事では、具体的なサンプルコードとともにその使い方を解説します。
基本構文
サブドキュメントのフィールドを指定するには、「外側のフィールド名.内側のフィールド名」という形式でパスを記述します。
db.yourCollectionName.distinct("yourOuterFieldName.yourInnerFieldName");
ドットで親フィールドと子フィールドをつなぐことで、MongoDBは自動的にネストされた構造の中をたどり、該当するすべての値を抽出してくれます。さらに、結果は自動的に重複排除されるため、ユニークな値の一覧が得られます。
サンプルコレクションの作成
まず、動作を確認するためのコレクションを作成しましょう。ここでは学生情報を格納するドキュメントを3件挿入します。各ドキュメントには StudentPersonalDetails という配列フィールドがあり、その中に複数の学生オブジェクトが含まれています。
> db.getDistinctListOfSubDocumentFieldDemo.insertOne(
{
"StudentId": 101,
"StudentPersonalDetails": [
{
"StudentName": "John",
"StudentAge": 24
},
{
"StudentName": "Carol",
"StudentAge": 21
}
]
}
);
{
"acknowledged" : true,
"insertedId" : ObjectId("5c90a9abb74d7cfe6392d7d8")
}
> db.getDistinctListOfSubDocumentFieldDemo.insertOne(
{
"StudentId": 102,
"StudentPersonalDetails": [
{
"StudentName": "Carol",
"StudentAge": 26
},
{
"StudentName": "Bob",
"StudentAge": 21
}
]
}
);
{
"acknowledged" : true,
"insertedId" : ObjectId("5c90a9ceb74d7cfe6392d7d9")
}
> db.getDistinctListOfSubDocumentFieldDemo.insertOne(
{
"StudentId": 103,
"StudentPersonalDetails": [
{
"StudentName": "Bob",
"StudentAge": 25
},
{
"StudentName": "David",
"StudentAge": 24
}
]
}
);
{
"acknowledged" : true,
"insertedId" : ObjectId("5c90a9e6b74d7cfe6392d7da")
}
3件とも正常に挿入されました。「Carol」と「Bob」は複数のドキュメントにまたがって登場している点に注目してください。これが後ほど重複排除の効果を確認できるポイントになります。
登録したドキュメントの確認
find() メソッドを使うと、コレクション内のすべてのドキュメントを表示できます。pretty() を付けると見やすい整形された形式で出力されます。
> db.getDistinctListOfSubDocumentFieldDemo.find().pretty();
実行結果は以下の通りです。
{
"_id" : ObjectId("5c90a9abb74d7cfe6392d7d8"),
"StudentId" : 101,
"StudentPersonalDetails" : [
{
"StudentName" : "John",
"StudentAge" : 24
},
{
"StudentName" : "Carol",
"StudentAge" : 21
}
]
}
{
"_id" : ObjectId("5c90a9ceb74d7cfe6392d7d9"),
"StudentId" : 102,
"StudentPersonalDetails" : [
{
"StudentName" : "Carol",
"StudentAge" : 26
},
{
"StudentName" : "Bob",
"StudentAge" : 21
}
]
}
{
"_id" : ObjectId("5c90a9e6b74d7cfe6392d7da"),
"StudentId" : 103,
"StudentPersonalDetails" : [
{
"StudentName" : "Bob",
"StudentAge" : 25
},
{
"StudentName" : "David",
"StudentAge" : 24
}
]
}
サブドキュメントのフィールド値を重複なしで取得する
それでは本題です。distinct() に対して "StudentPersonalDetails.StudentName" のようにドット記法でパスを指定して実行します。
> db.getDistinctListOfSubDocumentFieldDemo.distinct("StudentPersonalDetails.StudentName");
実行結果は以下の通りです。
[ "Carol", "John", "Bob", "David" ]
ご覧のとおり、配列内の各オブジェクトに散らばっていた StudentName の値が、1つのフラットな配列として返されました。元のデータでは「Carol」が2回、「Bob」も2回登場していますが、それぞれ1回だけ含まれており、重複が自動的に除去されていることが確認できます。
まとめ
- サブドキュメントや配列内オブジェクトのフィールド値を取得するには、
distinct()にドット記法でパスを指定します。 - 形式は
"親フィールド名.子フィールド名"です。 - 結果は自動的に重複排除され、ユニークな値の配列として返されます。
- 条件を絞り込みたい場合は、第2引数にクエリ条件を渡すことも可能です(例:
distinct("field", { condition }))。
このテクニックは、タグの一覧抽出やカテゴリの集計など、実務でも非常に頻繁に使われるパターンなので、ぜひ覚えておきましょう。
-
MongoDBでドキュメントフィールドの値から別の値(TotalPrice − Discount)を差し引いて計算する方法
MongoDBでは、aggregate()内の $subtract 演算子を使うことで、ドキュメントフィールドの値から別のフィールドの値を差し引いた結果を計算できます。例えば、総額(TotalPrice)から割引額(DiscountPrice)を引いた実質価格を求めるようなケースに便利です。サンプルコレクションの作成まず、ドキュメントを含むコレクションを作成しましょう。以下のコマンドでコレクションにドキュメントを挿入します。> db.demo599.insertOne({TotalPrice:250,DiscountPrice:35});{ ackno
-
MongoDBでグループごとの最大値を取得する方法($sortと$groupの活用)
MongoDBで、重複している個別の要素(グループ)ごとに最大値を取得したい場合、aggregate()メソッドと$sort・$groupステージを組み合わせることで実現できます。この記事では、実際のコード例を通じて、その手順をわかりやすく解説します。サンプルコレクションの作成まず、ドキュメントを含むコレクションを作成します。ここではdemo750というコレクションに、idとvalueフィールドを持つドキュメントを挿入します。> db.demo750.insertOne({id:101,value:50}); { acknowledged : true, inserted