2013-03-19 4 views
14

現在、私たちがSQL Server上で動作しているため、MongoDB集計フレームワークが私たちのニーズにどれくらい適しているか評価しています。私は、特定のクエリを実行するに苦労しています:mongodb集計フレームワークでcase-statementを実行する

は、私が欲しい

{ 
    name: 'A', 
    timespent: 100, 
}, 
{ 
    name: 'B', 
    timespent: 200, 
}, 
{ 
    name: 'C', 
    timespent: 300, 
}, 
{ 
    name: 'D', 
    timespent: 400, 
}, 
{ 
    name: 'E', 
    timespent: 500, 
} 

(SQLテーブルにとMongoDBのコレクションの完全な文書として列としてモデル化)私は以下の擬似レコードを持っていると言いますタイムゾーンフィールドを範囲にグループ化し、発生をカウントして、たとえば次の擬似レコード:

results{ 
    0-250: 2, 
    250-450: 2, 
    450-650: 1 
} 

注これらの範囲(250、450及び650)は、動的であり、おそらくユーザーにより経時的に変更されること。

select range, COUNT(*) as total from (
select case when Timespent <= 250 then '0-250' 
when Timespent <= 450 then '200-450' 
else '450-600' end as range 
from TestTable) as r 
group by r.range 

は再び、このSQLはいずれかの時点で利用可能な特定の範囲に合わせて、我々のアプリによって動的に構築されることに注意してください:SQLでは、私たちはこのような何かで結果を抽出します。

私は、mongodb集約フレームワークで適切な構造を見つけてそのようなクエリを実行するのに苦労しています。 1つの範囲の結果をパイプラインに挿入する(つまり、単一の範囲の結果を得る)ことによってクエリを実行できますが、単一のパイプラインクエリですべての範囲とその数を抽出する方法は不可能です。

+0

このリンクはお役に立ちます http://stackoverflow.com/questions/8945766/mongodb-mysql-sum-case-when-equivalent/21700596#21700596 –

+1

http://www.codefari.com/2015 /08/case-statement-in-mongodb-query.html上記の リンクはあなたに役立ちます – Singh

答えて

28

aggregation frameworkの "case" SQL文に相当するものは、$ cond演算子です(manual参照)。 $ condステートメントは "when-then"と "else"をシミュレートするために入れ子にすることができますが、別のアプローチを選択しました。これは読みやすく(以下を参照してください)、$ concat演算子を使用して範囲文字列はグループ化キーとして使用されます。

だから、特定のコレクションのために:生成するために

{ 
    "result" : [ 
     { 
      "_id" : "range 0-250", 
      "count" : 2 
     }, 
     { 
      "_id" : "range 251-450", 
      "count" : 2 
     }, 
     { 
      "_id" : "range 450-650", 
      "count" : 1 
     } 
    ], 
    "ok" : 1 
} 

db.xx.find() 
{ "_id" : ObjectId("514919fb23700b41723f94dc"), "name" : "A", "timespent" : 100 } 
{ "_id" : ObjectId("514919fb23700b41723f94dd"), "name" : "B", "timespent" : 200 } 
{ "_id" : ObjectId("514919fb23700b41723f94de"), "name" : "C", "timespent" : 300 } 
{ "_id" : ObjectId("514919fb23700b41723f94df"), "name" : "D", "timespent" : 400 } 
{ "_id" : ObjectId("514919fb23700b41723f94e0"), "name" : "E", "timespent" : 500 } 

集計(ハードコードされた)は次のようになります。

db.xx.aggregate([ 
    { $project: { 
    "_id": 0, 
    "range": { 
     $concat: [{ 
     $cond: [ { $lte: ["$timespent", 250] }, "range 0-250", "" ] 
     }, { 
     $cond: [ { $and: [ 
      { $gte: ["$timespent", 251] }, 
      { $lt: ["$timespent", 450] } 
     ] }, "range 251-450", "" ] 
     }, { 
     $cond: [ { $and: [ 
      { $gte: ["$timespent", 451] }, 
      { $lt: ["$timespent", 650] } 
     ] }, "range 450-650", "" ] 
     }] 
    } 
    }}, 
    { $group: { _id: "$range", count: { $sum: 1 } } }, 
    { $sort: { "_id": 1 } }, 
]); 

と結果は集約コマンドを使用するには、JSONオブジェクトとして "範囲"投影を構築する必要があります(または、文字列と

var ranges = [ 0, 250, 450, 650 ]; 
var rangeProj = { 
    "$concat": [] 
}; 

for (i = 1; i < ranges.length; i++) { 
    rangeProj.$concat.push({ 
    $cond: { 
     if: { 
     $and: [{ 
      $gte: [ "$timespent", ranges[i-1] ] 
     }, { 
      $lt: [ "$timespent", ranges[i] ] 
     }] 
     }, 
     then: "range " + ranges[i-1] + "-" + ranges[i], 
     else: "" 
    } 
    }) 
} 

db.xx.aggregate([{ 
    $project: { "_id": 0, "range": rangeProj } 
}, { 
    $group: { _id: "$range", count: { $sum: 1 } } 
}, { 
    $sort: { "_id": 1 } 
}]); 

、上記と同じ結果を返すであろう。そして、発電機はこのようになります

)JSON.parse(文字列)を使用します。

+0

この回答ありがとうございます。私はコンカットを考えたことはありませんでしたが、確かにいくつかのことを単純化しています。 –

+1

ありがとうございました簡潔で効果的な回答.. 詳細http://www.codefari.com/2015/08/case-statement-in-mongodb-query.html – Singh

+0

この回答は質問の特定のケースを解決しますが、このアプローチは排他的な条件のみをサポートします。結果の一部が複数のグループに分類される場合、それらのグループのそれぞれに集計されるのではなく、グループ「」 – ArnauOrriols

5

MongoDB 3.4から、$switch演算子を使用して、$projectステージでマルチスイッチ文を実行できます。

$groupパイプライン演算子はドキュメントを「範囲」でグループ化し、$sumアキュムレータ演算子を使用してグループごとに「カウント」を返します。

私たちのコレクションに次の書類を
db.collection.aggregate(
    [ 
     { "$project": { 
      "range": { 
       "$switch": { 
        "branches": [ 
         { 
          "case": { "$lte": [ "$timespent", 250 ] }, 
          "then": "0-250" 
         }, 
         { 
          "case": { 
           "$and": [ 
            { "$gt": [ "$timespent", 250 ] }, 
            { "$lte": [ "$timespent", 450 ] } 
           ] 
          }, 
          "then": "251-450" 
         }, 
         { 
          "case": { 
           "$and": [ 
            { "$gt": [ "$timespent", 450 ] }, 
            { "$lte": [ "$timespent", 650 ] } 
           ] 
          }, 
          "then": "451-650" 
         } 
        ], 
        "default": "650+" 
       } 
      } 
     }}, 
     { "$group": { 
      "_id": "$range", 
      "count": { "$sum": 1 } 
     }} 
    ] 
) 

{ "_id" : ObjectId("514919fb23700b41723f94dc"), "name" : "A", "timespent" : 100 }, 
{ "_id" : ObjectId("514919fb23700b41723f94dd"), "name" : "B", "timespent" : 200 }, 
{ "_id" : ObjectId("514919fb23700b41723f94de"), "name" : "C", "timespent" : 300 }, 
{ "_id" : ObjectId("514919fb23700b41723f94df"), "name" : "D", "timespent" : 400 }, 
{ "_id" : ObjectId("514919fb23700b41723f94e0"), "name" : "E", "timespent" : 500 } 

当社クエリ利回り:

{ "_id" : "451-650", "count" : 1 } 
{ "_id" : "251-450", "count" : 2 } 
{ "_id" : "0-250", "count" : 2 } 

私たちは、ソート、当社の文書パイプラインへ$sortのステージを追加することもできますこれは範囲によって異なりますが、これはlexicographic orderの文書を「範囲"