2013-12-08 10 views
13

MongoDB集計フレームワークを使用してメジアンを計算する方法があるかどうか、MongoDB集計を初めて使用しているかどうか疑問に思っていますか?MongoDB集計フレームワークの中央値を計算する

乾杯、

ルイス

+0

'$ median'のようなものはありません私の知る限りので、おそらくあなたは、このためにマップ-減らす使用する必要があります。 – hgoebl

+0

'$ median'アキュムレータのサポートを追加するためのオープン機能リクエストがあります。 MongoDBのissueトラッカーで[SERVER-4929](https://jira.mongodb.org/browse/SERVER-4929)をupvote/watchしてください。 – Stennie

答えて

2

は、集約フレームワークは、アウト・オブ・ボックスの中央値をサポートしていません。だから、自分で何かを書く必要があります。

アプリケーションレベルでこれを行うことをお勧めします。通常のfind()ですべてのドキュメントを取得し、結果セットをソートします(カーソルの.sort()関数を使用するか、アプリケーションでソートしますか?決定)。次に要素size/2を取得します。

実際にデータベースレベルでやりたい場合は、map-reduceを使用して実行できます。 map-functionはkeyと、単一の値を持つ配列、つまり中央値を取得する値を出力します。 reduce関数は受信した結果の配列を連結するだけで、各キーはすべての値を持つ配列になります。 finalize-functionは配列のソートによって再びその配列の中央値を計算し、次に要素番号size/2を得る。

+0

それは素晴らしいですね、私はそれを行こうと思います。 – user3080286

20

データセット全体をソートするか、データセットのサイズに比例する深​​さの再帰を使用するため、中央値は一般的な場合に計算するのがやや難解です。それはおそらく、多くのデータベースにメディアン演算子がない(MySQLには1つもない)理由があります。

中央値を計算するための最も簡単な方法は、これらの二つの文(私たちは中央値を計算したい属性がaと呼ばれ、私たちは、コレクション内のすべてのドキュメントを、それをオーバーしたい、collと仮定)で次のようになります。

count = db.coll.count(); 
db.coll.find().sort({"a":1}).skip(count/2 - 1).limit(1); 

これは人物suggest for MySQLと同等です。

+5

私は感謝を言うだけのコメントを置くことは許されていないことを知っている..しかし、これは美しいです:) – fguillen

1

集約フレームワークを使用してワンショットで行うことは可能です。

並べ替え=>並べ替えられた値=>取得する配列のサイズ=> 2でサイズを分ける=>除算のInt値を取得する(中央値の左側)=> 1を左側に追加する(右側)= >左側と右側に配列要素を取得=>二つの要素

の平均は、これは春のjava mongoTemplateのサンプルです:

モデルは作者(「所有者のログインで本のリストです。 ")、目的はユーザーの本の中央値を得ることです:

 GroupOperation countByBookOwner = group("owner").count().as("nbBooks"); 

    SortOperation sortByCount = sort(Direction.ASC, "nbBooks"); 

    GroupOperation putInArray = group().push("nbBooks").as("nbBooksArray"); 

    ProjectionOperation getSizeOfArray = project("nbBooksArray").and("nbBooksArray").size().as("size"); 

    ProjectionOperation divideSizeByTwo = project("nbBooksArray").and("size").divide(2).as("middleFloat"); 

    ProjectionOperation getIntValueOfDivisionForBornLeft = project("middleFloat", "nbBooksArray").and("middleFloat") 
      .project("trunc").as("beginMiddle"); 

    ProjectionOperation add1ToBornLeftToGetBornRight = project("beginMiddle", "middleFloat", "nbBooksArray") 
      .and("beginMiddle").project("add", 1).as("endMiddle"); 

    ProjectionOperation arrayElementAt = project("beginMiddle", "endMiddle", "middleFloat", "nbBooksArray") 
      .and("nbBooksArray").project("arrayElemAt", "$beginMiddle").as("beginValue").and("nbBooksArray") 
      .project("arrayElemAt", "$endMiddle").as("endValue"); 

    ProjectionOperation averageForMedian = project("beginMiddle", "endMiddle", "middleFloat", "nbBooksArray", 
      "beginValue", "endValue").and("beginValue").project("avg", "$endValue").as("median"); 

    Aggregation aggregation = newAggregation(countByBookOwner, sortByCount, putInArray, getSizeOfArray, 
      divideSizeByTwo, getIntValueOfDivisionForBornLeft, add1ToBornLeftToGetBornRight, arrayElementAt, 
      averageForMedian); 

    long time = System.currentTimeMillis(); 
    AggregationResults<MedianContainer> groupResults = mongoTemplate.aggregate(aggregation, "book", 
      MedianContainer.class); 

そして、ここで結果の集約:

{ 
"aggregate": "book" , 
"pipeline": [ 
    { 
     "$group": { 
      "_id": "$owner" , 
      "nbBooks": { 
       "$sum": 1 
      } 
     } 
    } , { 
     "$sort": { 
      "nbBooks": 1 
     } 
    } , { 
     "$group": { 
      "_id": null , 
      "nbBooksArray": { 
       "$push": "$nbBooks" 
      } 
     } 
    } , { 
     "$project": { 
      "nbBooksArray": 1 , 
      "size": { 
       "$size": ["$nbBooksArray"] 
      } 
     } 
    } , { 
     "$project": { 
      "nbBooksArray": 1 , 
      "middleFloat": { 
       "$divide": ["$size" , 2] 
      } 
     } 
    } , { 
     "$project": { 
      "middleFloat": 1 , 
      "nbBooksArray": 1 , 
      "beginMiddle": { 
       "$trunc": ["$middleFloat"] 
      } 
     } 
    } , { 
     "$project": { 
      "beginMiddle": 1 , 
      "middleFloat": 1 , 
      "nbBooksArray": 1 , 
      "endMiddle": { 
       "$add": ["$beginMiddle" , 1] 
      } 
     } 
    } , { 
     "$project": { 
      "beginMiddle": 1 , 
      "endMiddle": 1 , 
      "middleFloat": 1 , 
      "nbBooksArray": 1 , 
      "beginValue": { 
       "$arrayElemAt": ["$nbBooksArray" , "$beginMiddle"] 
      } , 
      "endValue": { 
       "$arrayElemAt": ["$nbBooksArray" , "$endMiddle"] 
      } 
     } 
    } , { 
     "$project": { 
      "beginMiddle": 1 , 
      "endMiddle": 1 , 
      "middleFloat": 1 , 
      "nbBooksArray": 1 , 
      "beginValue": 1 , 
      "endValue": 1 , 
      "median": { 
       "$avg": ["$beginValue" , "$endValue"] 
      } 
     } 
    } 
] 

}