$unwind(集計)
定義
互換性
次の環境でホストされる配置には $unwind
を使用できます。
MongoDB Atlas はクラウドでの MongoDB 配置のためのフルマネージド サービスです
MongoDB Enterprise: サブスクリプションベースの自己管理型 MongoDB バージョン
MongoDB Community: ソースが利用可能で、無料で使用できる自己管理型の MongoDB のバージョン
構文
フィールドパス オペランドまたはドキュメント オペランドを渡して、配列フィールドを展開できます。
フィールドパス オペランド
配列フィールドパスを$unwind
に渡すことができます。 この構文を使用する場合、フィールド値が null、欠落、または空の配列である場合、 $unwind
はドキュメントを出力しません。
{ $unwind: <field path> }
フィールド パスを指定するときは、フィールド名の前にドル記号$
を付け、引用符で囲みます。
オプション付きドキュメント オペランド
ドキュメントを$unwind
に渡して、さまざまな動作オプションを指定できます。
{ $unwind: { path: <field path>, includeArrayIndex: <string>, preserveNullAndEmptyArrays: <boolean> } }
フィールド | タイプ | 説明 |
---|---|---|
string | 配列フィールドへのフィールドパス。フィールドパスを指定するには、フィールド名の前にドル記号 | |
string | 任意。要素の配列インデックスを保持する新しいフィールドの名前。名前をドル記号 | |
ブール値 |
動作
非配列フィールドパス
オペランドが配列に解決されないが、欠落しているわけでも
null
でもなく、または空の配列でもない場合、$unwind
はオペランドを単一要素の配列として扱います。オペランドが
null
か欠落しているか空の配列になっている場合、$unwind
では preserveNullAndEmptyArrays オプションに設定した動作に従います。
欠落しているフィールド
入力ドキュメントに存在しないフィールド、またはフィールドが空の配列であるフィールドのパスを指定すると、 $unwind
はデフォルトで入力ドキュメントを無視し、その入力ドキュメントのドキュメントを出力しません。
配列フィールドが欠落しているか null か空の配列になっているドキュメントを出力するには、preserveNullAndEmptyArrays オプションを使用します。
例
配列の展開
mongosh
では、次のドキュメントを含むinventory
という名前のサンプル コレクションが作成されます。
db.inventory.insertOne({ "_id" : 1, "item" : "ABC1", sizes: [ "S", "M", "L"] })
次に示す集計では、$unwind
ステージを使用して、 sizes
配列にある要素ごとのドキュメントを出力します。
db.inventory.aggregate( [ { $unwind : "$sizes" } ] )
この操作は次の結果を返します。
{ "_id" : 1, "item" : "ABC1", "sizes" : "S" } { "_id" : 1, "item" : "ABC1", "sizes" : "M" } { "_id" : 1, "item" : "ABC1", "sizes" : "L" }
各ドキュメントは入力ドキュメントと同じですが、sizes
フィールドの値が元の sizes
配列の値に置き換えられています。
欠落値または配列以外の値
clothing
コレクションを考慮します。
db.clothing.insertMany([ { "_id" : 1, "item" : "Shirt", "sizes": [ "S", "M", "L"] }, { "_id" : 2, "item" : "Shorts", "sizes" : [ ] }, { "_id" : 3, "item" : "Hat", "sizes": "M" }, { "_id" : 4, "item" : "Gloves" }, { "_id" : 5, "item" : "Scarf", "sizes" : null } ])
$unwind
では次の場合、sizes
フィールドを単一の要素配列として扱います。
フィールドが存在する、
値が null 以外である、
値が空の配列ではない。
sizes
配列を次のように $unwind
で展開します。
db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )
$unwind
操作は以下を返します。
{ _id: 1, item: 'Shirt', sizes: 'S' }, { _id: 1, item: 'Shirt', sizes: 'M' }, { _id: 1, item: 'Shirt', sizes: 'L' }, { _id: 3, item: 'Hat', sizes: 'M' }
ドキュメント
"_id": 1
では、sizes
は入力された配列です。$unwind
はsizes
フィールドの各要素のドキュメントを返します。ドキュメント
"_id": 3
では、sizes
が単一の要素配列に解決されます。ドキュメント
"_id": 2, "_id": 4
および"_id": 5
は、sizes
フィールドを単一要素の配列に縮小できないため、何も返しません。
注意
{ path: <FIELD> }
構文は任意です。次に示す $unwind
操作は同等です。
db.clothing.aggregate( [ { $unwind: "$sizes" } ] ) db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )
preserveNullAndEmptyArrays
および includeArrayIndex
preserveNullAndEmptyArrays
と includeArrayIndex
の例では、次のコレクションが使用されます。
db.inventory2.insertMany([ { "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] }, { "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] }, { "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" }, { "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") }, { "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null } ])
preserveNullAndEmptyArrays
次の$unwind
操作ではpreserveNullAndEmptyArraysオプションを使用して、 sizes
フィールドが null、欠落、または空の配列であるドキュメントを含めます。
db.inventory2.aggregate( [ { $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true } } ] )
出力には、sizes
フィールドが null、欠落、または空の配列であるドキュメントが含まれます。
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" } { "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") } { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" } { "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") } { "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null }
includeArrayIndex
次の$unwind
操作では、 includeArrayIndexオプションを使用して、出力に配列インデックスを含めます。
db.inventory2.aggregate( [ { $unwind: { path: "$sizes", includeArrayIndex: "arrayIndex" } }])
この操作は sizes
配列を展開し、新しい arrayIndex
フィールドに配列インデックスを含めます。sizes
フィールドが入力された配列に対して解決されず、欠落、値が null、または空の配列でもない場合、arrayIndex
フィールドは null
になります。
{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S", "arrayIndex" : NumberLong(0) } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M", "arrayIndex" : NumberLong(1) } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L", "arrayIndex" : NumberLong(2) } { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M", "arrayIndex" : null }
展開された値によるグループ化
mongosh
では、次のドキュメントを含むinventory2
という名前のサンプル コレクションが作成されます。
db.inventory2.insertMany([ { "_id" : 1, "item" : "ABC", price: NumberDecimal("80"), "sizes": [ "S", "M", "L"] }, { "_id" : 2, "item" : "EFG", price: NumberDecimal("120"), "sizes" : [ ] }, { "_id" : 3, "item" : "IJK", price: NumberDecimal("160"), "sizes": "M" }, { "_id" : 4, "item" : "LMN" , price: NumberDecimal("10") }, { "_id" : 5, "item" : "XYZ", price: NumberDecimal("5.75"), "sizes" : null } ])
次のパイプラインでは sizes
配列を展開し、生成されたドキュメントを展開させたサイズ値でグループ化します。
db.inventory2.aggregate( [ // First Stage { $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true } }, // Second Stage { $group: { _id: "$sizes", averagePrice: { $avg: "$price" } } }, // Third Stage { $sort: { "averagePrice": -1 } } ] )
- 第 1 ステージ:
$unwind
ステージでは、sizes
配列の要素ごとに新しいドキュメントが出力されます。 ステージではpreserveNullAndEmptyArraysオプションを使用して、sizes
フィールドが欠落しているか null か空の配列になっているドキュメントを出力に含めます。 このステージでは、次のドキュメントを次のステージに渡します。{ "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "S" } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "M" } { "_id" : 1, "item" : "ABC", "price" : NumberDecimal("80"), "sizes" : "L" } { "_id" : 2, "item" : "EFG", "price" : NumberDecimal("120") } { "_id" : 3, "item" : "IJK", "price" : NumberDecimal("160"), "sizes" : "M" } { "_id" : 4, "item" : "LMN", "price" : NumberDecimal("10") } { "_id" : 5, "item" : "XYZ", "price" : NumberDecimal("5.75"), "sizes" : null } - 第 2 ステージ:
$group
ステージでは、ドキュメントをsizes
別にグループ化し、各サイズの平均価格を計算します。このステージでは、次のドキュメントを次のステージに渡します。{ "_id" : "S", "averagePrice" : NumberDecimal("80") } { "_id" : "L", "averagePrice" : NumberDecimal("80") } { "_id" : "M", "averagePrice" : NumberDecimal("120") } { "_id" : null, "averagePrice" : NumberDecimal("45.25") } - 第 3 ステージ:
$sort
ステージでは、ドキュメントをaveragePrice
の降順でソートします この操作では、次の結果を返します。{ "_id" : "M", "averagePrice" : NumberDecimal("120") } { "_id" : "L", "averagePrice" : NumberDecimal("80") } { "_id" : "S", "averagePrice" : NumberDecimal("80") } { "_id" : null, "averagePrice" : NumberDecimal("45.25") }
埋め込まれた配列の展開
mongosh
では、次のドキュメントを含むsales
という名前のサンプル コレクションが作成されます。
db.sales.insertMany([ { _id: "1", "items" : [ { "name" : "pens", "tags" : [ "writing", "office", "school", "stationary" ], "price" : NumberDecimal("12.00"), "quantity" : NumberInt("5") }, { "name" : "envelopes", "tags" : [ "stationary", "office" ], "price" : NumberDecimal("19.95"), "quantity" : NumberInt("8") } ] }, { _id: "2", "items" : [ { "name" : "laptop", "tags" : [ "office", "electronics" ], "price" : NumberDecimal("800.00"), "quantity" : NumberInt("1") }, { "name" : "notepad", "tags" : [ "stationary", "school" ], "price" : NumberDecimal("14.95"), "quantity" : NumberInt("3") } ] } ])
次の操作では、販売された商品をタグごとにグループ化し、タグごとに合計販売額を計算します。
db.sales.aggregate([ // First Stage { $unwind: "$items" }, // Second Stage { $unwind: "$items.tags" }, // Third Stage { $group: { _id: "$items.tags", totalSalesAmount: { $sum: { $multiply: [ "$items.price", "$items.quantity" ] } } } } ])
- 第 1 ステージ
1 つ目の
$unwind
ステージでは、新しいドキュメントが次のようにitems
配列の要素ごとに出力されます。{ "_id" : "1", "items" : { "name" : "pens", "tags" : [ "writing", "office", "school", "stationary" ], "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "envelopes", "tags" : [ "stationary", "office" ], "price" : NumberDecimal("19.95"), "quantity" : 8 } } { "_id" : "2", "items" : { "name" : "laptop", "tags" : [ "office", "electronics" ], "price" : NumberDecimal("800.00"), "quantity" : 1 } } { "_id" : "2", "items" : { "name" : "notepad", "tags" : [ "stationary", "school" ], "price" : NumberDecimal("14.95"), "quantity" : 3 } } - 第 2 ステージ
2 つ目の
$unwind
ステージでは、新しいドキュメントが次のようにitems.tags
配列の要素ごとに出力されます。{ "_id" : "1", "items" : { "name" : "pens", "tags" : "writing", "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "pens", "tags" : "office", "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "pens", "tags" : "school", "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "pens", "tags" : "stationary", "price" : NumberDecimal("12.00"), "quantity" : 5 } } { "_id" : "1", "items" : { "name" : "envelopes", "tags" : "stationary", "price" : NumberDecimal("19.95"), "quantity" : 8 } } { "_id" : "1", "items" : { "name" : "envelopes", "tags" : "office", "price" : NumberDecimal("19.95"), "quantity" : 8 } } { "_id" : "2", "items" : { "name" : "laptop", "tags" : "office", "price" : NumberDecimal("800.00"), "quantity" : 1 } } { "_id" : "2", "items" : { "name" : "laptop", "tags" : "electronics", "price" : NumberDecimal("800.00"), "quantity" : 1 } } { "_id" : "2", "items" : { "name" : "notepad", "tags" : "stationary", "price" : NumberDecimal("14.95"), "quantity" : 3 } } { "_id" : "2", "items" : { "name" : "notepad", "tags" : "school", "price" : NumberDecimal("14.95"), "quantity" : 3 } } - 第 3 ステージ
$group
ステージでは、ドキュメントをタグ別にグループ化し、各タグが付いたアイテムの合計売上額を計算します。{ "_id" : "writing", "totalSalesAmount" : NumberDecimal("60.00") } { "_id" : "stationary", "totalSalesAmount" : NumberDecimal("264.45") } { "_id" : "electronics", "totalSalesAmount" : NumberDecimal("800.00") } { "_id" : "school", "totalSalesAmount" : NumberDecimal("104.85") } { "_id" : "office", "totalSalesAmount" : NumberDecimal("1019.60") }