MongoDB中的条件$ sum

时间:2012-12-31 14:12:09

标签: sql mongodb mongodb-query aggregation-framework

我在mongodb中的集合类似于SQL中的下表:

观点(公司,情绪)

现在,我需要执行这样的查询:

SELECT
  Company, 
  SUM(CASE WHEN Sentiment >0 THEN Sentiment ELSE 0 END) AS SumPosSenti, 
  SUM(CASE WHEN Sentiment <0 THEN Sentiment ELSE 0 END) AS SumNegSenti
FROM Sentiments
GROUP BY Company

我应该怎么做才能在Mongo中编写此查询?我陷入了以下问题:

db.Sentiments.aggregate(
{ $project: {_id:0, Company:1, Sentiment: 1} },
{ $group: {_id: "$Company", SumPosSenti: {$sum: ? }, SumNegSenti: {$sum: ? } } }
);

3 个答案:

答案 0 :(得分:47)

正如Sammaye建议的那样,您需要使用$cond聚合投影运算符来执行此操作:

db.Sentiments.aggregate(
    { $project: {
        _id: 0,
        Company: 1,
        PosSentiment: {$cond: [{$gt: ['$Sentiment', 0]}, '$Sentiment', 0]},
        NegSentiment: {$cond: [{$lt: ['$Sentiment', 0]}, '$Sentiment', 0]}
    }},
    { $group: {
        _id: "$Company",
        SumPosSentiment: {$sum: '$PosSentiment'},
        SumNegSentiment: {$sum: '$NegSentiment'}
    }});

答案 1 :(得分:28)

从版本3.4开始,我们可以使用$switch运算符,该运算符允许$group阶段中的逻辑条件处理。当然,我们仍然需要使用$sum累加器来返回总和。

db.Sentiments.aggregate(
    [
        { "$group": { 
            "_id": "$Company",  
            "SumPosSenti": { 
                "$sum": { 
                    "$switch": { 
                        "branches": [ 
                            { 
                                "case": { "$gt": [ "$Sentiment", 0 ] }, 
                                "then": "$Sentiment"
                            }
                        ], 
                        "default": 0 
                    }
                }
            }, 
            "SumNegSenti": {
                "$sum": { 
                    "$switch": { 
                        "branches": [ 
                            { 
                                "case": { "$lt": [ "$Sentiment", 0 ] }, 
                                "then": "$Sentiment"
                            }
                        ], 
                        "default": 0 
                    } 
                }
            }
        }}
    ]
)

如果您尚未将mongod迁移到3.4或更高版本,请注意此$project中的answer阶段是多余的,因为$cond运算符会返回一个数字值意味着您可以$group将文档$sum应用于$cond表达式。

这将提高应用程序的性能,尤其是对于大型集合。

db.Sentiments.aggregate(
    [
        { '$group': {
            '_id': '$Company',
            'PosSentiment': { 
                '$sum': {
                    '$cond': [
                        { '$gt': ['$Sentiment', 0]}, 
                        '$Sentiment', 
                        0
                    ]
                }
            },
            'NegSentiment': { 
                '$sum': {
                    '$cond': [
                        { '$lt': ['$Sentiment', 0]}, 
                        '$Sentiment', 
                        0
                    ]
                }
            }
        }}
    ]
)

考虑一个带有以下文件的集合Sentiments:

{ "Company": "a", "Sentiment" : 2 }
{ "Company": "a", "Sentiment" : 3 }
{ "Company": "a", "Sentiment" : -1 }
{ "Company": "a", "Sentiment" : -5 }

聚合查询产生:

{ "_id" : "a", "SumPosSenti" : 5, "SumNegSenti" : -6 }

答案 2 :(得分:2)

解释上面使用数组语法的片段:

public class WebLinkActivity extends Activity {
    WebView mWebView;
    private Button mCancel;
    private String mUrl;
    ProgressDialog progressDialog;

public void clearCookies() {
    try {
        android.webkit.CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.removeAllCookie();
    } catch (Exception ex) {
    }
}

public void closeWebLinkClick(View view) {
    Utilities.logInfo("closeWebLinkClick", "Close WebLink Full Screen");
    setResult(Activity.RESULT_CANCELED, null);
    finish();
}

@Override
public void onResume() {
    super.onResume();
}

@Override
protected void onPause() {
    super.onPause();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setResult(Activity.RESULT_OK, null);
    Bundle extras = getIntent().getExtras();
    mUrl = extras.getString("url");

    // Setup the web view. It will redirect to SSO site for login
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setContentView(R.layout.activity_weblink);

    mCancel = (Button) findViewById(R.id.closeWebLink);
    mCancel.setTextColor(Color.WHITE);

    mWebView = (WebView) findViewById(R.id.weblinkViewer);

    progressDialog = new ProgressDialog(WebLinkActivity.this);
    progressDialog.setMessage("Loading...");
    progressDialog.show();


    // Link not provided
    if (Utilities.stringIsBlank(mUrl)) {
        String dataString = "<head><style type='text/css'>"
                + "body{margin:auto auto;text-align:center;vertical-align: middle;} </style></head>"
                + "<body><img src=\"invalid_link.png\"/></body>";
        mWebView.loadDataWithBaseURL("file:///android_res/drawable/", dataString, "text/html", "utf-8", null);
        if (progressDialog != null)
            progressDialog.dismiss();
    }
    // Network is not available
    else if (!Utilities.isNetworkAvailable()) {
        String dataString = "<head><style type='text/css'>"
                + "body{margin:auto auto;text-align:center;vertical-align: middle;} </style></head>"
                + "<body><img src=\"not_connected.png\"/></body>";
        mWebView.loadDataWithBaseURL("file:///android_res/drawable/", dataString, "text/html", "utf-8", null);
        if (progressDialog != null)
            progressDialog.dismiss();
    }
    // Normal processing
    else {
        // See if url is missing http. If so add it in
        if (!mUrl.toLowerCase().contains("http")) {
            mUrl = "http://" + mUrl;
        }

        // Most likely an image is ends with image attribute
        if (Utilities.isImage(mUrl)) {
            String html = "<html><body><img src=\"" + mUrl + "\" width=\"100%\"/></body></html>";
            mWebView.loadData(html, "text/html", null);
            if (progressDialog != null)
                progressDialog.dismiss();
        }
        // Normal web view
        else {
            mWebView.setWebChromeClient(new WebChromeClient() {
                public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
                    // callback.invoke(String origin, boolean allow, boolean remember);
                    callback.invoke(origin, true, false);
                }
            });

            mWebView.setWebViewClient(new WebViewClient() {
                @Override
                public boolean shouldOverrideUrlLoading(WebView view, String url) {
                    view.loadUrl(url);
                    return true;
                }

                @Override
                public void onPageFinished(WebView view, String url) {
                    if (progressDialog.isShowing()) {
                        progressDialog.dismiss();
                    }
                }
            });
            mWebView.getSettings().setJavaScriptEnabled(true);
            mWebView.getSettings().setAppCacheEnabled(true);
            // HTML5 API flags
            mWebView.getSettings().setAppCacheEnabled(true);
            mWebView.getSettings().setDatabaseEnabled(true);
            mWebView.getSettings().setDomStorageEnabled(true);
            mWebView.loadUrl(mUrl);
        }
    }
}
}

等于:

PosSentiment: {$cond: [{$gt: ['$Sentiment', 0]}, '$Sentiment', 0]}

数组语法将长语法概括为PosSentiment: {$cond: { if: {$gt: ['$Sentiment', 0]}, then: '$Sentiment', else: 0} }