2016-04-07 14 views
2

文書からフィールド(レベル)の最大値を持つ配列要素を取得するには、助けが必要です。次に、配列要素フィールド "bssid"でグループ化された総発生数を数えます。配列の要素を集計の最大値でグループ化する方法

は、以下のデータが

/* 1 */ 
{ 
    "_id" : "18:59:36:0c:94:a3", 
    "timestamp" : "1460012567", 
    "apdata" : [{ 
     "bssid" : "f4:b7:e2:56:e4:20", 
     "ssid" : "Test Network2", 
     "level" : -55 
    }, { 
     "bssid" : "b8:a3:86:67:03:56", 
     "ssid" : "Test Network1", 
     "level" : -76 
    }] 
} 
/* 2 */ 
{ 
    "_id" : "d0:b3:3f:b9:42:38", 
    "timestamp" : "1460013345", 
    "apdata" : [{ 
     "bssid" : "f4:b7:e2:56:e4:20", 
     "ssid" : "Test Network2", 
     "level" : -65 
    }, { 
     "bssid" : "b8:a3:86:67:03:56", 
     "ssid" : "Test Network1", 
     "level" : -46 
    }] 
} 
/* 3 */ 
{ 
    "_id" : "d0:b3:3f:b9:42:41", 
    "timestamp" : "1460013145", 
    "apdata" : [{ 
     "bssid" : "f4:b7:e2:56:e4:20", 
     "ssid" : "Test Network2", 
     "level" : -65 
    }, { 
     "bssid" : "b8:a3:86:67:03:56", 
     "ssid" : "Test Network1", 
     "level" : -46 
    }] 
} 

必要な出力が各BSSIDは、コレクション全体にわたる各文書の配列内の最大値を持っていた時間のカウントがある

{ 
    "bssid" : "f4:b7:e2:56:e4:20", 
    "ssid" : "Test Network2", 
    "count" : 1 
}, { 
    "bssid" : "b8:a3:86:67:03:56", 
    "ssid" : "Test Network1", 
    "count" : 2 
} 

で考えてみましょう。

+0

通常、_idは一意の識別子です。どのようにグループ分けできますか? –

+0

@JohnGreenall質問の論理に従えば、すべての配列に "2"しかないappdata.bssidの値をグループ化するように求めています。質問のポイントは、質問とデータに関連する最善のフレーズとして、*「特定のネットワークがすべてのドキュメントから最大値を持つレベルを数える」*です。そして解決に向けたいくつかの注目すべきアプローチもある問題です。 –

答えて

1

あなたはMongoDBの3.2が利用できる持っているなら、あなたはこのような何か行うことができます。

db.sample.aggregate([ 
    { "$unwind": "$apdata" }, 
    { "$group": { 
    "_id": "$_id", 
    "apdata": { "$push": "$apdata" }, 
    "max": { "$max": "$apdata.level" } 
    }}, 
    { "$unwind": "$apdata" }, 
    { "$redact": { 
    "$cond": { 
     "if": { "$eq": [ "$apdata.level", "$max" ] }, 
     "then": "$$KEEP", 
     "else": "$$PRUNE" 
    } 
    }}, 
    { "$group": { 
    "_id": "$apdata.bssid", 
    "ssid": { "$first": "$apdata.ssid" }, 
    "count": { "$sum": 1 } 
    }} 
]) 

とのMongoDB 2.4のために:あなたがこれを行う必要があり、少なくともMongoDBの2.6については

db.sample.aggregate([ 
    { "$project": { 
    "apdata": { 
     "$arrayElemAt": [ 
     { "$filter": { 
      "input": "$apdata", 
      "as": "el", 
      "cond": { 
      "$eq": [ 
       "$$el.level", 
       { "$max": { 
       "$map": { 
        "input": "$apdata", 
        "as": "data", 
        "in": "$$data.level" 
       } 
       }} 
      ] 
      } 
     }}, 
     0 
     ] 
    } 
    }}, 
    { "$group": { 
    "_id": "$apdata.bssid", 
    "ssid": { "$first": "$apdata.ssid" }, 
    "count": { "$sum": 1 } 
    }} 
]) 

を2.2このように:

db.sample.aggregate([ 
    { "$unwind": "$apdata" }, 
    { "$group": { 
    "_id": "$_id", 
    "apdata": { "$push": "$apdata" }, 
    "max": { "$max": "$apdata.level" } 
    }}, 
    { "$unwind": "$apdata" }, 
    { "$project": { 
    "apdata": 1, 
    "isMax": { "$eq": [ "$apdata.level", "$max" ] } 
    }}, 
    { "$match": { "isMax": true } }, 
    { "$group": { 
    "_id": "$apdata.bssid", 
    "ssid": { "$first": "$apdata.ssid" }, 
    "count": { "$sum": 1 } 
    }} 
]) 

すべての場合$maxを使用して、各ドキュメントの配列の「最大」値を「最初に」取得してから、$groupで使用する前に配列コンテンツを「フィルタリング」することができます。これに対するアプローチはバージョンによって異なるだけです

MongoDB 3.2$maxは値の「配列」で直接動作することができます。したがって、$mapは、"level"の値を取得し、その「最大」が実際に何であるかを調べるために使用されます。

は次いで​​だけでは「最大」値に一致する配列要素を返すために使用することができ、最終的に$arrayElemAtはプレーンとして要素「のみ」(「ゼロ」のインデックスのうち二つの可能と)ことを返すために使用され資料。あなたは基本的に_id$first"ssid"値を取得するために、両方のためにその全体の声明を繰り返し、それが証明するために別途$projectに書くことが少し簡単だ場合

全体のプロセスは、「のみ」$groupで行うことができます。

MongoDB 2.6:これは、特に操作性の高い演算子がなく、特に$maxがアレイ上で「直接」動作する能力が欠けています。注目すべきことは、最初に$unwindの配列が必要であり、実際にはその最大値を得るために、元の文書の上に実際に$groupだけです。

このプロセスでは、後で配列から要素をグループ化してから$redactを使用してコンテンツをフィルタ処理するため、実際には$unwindが必要になります。これは、の「論理的な」形式で、"level"と、前の段階から計算された「最大」を直接比較することができます。したがって、「最大」でない要素は削除されます。

MongoDB 2。4$redactの代わりに実際に$project物理を必要とする以外は、$matchでフィルタリングに使用するフィールドをドキュメントに入れることを除いて、基本的に同じロジックです。


すべてのバージョンはあなたがグループ化キーの発生をカウントする"ssid"のためにそのグループの境界とシンプルな$sumにグループ化キーと$first結果を"apdata.bssid"へのパスを指定する同じ最終$groupを、持っています結果。

すべては、次のと同じように返します。

{ "_id" : "f4:b7:e2:56:e4:20", "ssid" : "Test Network2", "count" : 1 } 
{ "_id" : "b8:a3:86:67:03:56", "ssid" : "Test Network1", "count" : 2 } 

次のように実際のMongoDB 3.2のための最も「効率的な」形式は次のようになります。

db.sample.aggregate([ 
    { "$group": { 
    "_id": { 
     "$arrayElemAt": [ 
     { "$map": { 
      "input": { 
      "$filter": { 
       "input": "$apdata", 
       "as": "el", 
       "cond": { 
       "$eq": [ 
        "$$el.level", 
        { "$max": { 
        "$map": { 
         "input": "$apdata", 
         "as": "data", 
         "in": "$$data.level" 
        } 
        }} 
       ] 
       } 
      }    
      }, 
      "as": "apdata", 
      "in": { 
      "bssid": "$$apdata.bssid", 
      "ssid": "$$apdata.ssid" 
      } 
     }}, 
     0 
     ] 
    }, 
    "count": { "$sum": 1 } 
    }} 
]) 

をわずかに異なる形で起因します化合物_idであるが、それは単一の$groupステージのみであり、「最大」値のアレイ要素データを見つけるためのプロセス全体を繰り返すことはない:

{ 
    "_id" : { 
     "bssid" : "b8:a3:86:67:03:56", 
     "ssid" : "Test Network1" 
    }, 
    "count" : 2 
    } 
    { 
    "_id" : { 
     "bssid" : "f4:b7:e2:56:e4:20", 
     "ssid" : "Test Network2" 
    }, 
    "count" : 1 
    } 
+0

Neil Lunnさん、ありがとうございました。詳細な解説をいただきありがとうございます。Mongodb初心者には大いに役立ちます – rkd

関連する問題