2012-03-28 145 views
1

鑑於我有以下產品: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,orderDate存儲和總和所有訂單的訂單

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

的所有倉位價格。 我該如何解決這個問題?

例子:

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

編輯:
這個例子是由我知道,一個訂單的總不應該改變產品的價格歷史訂單更改。這是一個真正的「加入」是必要的一個壞榜樣。試想一下,當產品價格變化時,我必須改變總計。因此,我不想簡單地將總數字段歸一化。 在產品更改時「修復」訂單中的總計是可行的。但是,當我有很多不同的文檔類型引用該產品時,我需要使用某種功能逐個更改它們。 肖恩的回答把我的想法置於腦海,我可以用修改偵聽器修復非規範化字段,但每次某種文檔類型與另一個文檔類型相關時,我都需要一個偵聽器 - 聽起來像很多工作。

+0

請參閱http:// stackoverflow。com/questions/3033443/best-way-to-do-one-to-many-join-in-couchdb – 2012-03-28 15:57:14

+0

嗨,Ryan,感謝您的評論。我以前就知道這一點,我想我已經閱讀過任何SO線程:-) 你鏈接的是關於「一對多連接」,我要求「多對多」連接。我知道我可以在當前的couchdb版本中包含include_docs = true,這將有效地爲訂單提取相關產品,但我無法減少計算結果的總數。我沒有看到減少在這裏不起作用的原因。 – 2012-03-28 16:56:54

+0

好吧,閱讀http://stackoverflow.com/questions/3033443/best-way-to-do-one-to-many-join-in-couchdb再次意味着它不是一個「一對多連接」,這是討論。我想我缺少的是這裏的顯示功能。我不明白爲什麼我不能使用還原:-) – 2012-03-28 17:09:42

回答

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 =真&鍵=「2666df80782596200fca49557d758228」您例如以便與行項目,包括價格回報?

不幸的是,你不能使用include_docs和reduce,所以這可能是一個很好的起點,比如我之前提到的創建訂單摘要文檔看起來像你想要的最終答案的_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 

順便說一句,在這種情況下, ,關鍵值是訂單日期...

+0

你確定你的發射會得到那些產品ID嗎?因爲你有一條if語句將範圍縮小到命令範圍,然後你嘗試從這個圈子中發出id。 – anvarik 2012-03-28 23:18:24

+0

感謝您的詳細解答!我知道有人會說訂單的價格會變成歷史和只讀的:-)這個例子是彌補的,是一個真正的「加入」是必要的一個壞例子。但是,_changes監聽器可能是反規範化的一個很好的解決方案 - 當原始文檔發生變化時,它可以通過所有文檔,在這些文檔中我將規範化了一個值並進行了更改。鏈接的文檔解決方案看起來不錯 - 昨天晚上我讀了http://goo.gl/838Zj後得到了類似的東西,但沒有在這裏發佈。以後再試試你的代碼 – 2012-03-29 07:18:22

+0

好的,我可以證實它是一個解決方案。列表函數會被每個請求調用並不是很好。如果我們找到一個與reduce一起工作的解決方案,結果(「總計」)將存儲在b-tree中。 – 2012-03-29 09:19:32