MongoDB中的有条件求和

69

我的MongoDB集合类似于SQL中的以下表格:

Sentiments(公司,情感)

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

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: ? } } }
);

2
你可能可以在求和中使用 $cond:http://docs.mongodb.org/manual/reference/aggregation/#conditional-expressions,但这听起来像是对速度和可扩展性查询的破坏,我可以想象这个查询在 SQL 中很慢,在这里处理中等结果集也会很慢。 - Sammaye
@Sammaye 我正在尝试用 {$cond: { Sentiment: { $gte: 0} } } 替换 '?'。但是这似乎是错误的语法...我没有得到任何输出。 - Aafreen Sheikh
2
$cond 的作用类似于 if 语句,就像 case 一样,因此第一个表达式将是:Sentiment >0,然后是 Sentiment,接着是第一个 $sum 中的第一个 $cond 的 0 - Sammaye
3个回答

85

正如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'}
    }});

63
从版本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阶段是多余的,因为$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 }

请注意,这也适用于希望检查布尔值或字符串而不是数字的用例。使用 $eq 进行直接匹配,并确保将您的 then 值设置为您想要计数的数字(例如 then: 1)。 - Noah Kreiger

10
解释上面的代码片段,它使用了数组语法:
PosSentiment: {$cond: [{$gt: ['$Sentiment', 0]}, '$Sentiment', 0]}

相等于:

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

数组语法将长格式简化为 { $cond: [if, then, else] }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接