定義
互換性
次の環境でホストされる配置には $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: Decimal128("80"), sizes: [ "S", "M", "L"] }, { _id: 2, item: "EFG", price: Decimal128("120"), sizes: [ ] }, { _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M" }, { _id: 4, item: "LMN" , price: Decimal128("10") }, { _id: 5, item: "XYZ", price: Decimal128("5.75"), sizes: null } ])
preserveNullAndEmptyArrays
次の$unwind操作ではpreserveNullAndEmptyArraysオプションを使用して、 sizesフィールドが null、欠落、または空の配列であるドキュメントを含めます。
db.inventory2.aggregate( [ { $unwind: { path: "$sizes", preserveNullAndEmptyArrays: true } } ] )
出力には、sizes フィールドが null、欠落、または空の配列であるドキュメントが含まれます。
{ _id: 1, item: "ABC", price: Decimal128("80"), sizes: "S" } { _id: 1, item: "ABC", price: Decimal128("80"), sizes: "M" } { _id: 1, item: "ABC", price: Decimal128("80"), sizes: "L" } { _id: 2, item: "EFG", price: Decimal128("120") } { _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M" } { _id: 4, item: "LMN", price: Decimal128("10") } { _id: 5, item: "XYZ", price: Decimal128("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: Decimal128("80"), sizes: "S", arrayIndex: Long(0) } { _id: 1, item: "ABC", price: Decimal128("80"), sizes: "M", arrayIndex: Long(1) } { _id: 1, item: "ABC", price: Decimal128("80"), sizes: "L", arrayIndex: Long(2) } { _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M", arrayIndex: null }
展開された値によるグループ化
mongoshでは、次のドキュメントを含むinventory2という名前のサンプル コレクションが作成されます。
db.inventory2.insertMany([ { _id: 1, item: "ABC", price: Decimal128("80"), sizes: [ "S", "M", "L"] }, { _id: 2, item: "EFG", price: Decimal128("120"), sizes: [ ] }, { _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M" }, { _id: 4, item: "LMN" , price: Decimal128("10") }, { _id: 5, item: "XYZ", price: Decimal128("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: Decimal128("80"), sizes: "S" } { _id: 1, item: "ABC", price: Decimal128("80"), sizes: "M" } { _id: 1, item: "ABC", price: Decimal128("80"), sizes: "L" } { _id: 2, item: "EFG", price: Decimal128("120") } { _id: 3, item: "IJK", price: Decimal128("160"), sizes: "M" } { _id: 4, item: "LMN", price: Decimal128("10") } { _id: 5, item: "XYZ", price: Decimal128("5.75"), sizes: null } - 第 2 ステージ:
$groupステージでは、ドキュメントをsizes別にグループ化し、各サイズの平均価格を計算します。このステージでは、次のドキュメントを次のステージに渡します。{ _id: "S", averagePrice: Decimal128("80") } { _id: "L", averagePrice: Decimal128("80") } { _id: "M", averagePrice: Decimal128("120") } { _id: null, averagePrice: Decimal128("45.25") } - 第 3 ステージ:
$sortステージでは、ドキュメントをaveragePriceの降順でソートします この操作では、次の結果を返します。{ _id : "M", averagePrice: Decimal128("120") } { _id : "L", averagePrice: Decimal128("80") } { _id : "S", averagePrice: Decimal128("80") } { _id : null, averagePrice: Decimal128("45.25") }
埋め込まれた配列の展開
mongoshでは、次のドキュメントを含むsalesという名前のサンプル コレクションが作成されます。
db.sales.insertMany( [ { _id: "1", items: [ { name: "pens", tags: [ "writing", "office", "school", "stationary" ], price: Decimal128("12.00"), quantity: Int32("5") }, { name: "envelopes", tags: [ "stationary", "office" ], price: Decimal128("19.95"), quantity: Int32("8") } ] }, { _id: "2", items: [ { name: "laptop", tags: [ "office", "electronics" ], price: Decimal128("800.00"), quantity: Int32("1") }, { name: "notepad", tags: [ "stationary", "school" ], price: Decimal128("14.95"), quantity: Int32("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: Decimal128("12.00"), quantity: 5 } } { _id: "1", items: { name: "envelopes", tags: [ "stationary", "office" ], price: Decimal128("19.95"), quantity: 8 } } { _id: "2", items: { name: "laptop", tags: [ "office", "electronics" ], price: Decimal128("800.00"), quantity": 1 } } { _id: "2", items: { name: "notepad", tags: [ "stationary", "school" ], price: Decimal128("14.95"), quantity: 3 } } - 第 2 ステージ
2 つ目の
$unwindステージでは、新しいドキュメントが次のようにitems.tags配列の要素ごとに出力されます。{ _id: "1", items: { name: "pens", tags: "writing", price: Decimal128("12.00"), quantity: 5 } } { _id: "1", items: { name: "pens", tags: "office", price: Decimal128("12.00"), quantity: 5 } } { _id: "1", items: { name: "pens", tags: "school", price: Decimal128("12.00"), quantity: 5 } } { _id: "1", items: { name: "pens", tags: "stationary", price: Decimal128("12.00"), quantity: 5 } } { _id: "1", items: { name: "envelopes", tags: "stationary", price: Decimal128("19.95"), quantity: 8 } } { _id: "1", items: { name: "envelopes", tags: "office", "price" : Decimal128("19.95"), quantity: 8 } } { _id: "2", items: { name: "laptop", tags: "office", price: Decimal128("800.00"), quantity: 1 } } { _id: "2", items: { name: "laptop", tags: "electronics", price: Decimal128("800.00"), quantity: 1 } } { _id: "2", items: { name: "notepad", tags: "stationary", price: Decimal128("14.95"), quantity: 3 } } { _id: "2", items: { name: "notepad", "ags: "school", price: Decimal128("14.95"), quantity: 3 } } - 第 3 ステージ
$groupステージでは、ドキュメントをタグ別にグループ化し、各タグが付いたアイテムの合計売上額を計算します。{ _id: "writing", totalSalesAmount: Decimal128("60.00") } { _id: "stationary", totalSalesAmount: Decimal128("264.45") } { _id: "electronics", totalSalesAmount: Decimal128("800.00") } { _id: "school", totalSalesAmount: Decimal128("104.85") } { _id: "office", totalSalesAmount: Decimal128("1019.60") }
このページのC#の例では、Atlasサンプルデータセット の sample_mflixデータベースを使用します。MongoDB Atlasクラスターを無料で作成して、サンプルデータセットをロードする方法については、 MongoDB .NET/ C#ドライバーのドキュメントの「 開始 」を参照してください。
次の Movie クラスは、sample_mflix.movies コレクション内のドキュメントをモデル化します。
public class Movie { public ObjectId Id { get; set; } public int Runtime { get; set; } public string Title { get; set; } public string Rated { get; set; } public List<string> Genres { get; set; } public string Plot { get; set; } public ImdbData Imdb { get; set; } public int Year { get; set; } public int Index { get; set; } public string[] Comments { get; set; } [] public DateTime LastUpdated { get; set; } }
注意
パスカルケースの ConventionPack
このページのC# クラスはプロパティ名にパスカルケースを使用していますが、MongoDB コレクションのフィールド名はキャメルケースを使用しています。この違いを考慮するために、アプリケーションが起動する際に次のコードを使用してConventionPackを登録してください。
var camelCaseConvention = new ConventionPack { new CamelCaseElementNameConvention() }; ConventionRegistry.Register("CamelCase", camelCaseConvention, type => true);
MongoDB .NET/ C#ドライバーを使用して$unwind ステージを集計パイプラインに追加するには、 PipelineDefinitionオブジェクトで Unwind() メソッドを呼び出します。
次の例では、各入力 Movieドキュメントの Genresフィールドを反復処理するパイプラインステージを作成しています。ステージは、Genresフィールドの各値に対して新しい Movieドキュメントを作成し、その Genresフィールドに入力ドキュメントの Genres 値を入力します。
var pipeline = new EmptyPipelineDefinition<Movie>() .Unwind(m => m.Genres);
AggregateUnwindOptionsオブジェクトを使用して、Unwind() メソッドの動作をカスタマイズできます。次の例では、前の例と同じ操作を実行しますが、次のオプションも含まれています。
PreserveNullAndEmptyArraysを指定すると、Genresフィールドに空の配列が含まれるドキュメントが出力に含まれるようになります。IncludeArrayIndexオプションは、各出力ドキュメントにIndexという名前の新しいフィールドを追加します。このフィールドの値は、入力ドキュメントのGenres配列内のGenresフィールドの値の配列インデックスです。
var pipeline = new EmptyPipelineDefinition<Movie>() .Unwind(m => m.Genres, new AggregateUnwindOptions<Movie>() { PreserveNullAndEmptyArrays = true, IncludeArrayIndex = new ExpressionFieldDefinition<Movie, int>( m => m.Index) });
このページのNode.js の例では、Atlasサンプルデータセット の sample_mflixデータベースを使用します。無料のMongoDB Atlas cluster を作成し、サンプルデータセットをロードする方法については、 MongoDB Node.jsドライバーのドキュメントの開始を参照してください。
MongoDB Node.jsドライバーを使用して $unwind ステージを集計パイプラインに追加するには、パイプラインオブジェクトで $unwind 演算子を使用します。
次の例では、各入力 movieドキュメントの genresフィールドを反復処理するパイプラインステージを作成しています。ステージは、genresフィールドの各値に対して新しい movieドキュメントを作成し、その genresフィールドに入力ドキュメントの genres 値を入力します。次に、この例では集計パイプラインを実行します。
const pipeline = [{ $unwind: "$genres" }]; const cursor = collection.aggregate(pipeline); return cursor;
$unwind メソッドの動作をカスタマイズできます。次の例では、前の例と同じ操作を実行しますが、次のオプションも含まれています。
preserveNullAndEmptyArraysを指定すると、genresフィールドに空の配列が含まれるドキュメントが出力に含まれるようになります。includeArrayIndex各出力ドキュメントにindexという名前の新しいフィールドを追加します。フィールドには、入力ドキュメントのgenresフィールド内のgenres値の配列インデックスが含まれます。
const pipeline = [ { $unwind: { path: "$genres", preserveNullAndEmptyArrays: true, includeArrayIndex: "index" } } ]; const cursor = collection.aggregate(pipeline); return cursor;
詳細
関連するメソッドの詳細については、$group、$sum、および$multiplyのガイドを参照してください。
完全な例で $unwind を使用する方法については、「配列とグループ データの展開」チュートリアルを参照してください。