から クリスは、あなたが持っている基本的な問題は、あなたがおそらく本当に一貫性のある属性のパスに値を使用して代わりにする必要があります「という名前のキーを」使用していることです。つまり、"browsers"
のようなキーの代わりに、これはおそらく単に各エントリの"type": "browser"
となるはずです。
この理由は、データを集約する一般的なアプローチで明らかになります。それはまた、一般的にクエリに役立ちます。しかし、基本的には、最初のデータ形式をこの種の構造に強制して、最初に集約します。
最新のリリースでは
(MongoDBの3.4.4およびそれ以上)、我々は$objectToArray
を経由して、あなたの名前のキーと連携し、次のように操作することができます。
db.channel_report.aggregate([
{ "$project": {
"timestamp": 1,
"sources": {
"$reduce": {
"input": {
"$map": {
"input": { "$objectToArray": "$sources_info" },
"as": "s",
"in": {
"$map": {
"input": "$$s.v",
"as": "v",
"in": {
"type": "$$s.k",
"name": "$$v.name",
"count": "$$v.count"
}
}
}
}
},
"initialValue": [],
"in": { "$concatArrays": ["$$value", "$$this"] }
}
}
}},
{ "$unwind": "$sources" },
{ "$group": {
"_id": {
"year": { "$year": "$timestamp" },
"type": "$sources.type",
"name": "$sources.name"
},
"count": { "$sum": "$sources.count" }
}},
{ "$group": {
"_id": { "year": "$_id.year", "type": "$_id.type" },
"v": { "$push": { "name": "$_id.name", "count": "$count" } }
}},
{ "$group": {
"_id": "$_id.year",
"sources_info": {
"$push": { "k": "$_id.type", "v": "$v" }
}
}},
{ "$addFields": {
"sources_info": { "$arrayToObject": "$sources_info" }
}}
])
MongoDBの3にノッチをそれをバック取ります。4(今ではほとんどのホスティングサービスではデフォルトであるべきである)あなたが交互に手動で各キーの名前を宣言することができます:
db.channel_report.aggregate([
{ "$project": {
"timestamp": 1,
"sources": {
"$concatArrays": [
{ "$map": {
"input": "$sources_info.browsers",
"in": {
"type": "browsers",
"name": "$$this.name",
"count": "$$this.count"
}
}},
{ "$map": {
"input": "$sources_info.operating_systems",
"in": {
"type": "operating_systems",
"name": "$$this.name",
"count": "$$this.count"
}
}},
{ "$map": {
"input": "$sources_info.continent_ids",
"in": {
"type": "continent_ids",
"name": "$$this.name",
"count": "$$this.count"
}
}},
{ "$map": {
"input": "$sources_info.country_ids",
"in": {
"type": "country_ids",
"name": "$$this.name",
"count": "$$this.count"
}
}},
{ "$map": {
"input": "$sources_info.city_ids",
"in": {
"type": "city_ids",
"name": "$$this.name",
"count": "$$this.count"
}
}}
]
}
}},
{ "$unwind": "$sources" },
{ "$group": {
"_id": {
"year": { "$year": "$timestamp" },
"type": "$sources.type",
"name": "$sources.name"
},
"count": { "$sum": "$sources.count" }
}},
{ "$group": {
"_id": { "year": "$_id.year", "type": "$_id.type" },
"v": { "$push": { "name": "$_id.name", "count": "$count" } }
}},
{ "$group": {
"_id": "$_id.year",
"sources": {
"$push": { "k": "$_id.type", "v": "$v" }
}
}},
{ "$project": {
"sources_info": {
"browsers": {
"$arrayElemAt": [
"$sources.v",
{ "$indexOfArray": [ "$sources.k", "browsers" ] }
]
},
"operating_systems": {
"$arrayElemAt": [
"$sources.v",
{ "$indexOfArray": [ "$sources.k", "operating_systems" ] }
]
},
"continent_ids": {
"$arrayElemAt": [
"$sources.v",
{ "$indexOfArray": [ "$sources.k", "continent_ids" ] }
]
},
"country_ids": {
"$arrayElemAt": [
"$sources.v",
{ "$indexOfArray": [ "$sources.k", "country_ids" ] }
]
},
"city_ids": {
"$arrayElemAt": [
"$sources.v",
{ "$indexOfArray": [ "$sources.k", "city_ids" ] }
]
}
}
}}
])
我々は$indexOfArray
の代わりに$map
と$filter
を使用してのMongoDB 3.2にバックアップしても風をすることができますが、説明するのが一般的なアプローチです。
を連結アレイ
起こる必要がある主なものは、指定されたキーを使用して、多くの異なるアレイからデータを取得し、各キーの名前を表す"type"
プロパティを持つ「単一のアレイ」を作ることです。あなたがしたいデータの一部
/* 1 */
{
"_id" : ObjectId("59b9d08e402025326e1a0f30"),
"timestamp" : ISODate("2017-09-14T00:42:54.510Z"),
"sources" : [
{
"type" : "browsers",
"name" : "Chrome",
"count" : NumberLong(2)
},
{
"type" : "operating_systems",
"name" : "Mac OS X",
"count" : NumberLong(2)
},
{
"type" : "continent_ids",
"name" : "EU",
"count" : NumberLong(1)
},
{
"type" : "country_ids",
"name" : "DE",
"count" : NumberLong(1)
},
{
"type" : "city_ids",
"name" : "Solingen",
"count" : NumberLong(1)
}
]
}
アンワインドとグループ
:これは、データが最初の場所に格納する必要があるか間違いなくある、とアプローチのいずれかの最初の集約の段階は次のように出てきます実際に累積するには、アレイ内の「"type"
」および「"name"
」のプロパティが含まれます。 「配列内」の文書間で累積する必要がある場合は、グルーピングキーの一部としてこれらの値にアクセスできるように、使用するプロセスは$unwind
です。これが何を意味するのか
を組み合わせアレイ上$unwind
を使用した後、あなたはその後、$sum
"count"
値に順番にこれらのキーと減少"timestamp"
細部の両方に$group
にしたいということです。
「サブレベル」の詳細(ブラウザ内の各ブラウザの名前)があるので、追加の$group
パイプラインステージを使用し、グループ化キーの細分性を徐々に減らし、$push
を使用して配列に詳細を累積します。それは、もともとあったように同じ形式で表現されていないが、これは実際に、データの最終状態である
/* 1 */
{
"_id" : 2017,
"sources_info" : [
{
"k" : "continent_ids",
"v" : [
{
"name" : "EU",
"count" : NumberLong(1)
}
]
},
{
"k" : "city_ids",
"v" : [
{
"name" : "Solingen",
"count" : NumberLong(1)
}
]
},
{
"k" : "country_ids",
"v" : [
{
"name" : "DE",
"count" : NumberLong(1)
}
]
},
{
"k" : "browsers",
"v" : [
{
"name" : "Chrome",
"count" : NumberLong(2)
}
]
},
{
"k" : "operating_systems",
"v" : [
{
"name" : "Mac OS X",
"count" : NumberLong(2)
}
]
}
]
}
:いずれの場合も
、出力の非常に最後の段階を省略し、蓄積構造は、として出てきます見つかりました。これ以上の処理は単に名前付きキーとして出力するだけの美容であるため、この時点では間違いなく完全です。名前のキーを様々なアプローチがいずれか、または名前付きキーを持つオブジェクトに戻って配列の内容を変換するために$arrayToObject
を使用して一致するキー名で、配列のエントリを探している示されているように
へ
出力。当然の凝集パイプラインアプローチのいずれかに適用
db.channel_report.aggregate([
{ "$project": {
"timestamp": 1,
"sources": {
"$reduce": {
"input": {
"$map": {
"input": { "$objectToArray": "$sources_info" },
"as": "s",
"in": {
"$map": {
"input": "$$s.v",
"as": "v",
"in": {
"type": "$$s.k",
"name": "$$v.name",
"count": "$$v.count"
}
}
}
}
},
"initialValue": [],
"in": { "$concatArrays": ["$$value", "$$this"] }
}
}
}},
{ "$unwind": "$sources" },
{ "$group": {
"_id": {
"year": { "$year": "$timestamp" },
"type": "$sources.type",
"name": "$sources.name"
},
"count": { "$sum": "$sources.count" }
}},
{ "$group": {
"_id": { "year": "$_id.year", "type": "$_id.type" },
"v": { "$push": { "name": "$_id.name", "count": "$count" } }
}},
{ "$group": {
"_id": "$_id.year",
"sources_info": {
"$push": { "k": "$_id.type", "v": "$v" }
}
}},
/*
{ "$addFields": {
"sources_info": { "$arrayToObject": "$sources_info" }
}}
*/
]).map(d => Object.assign(d,{
"sources_info": d.sources_info.reduce((acc,curr) =>
Object.assign(acc,{ [curr.k]: curr.v }),{})
}))
:
代替的には、シェルにカーソル結果を操作する本.map()
例によって示されるように、単に、コードにその最後の操作を行うことです。
もちろんも$concatArrays
であればすべてのエントリが(それらがあるように見えるように)"name"
と"type"
の一意の識別の組み合わせを有し、かつ、処理することにより、最終的な出力を変更するアプリケーションと手段として$setUnion
で置換することができますカーソルをMongoDB 2.6のように遠くまで適用することもできます。
最終出力
最終出力(実際には当然の集合が、質問サンプルのみ一つの文書は)として示すように、最後のサンプル出力からすべてのサブキーに蓄積し、再構成:
{
"_id" : 2017,
"sources_info" : {
"continent_ids" : [
{
"name" : "EU",
"count" : NumberLong(1)
}
],
"city_ids" : [
{
"name" : "Solingen",
"count" : NumberLong(1)
}
],
"country_ids" : [
{
"name" : "DE",
"count" : NumberLong(1)
}
],
"browsers" : [
{
"name" : "Chrome",
"count" : NumberLong(2)
}
],
"operating_systems" : [
{
"name" : "Mac OS X",
"count" : NumberLong(2)
}
]
}
}
sources_info
の各キーの配列エントリは、同じ"name"
を共有する他のすべてのエントリの累積カウントに縮小されます。
$ unwindを試してグルーピングしましたか? –
試しましたが、実際には5つの異なるグループについて話しています。ブラウザのように、operating_system、conti ...などそれはちょっと間違って、遅いと感じる。あなたの計画をスケッチできるなら、それは非常に素晴らしいでしょう! – Kr0e
より具体的にする必要があります。あなたのサンプル文書には ''ブラウザ '"よりもはるかに多くの部分がありますので、使用している' $ push'は各文書の各プロパティを結果配列にプッシュするだけです。これはまた、合理的なサイズのデータを破ることにもつながります。だから、あなたが実際に期待しているものが、その中の「すべての鍵」であるならば、本当にあなたの質問で言いたいことがあります。これらのプロパティのいずれかがなぜ配列そのものにあるのかについては、本当に不明です。だからあなたの意図した結果を精巧に説明し完全に説明することは害ではないでしょう。 –