CouchDB-Map-Reduce类似于SQL Group by

CouchDB-Map-Reduce类似于SQL Group by,couchdb,Couchdb,考虑存储在CouchDB中的以下示例文档 { "_id":...., "rev":...., "type":"orders", "Period":"2013-01", "Region":"East", "Category":"Stationary", "Product":"Pen", "Rate":1, "Qty":10, "Amount":10 } { "_id":...., "rev":...., "type":"orders", "Period":"2013-02", "Region"

考虑存储在CouchDB中的以下示例文档

 {
"_id":....,
"rev":....,
"type":"orders",
"Period":"2013-01",
"Region":"East",
"Category":"Stationary",
"Product":"Pen",
"Rate":1,
"Qty":10,
"Amount":10
}

{
"_id":....,
"rev":....,
"type":"orders",
"Period":"2013-02",
"Region":"South",
"Category":"Food",
"Product":"Biscuit",
"Rate":7,
"Qty":5,
"Amount":35
}
考虑以下SQL查询

SELECT Period, Region,Category, Product, Min(Rate),Max(Rate),Count(Rate), Sum(Qty),Sum(Amount)
FROM Sales
GROUP BY Period,Region,Category, Product;
是否可以在couchdb中创建与上述SQL查询等价的map/reduce视图并生成类似的输出

[
    {
        "Period":"2013-01",
        "Region":"East",
        "Category":"Stationary",
        "Product":"Pen",
        "MinRate":1,
        "MaxRate":2,
        "OrdersCount":20,
        "TotQty":1000,
        "Amount":1750
    },
    {
    ... 
    }

]

我将提出一个非常简单的解决方案,要求在“select”子句中聚合每个变量一个视图。虽然可以在单个视图中聚合所有变量,但reduce函数要复杂得多

设计文档如下所示:

{
    "_id": "_design/ddoc",
    "_rev": "...",
    "language": "javascript",
    "views": {
        "rates": {
            "map": "function(doc) {\n  emit([doc.Period, doc.Region, doc.Category, doc.Product], doc.Rate);\n}",
            "reduce": "_stats"
        },
        "qty": {
            "map": "function(doc) {\n  emit([doc.Period, doc.Region, doc.Category, doc.Product], doc.Qty);\n}",
            "reduce": "_stats"
        }
    }
}
{"rows":[
{"key":["2013-01","East","Stationary","Pen"],"value":{"sum":4,"count":3,"min":1,"max":2,"sumsqr":6}},
{"key":["2013-01","North","Stationary","Pen"],"value":{"sum":1,"count":1,"min":1,"max":1,"sumsqr":1}},
{"key":["2013-01","South","Stationary","Pen"],"value":{"sum":0.5,"count":1,"min":0.5,"max":0.5,"sumsqr":0.25}},
{"key":["2013-02","South","Food","Biscuit"],"value":{"sum":7,"count":1,"min":7,"max":7,"sumsqr":49}}
]}
现在,您可以查询
/\u design/ddoc/\u view/rates?group\u level=4
,以获取有关“Rate”变量的统计信息。结果应该如下所示:

{
    "_id": "_design/ddoc",
    "_rev": "...",
    "language": "javascript",
    "views": {
        "rates": {
            "map": "function(doc) {\n  emit([doc.Period, doc.Region, doc.Category, doc.Product], doc.Rate);\n}",
            "reduce": "_stats"
        },
        "qty": {
            "map": "function(doc) {\n  emit([doc.Period, doc.Region, doc.Category, doc.Product], doc.Qty);\n}",
            "reduce": "_stats"
        }
    }
}
{"rows":[
{"key":["2013-01","East","Stationary","Pen"],"value":{"sum":4,"count":3,"min":1,"max":2,"sumsqr":6}},
{"key":["2013-01","North","Stationary","Pen"],"value":{"sum":1,"count":1,"min":1,"max":1,"sumsqr":1}},
{"key":["2013-01","South","Stationary","Pen"],"value":{"sum":0.5,"count":1,"min":0.5,"max":0.5,"sumsqr":0.25}},
{"key":["2013-02","South","Food","Biscuit"],"value":{"sum":7,"count":1,"min":7,"max":7,"sumsqr":49}}
]}
对于“Qty”变量,查询将是
/\u design/ddoc/\u view/Qty?group\u level=4


使用
group\u level
属性,您可以控制要执行聚合的级别。例如,使用
group_level=2
进行查询将聚合到“Period”和“Region”。

首先,我相信@benedolph的答案是最佳实践和最佳案例场景。理想情况下,每个reduce应该返回1个标量值,以使代码尽可能简单

但是,您确实需要发出多个查询来检索问题所描述的完整结果集。如果您没有并行运行查询的选项,或者将查询数量控制在较低水平非常重要,那么可以一次完成所有查询

您的地图功能将保持相当简单:

功能(doc){
发出(【单据期间、单据区域、单据类别、单据产品】、单据);
}
reduce函数会变得冗长:

函数(键、值、返回值){
//helper函数,用于对对象数组中指定字段的所有值求和
函数sumField(arr,field){
返回arr.reduce(功能(上、当前){
返回上一个+当前[字段];
}, 0);
}
//helper函数,用于从对象数组创建仅包含单个属性的数组
//(此函数来自下划线.js,至少它的名称和概念)
函数清除(arr,字段){
返回arr.map(函数(项){
退货项目[字段];
});
}
//rereduce使这一点更具挑战性,我现在无法彻底测试这一点
//有关更多信息,请参阅CouchDB wiki
如果(减少){
//rereduce处理传递值
//(因此,下面的“值”是先前reduce函数的结果,而不是map函数)
返回{
ordersont:sumField(值为“ordersont”),
MinRate:Math.min.apply(Math,pull(值,“MinRate”),
MaxRate:Math.max.apply(Math,pull(值,“MaxRate”),
TotQty:sumField(值,“TotQty”),
金额:sumField(值,“金额”)
};
}否则{
风险值比率=采摘(值,“比率”);
//这需要一组文档,并提供您要求的统计信息
返回{
OrderScont:values.length,
MinRate:Math.min.apply(Math,rates),
MaxRate:Math.max.apply(Math,rates),
TotQty:sumField(值,“数量”),
金额:sumField(值,“金额”)
};
}
}
我根本无法测试此代码的“rereduce”分支,您必须自己测试。(但这应该行得通)有关reduce vs rereduce的信息,请参阅

我在顶部添加的helper函数实际上使代码总体上更短,更易于阅读,它们在很大程度上受我的使用经验的影响。但是,不能在reduce函数中包含CommonJS模块,因此必须手动编写


同样,最好的情况是让每个聚合字段都获得自己的map/reduce索引,但是如果您没有选择,那么上面的代码应该可以获得您在问题中描述的内容。

您尝试过什么吗?一个简单的视图就可以了。一些示例文档将有助于有人给您提供更具体/详细的答案。@DominicBarnes提供了一些示例文档。感谢您的时间和详细答案。我将遵循您的解决方案。谢谢您的解决方案。这对于聚合单个变量很方便。