使用可能なバージョンに応じてさまざまなアプローチがありますが、基本的には、ドキュメントフィールドを「配列」内の個別のドキュメントに変換し、その配列を $ unwind
$ group
を連続して実行します
出力の合計と配列を累積するためのステージ。
MongoDB3.4.4以降
最新のリリースには、 $ arrayToObject
のような特別な演算子があります。
および $ objectToArray
これにより、ソースドキュメントから最初の「配列」への転送が以前のリリースよりも動的になります。
db.profile.aggregate([
{ "$project": {
"_id": 0,
"data": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$in": [ "$$this.k", ["gender","caste","education"] ] }
}
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": "$data",
"total": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.k",
"v": {
"$push": { "name": "$_id.v", "total": "$total" }
}
}},
{ "$group": {
"_id": null,
"data": { "$push": { "k": "$_id", "v": "$v" } }
}},
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": "$data"
}
}}
])
したがって、 $ objectToArray
を使用します。
最初のドキュメントを"k"
のようにキーと値の配列にします および"v"
結果のオブジェクト配列のキー。 $ filter
を適用します
ここで「キー」で選択します。ここでは、 $ in
を使用しています
必要なキーのリストを使用しますが、これをキーのリストとしてより動的に使用して、短い方を「除外」することができます。論理演算子を使用して条件を評価しているだけです。
ここでの最終段階では、 $ replaceRoot
を使用します。
そして、その間のすべての操作と「グループ化」により、その "k"
が維持されます。 および"v"
フォームでは、 $ arrayToObject
を使用します。
ここでは、「オブジェクトの配列」を結果として出力の最上位ドキュメントの「キー」にプロモートします。
MongoDB 3.6 $ mergeObjects
ここでの追加のしわとして、MongoDB3.6には $ mergeObjects
これは、"アキュムレータとして使用できます。 "
$ group
パイプラインステージも同様に、 $ push
>
そして、最終的な $ replaceRoot
を作成します。
"data"
をシフトするだけです 代わりに、返されたドキュメントの「ルート」へのキー:
db.profile.aggregate([
{ "$project": {
"_id": 0,
"data": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$in": [ "$$this.k", ["gender","caste","education"] ] }
}
}
}},
{ "$unwind": "$data" },
{ "$group": { "_id": "$data", "total": { "$sum": 1 } }},
{ "$group": {
"_id": "$_id.k",
"v": {
"$push": { "name": "$_id.v", "total": "$total" }
}
}},
{ "$group": {
"_id": null,
"data": {
"$mergeObjects": {
"$arrayToObject": [
[{ "k": "$_id", "v": "$v" }]
]
}
}
}},
{ "$replaceRoot": { "newRoot": "$data" } }
])
これは、全体的に示されているものと実際にはそれほど違いはありませんが、 $ mergeObjects
このように使用でき、グループ化キーが別のものであり、オブジェクトのルートスペースへの最終的な「マージ」を望まない場合に役立つ可能性があります。
$ arrayToObject
「値」を「キー」の名前に戻すには引き続き必要ですが、新しい累積ではキーの「マージ」が可能になるため、グループ化後ではなく、累積中に実行します。
MongoDB 3.2
バージョンを元に戻すか、3.4.4リリースよりも小さいMongoDB 3.4.xを使用している場合でも、これの多くを使用できますが、代わりに、より静的な方法でアレイの作成を処理します。集計演算子がないため、出力の最終的な「変換」の処理が異なるため、次のようになります。
db.profile.aggregate([
{ "$project": {
"data": [
{ "k": "gender", "v": "$gender" },
{ "k": "caste", "v": "$caste" },
{ "k": "education", "v": "$education" }
]
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": "$data",
"total": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.k",
"v": {
"$push": { "name": "$_id.v", "total": "$total" }
}
}},
{ "$group": {
"_id": null,
"data": { "$push": { "k": "$_id", "v": "$v" } }
}},
/*
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": "$data"
}
}}
*/
]).map( d =>
d.data.map( e => ({ [e.k]: e.v }) )
.reduce((acc,curr) => Object.assign(acc,curr),{})
)
これはまったく同じことですが、ドキュメントを配列に動的に変換する代わりに、実際には各配列メンバーに同じ "k"
を「明示的に」割り当てます。 および"v"
表記。ここではどの集計演算子もそれに依存していないため、この時点ではこれらのキー名を慣例として保持しているだけです。
また、 $ replaceRoot
を使用する代わりに
、前のパイプラインステージの実装がそこで行っていたのとまったく同じことを行いますが、代わりにクライアントコードで行います。すべてのMongoDBドライバーには、 cursor.map()<が実装されています。 / code>
「カーソル変換」を有効にします。ここでは、シェルで Array.map()
および Array.reduce()
その出力を取得し、配列のコンテンツを、返された最上位のドキュメントのキーになるように再度プロモートします。
MongoDB 2.6
そして、MongoDB 2.6に戻って、その間のバージョンをカバーします。ここで変更されるのは、 $ map
および $literal
配列宣言を使用した入力の場合:
db.profile.aggregate([
{ "$project": {
"data": {
"$map": {
"input": { "$literal": ["gender","caste", "education"] },
"as": "k",
"in": {
"k": "$$k",
"v": {
"$cond": {
"if": { "$eq": [ "$$k", "gender" ] },
"then": "$gender",
"else": {
"$cond": {
"if": { "$eq": [ "$$k", "caste" ] },
"then": "$caste",
"else": "$education"
}
}
}
}
}
}
}
}},
{ "$unwind": "$data" },
{ "$group": {
"_id": "$data",
"total": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.k",
"v": {
"$push": { "name": "$_id.v", "total": "$total" }
}
}},
{ "$group": {
"_id": null,
"data": { "$push": { "k": "$_id", "v": "$v" } }
}},
/*
{ "$replaceRoot": {
"newRoot": {
"$arrayToObject": "$data"
}
}}
*/
])
.map( d =>
d.data.map( e => ({ [e.k]: e.v }) )
.reduce((acc,curr) => Object.assign(acc,curr),{})
)
ここでの基本的な考え方は、提供されたフィールド名の配列を「反復」することであるため、実際の値の割り当ては、 $ cond
ステートメント。 3つの可能な結果の場合、これは、結果ごとに「分岐」するための1つのネストのみを意味します。
3.4以降の最新のMongoDBには、 $ switch
があります。
これにより、この分岐が簡単になりますが、これはロジックが常に可能であり、 $ cond
演算子は、MongoDB2.2で集約フレームワークが導入されてから存在しています。
繰り返しになりますが、カーソルの結果に同じ変換が適用されます。新しいものはなく、ほとんどのプログラミング言語には、最初からではなくても、何年にもわたってこれを実行する機能があります。
もちろん、基本的なプロセスはMongoDB 2.2に戻ることもできますが、配列の作成と $ unwind
別の方法で。ただし、現時点では、2.8未満のMongoDBを実行している人はいないはずであり、3.0からの公式サポートも急速に不足しています。
出力
視覚化のために、ここで示されているすべてのパイプラインの出力は、最後の「変換」が行われる前に次の形式になります。
/* 1 */
{
"_id" : null,
"data" : [
{
"k" : "gender",
"v" : [
{
"name" : "Male",
"total" : 3.0
},
{
"name" : "Female",
"total" : 2.0
}
]
},
{
"k" : "education",
"v" : [
{
"name" : "M.C.A",
"total" : 1.0
},
{
"name" : "B.E",
"total" : 3.0
},
{
"name" : "B.Com",
"total" : 1.0
}
]
},
{
"k" : "caste",
"v" : [
{
"name" : "Lingayath",
"total" : 3.0
},
{
"name" : "Vokkaliga",
"total" : 2.0
}
]
}
]
}
次に、 $ replaceRoot
のいずれかを使用します
または、示されているようにカーソルが変換され、結果は次のようになります。
/* 1 */
{
"gender" : [
{
"name" : "Male",
"total" : 3.0
},
{
"name" : "Female",
"total" : 2.0
}
],
"education" : [
{
"name" : "M.C.A",
"total" : 1.0
},
{
"name" : "B.E",
"total" : 3.0
},
{
"name" : "B.Com",
"total" : 1.0
}
],
"caste" : [
{
"name" : "Lingayath",
"total" : 3.0
},
{
"name" : "Vokkaliga",
"total" : 2.0
}
]
}
したがって、いくつかの新しくて凝った演算子を利用可能な集約パイプラインに入れることができますが、最も一般的なユースケースはこれらの「パイプライン変換の終わり」です。この場合、各ドキュメントで同じ変換を行うこともできます。代わりにカーソルの結果が返されます。