CouchDB:查询多对多“关系”

时间:2012-03-28 13:40:23

标签: couchdb

鉴于我有以下产品:

{
   "_id": "2666df80782596200fca49557d757870",
   "_rev": "3-99382057f6c484526835f1042753ccf2",
   "type": "product",
   "name": "Shirt",
   "hersteller": "oska",
   "price": 11.15
}

{
   "_id": "2666df80782596200fca49557d758e8c",
   "_rev": "1-01cc88e69e5ff30f0d011fdf61fbedbc",
   "type": "product",
   "name": "Pullover",
   "hersteller": "acme",
   "price": 7.58
}

订单

{
   "_id": "2666df80782596200fca49557d758228",
   "_rev": "2-0b4b3a8605893b60c962b8ae78f0b775",
   "type": "order",
   "orderDate": "01.01.2012",
   "positions": [
       "2666df80782596200fca49557d757870",
       "2666df80782596200fca49557d758e8c"
   ]
}

我想查询所有订单的_id,orderDate和所有头寸价格的总和。 我该如何处理?

示例:

[
 { "_id": "2666df80782596200fca49557d758228", "orderDate": "01.01.2012", "total": 18.73 }
]

编辑:
这个例子组成了,我知道订单的总数不应该改变历史订单的产品价格变化。对于需要真正“加入”的事情来说,这是一个不好的例子。想象一下,当产品价格发生变化时,我必须更改总数。因此,我不想简单地对总计字段进行非规范化。 当产品发生变化时,可以“修复”订单中的总计。但是,当我有很多不同的文档类型引用产品时,我需要使用某些功能逐个类型地更改它们。 Sean的回答把这个想法放到我的脑海中,我可以用一个改变监听器来修复非规范化的字段,但是每次有些doc类型与另一个相关时我都需要一个监听器 - 听起来像是很多工作。

1 个答案:

答案 0 :(得分:1)

我认为最好的方法是让一个_changes监听器在记录变为历史记录时(即只读)用位置中每个产品的价格值更新订单文档。对于过去的订单,价格可能会发生变化似乎是错误的,而且您无法通过兔子洞价格日期范围打印正确的报告/发票....

要解决您所说的问题,如果您有一些可以使用JSON的客户端代码,Linked Documents下指向的技巧可能有所帮助。给定地图函数,如:

function(doc) {
 if(doc.type == 'order') {
  for(var position in doc.positions) emit(doc._id,{_id: doc.positions[position]});
 }
}

您可以使用?include_docs = true& key =" 2666df80782596200fca49557d758228" 返回包含价格在内的订单项的示例订单。

不幸的是,你不能使用带有reduce的include_docs,所以这可能是我提到的_changes守护进程的一个很好的起点,可以创建一个看起来像你想要的最终答案的订单摘要文档。


[编辑添加解决方案] 好吧,我找到了一种方法,但它并不漂亮。

给定具有此视图映射功能的设计文档:

function(doc) {
 if(doc.type == 'order') {
  for(var p in doc.positions) emit(doc.orderDate, {_id: doc.positions[p]});
 }
}

和此列表功能:

function(head, req) {
 var count = 0;
 var dates = {};
 var totals = {};

 while(r = getRow()) {
  if(!(r.id in dates)) {
   count += 1;
   dates[r.id] = r.key;
   totals[r.id] = 0; 
  }
  totals[r.id] += r.doc.price;
 }
 start({'headers': {'Content-Type': 'application/json'}});
 send('[\n');
 for(var order in dates) {
  count -= 1;
  send(JSON.stringify({'_id': order, 'orderDate': dates[order], 'total': totals[order]}));
  send((count > 0)?',\n':'\n');
 }
 send(']\n');
}

您想要的结果是

/db/_design/[Design Doc Name]/_list/[List Function Name]/[Map Function Name]?include_docs=true
在这种情况下,键值是orderDate ...