Docs Menu
Docs Home
/
データベース マニュアル
/ / /

$unwind(集計)

$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

任意。要素の配列インデックスを保持する新しいフィールドの名前。名前をドル記号$で始めることはできません。

ブール値

任意。

  • trueの場合、 がpath null、欠落、または空の配列である場合、$unwind はドキュメントを出力します。

  • falseの場合、 がpath null、欠落、または空の配列の場合、$unwind はドキュメントを出力しません。

デフォルト値は false です。

  • オペランドが配列に解決されないが、欠落しているわけでも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は入力された配列です。 $unwindsizesフィールドの各要素のドキュメントを返します。

  • ドキュメント"_id": 3では、 sizesが単一の要素配列に解決されます。

  • ドキュメント "_id": 2, "_id": 4 および "_id": 5 は、sizes フィールドを単一要素の配列に縮小できないため、何も返しません。

注意

{ path: <FIELD> } 構文は任意です。次に示す $unwind 操作は同等です。

db.clothing.aggregate( [ { $unwind: "$sizes" } ] )
db.clothing.aggregate( [ { $unwind: { path: "$sizes" } } ] )

preserveNullAndEmptyArraysincludeArrayIndex の例では、次のコレクションが使用されます。

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 }
])

次の$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 }

次の$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") }

Tip

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; }
[BsonElement("lastupdated")]
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)
});

戻る

$unset

項目一覧