在我之前询问的CouchDB问题(Can you implement document joins using CouchDB 2.0 'Mango'?)中,答案提到了创建域对象而不是在Couch中存储关系数据。
然而,我的用例不一定是在Couch中存储关系数据,而是为了压缩关系数据。例如,我有几个供应商收集的Invoice
实体。所以我有两个不同的模式用于该实体。
所以我最终可能会在Couch中找到2个文档:
{
"type": "Invoice",
"subType": "supplier B",
"total": 22.5,
"date": "10 Jan 2017",
"customerName": "me"
}
{
"type": "Invoice",
"subType": "supplier A",
"InvoiceTotal": 10.2,
"OrderDate": <some other date format>,
"customerName": "me"
}
我也有这样的文档:
{
"type": "Customer",
"name": "me",
"details": "etc..."
}
我的意图是“平坦化”&#39; Invoice实体,然后加入reduce函数。因此,map函数如下所示:
function(doc) {
switch(doc.type) {
case 'Customer':
emit(doc.customerName, { doc information ..., type: "Customer" });
break;
case 'Invoice':
switch (doc.subType) {
case 'supplier B':
emit (doc.customerName, { total: doc.total, date: doc.date, type: "Invoice"});
break;
case 'supplier A':
emit (doc.customerName, { total: doc.InvoiceTotal, date: doc.OrderDate, type: "Invoice"});
break;
}
break;
}
}
然后我会使用reduce函数来比较具有相同customerName(即连接)的文档。
这是否可以使用CouchDB?如果没有,为什么?
答案 0 :(得分:0)
首先为迟到的事道道歉,我以为我直接看了它,但是自从我们前几天交换以来,我还没有去过。
Reduce函数只应用于减少标量值,而不是用于聚合数据。因此,您不会使用它们来实现诸如连接或删除重复项之类的事情,但您可以使用它们来计算每个客户的发票数量 - 您会看到这个想法。原因是你只能对你的reduce函数调用(传递记录的顺序,rereduce参数等等)做出弱假设,这样你就很容易遇到严重的性能问题。
但这是设计的,因为reduce函数的预期用途是减少标量值。考虑它的一个简单方法是说在reduce函数中不应该发生过滤,过滤和检查键之类的事情应该在map中完成。
如果您只想比较具有相同客户名称的文档,则根本不需要reduce函数,您可以查询视图中的以下参数:
startkey=["customerName"]
endkey=["customerName", {}]
否则,您可能需要先创建一个单独的视图来过滤客户,然后返回其名称,然后使用这些名称使用keys
view parameter批量查询您的视图。如果您只想一次过滤一个客户,和/或需要以部分方式匹配复杂密钥,则Startkey / endkey是好的。
如果您追求的是数字,您可能想要这样做:
if(doc.type == "Invoice") {
emit([doc.customerName, doc.supplierName, doc.date], doc.amount)
}
然后使用the _stats built-in reduce function获取金额(总和,最小,最大值)的统计数据
为了获得供应商花费的金额,您只需要对视图进行简化查询,并使用参数group_level = 2按密钥的前2个元素进行聚合。您可以将其与startkey和endkey结合使用,以过滤此键的特定值:
startkey=["name1", "supplierA"]
endkey=["name1", "supplierA", {}]
然后,您可以根据此示例构建以执行以下操作:
if(doc.type == "Invoice") {
emit(["BY_DATE", doc.customerName, doc.date], doc.amount);
emit(["BY_SUPPLIER", doc.customerName, doc.supplierName], doc.amount);
emit(["BY_SUPPLIER_AND_DATE", doc.customerName, doc.supplierName, doc.date], doc.amount);
}
希望这有帮助
答案 1 :(得分:0)
通过视图“规范化”不同的模式(或subTypes
)是完全可以的。但是,您无法基于这些规范化模式创建视图,并且从长远来看,可能很难管理不同的模式。
更好的解决方案可能是在将文档写入CouchDB之前对文档进行规范化。如果您仍需要原始模式中的文档,则可以添加子属性original
,以便将文档存储为原始格式。这样可以更轻松地处理数据:
{
"type": "Invoice",
"total": 22.5,
"date": "2017-01-10T00:00:00.000Z",
"customerName": "me",
"original": {
"supplier": "supplier B",
"total": 22.5,
"date": "10 Jan 2017",
"customerName": "me"
}
},
{
"type": "Invoice",
"total": 10.2,
"date": "2017-01-12T00:00:00:00.000Z,
"customerName": "me",
"original": {
"subType": "supplier A",
"InvoiceTotal": 10.2,
"OrderDate": <some other date format>,
"customerName": "me"
}
}
我还将日期转换为ISO格式,因为它与new Date()
一起解析,正确排序并且是人类可读的。您可以轻松发出按年,月,日等分组的发票。
最好只使用内置函数使用reduce,因为必须在查询上重新执行reduce,并且在许多文档上执行JavaScript是一项复杂且耗时的操作,即使数据库根本没有更改。您可以在CouchDB process中找到有关减少流程的更多信息。在将文档存储到CouchDB中之前,尽可能多地预处理文档更有意义。