2014-02-10 108 views
7

我一直在尋找持久庫來與sql數據庫接口。假設我有一個包含配方的數據庫,包含Recipe,Ingredient和RecIng表格。Haskell持久連接與Esqueleto

我(固然有限)的固定理解使我相信我應該這樣定義的表:

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| 
Recipe 
    title String 
Ingredient 
    name String 
RecIng 
    recId RecipeId 
    ingId IngredientId 
    quantity Int 
|] 

有了這個,就可以使用Esqueleto得到內這些表之間的連接:

select $ 
from $ \(i `InnerJoin ` ri `InnerJoin` r) -> do 
    on (r ^. RecipeId ==. ri ^. RecIngIngId) 
    on (i ^. IngredientId ==. ri ^. RegIngRecId) 
    return (r, ri, i) 

這將返回(Recipe,RecIng,Ingredient)的元組。

我真正想要的是查詢導致以下食譜的方式:

data Recipe = Recipe { title :: String 
        , ingredients :: [Ingredient] 
        } 

data Ingredient = Ingredient { name :: String 
          , quantity :: Integer 
          } 

除了定義一組額外的數據類型,並且轉換的元組,是有這樣一個最佳實踐之類的事情?

+4

就我所知,您需要手動在持久表示和自定義數據類型之間進行映射。這是一個鍋爐板,但在這種情況下,我有點喜歡它,你可以清楚地映射到數據庫中的內容,並且從中轉換到另一種類型是非常安全的:-)也可能有些情況下, t想要通過連接獲取數據。 –

回答

7

對Adam的評論+1,這是IMO的正確答案。

可能會使用嵌入式實體,這實質上意味着將JSON編碼到每個配方的成分列表中。但是,這將是糟糕的SQL設計,會導致更新表鎖定問題,並且不能很好地擴展大量成分。

換句話說,您希望使用的Haskell表示與將數據存儲在數據庫中的正確方式之間存在不匹配。這並不意味着數據庫格式或Haskell數據類型存在問題:這是邏輯上的區別。對這種差距的正確迴應是有兩種數據類型和一種智能的方式來在它們之間進行轉換。

+0

對於通過esqueleto連接(大概會使用一個數據庫事務,但可能會佔用大量內存)通過一個查詢獲取所有內容,或者使用標準持久性來獲取特定結果,您是否有更好的意見? – oneway

+1

任何一種方法都可以通過單個數據庫事務完成。如果您沒有加入,您將避免爲每種成分傳輸配方信息的開銷。但是使用連接可以避免額外的數據庫往返,對於多個配方來說,這可能會更勝一籌。如果你正在談論原始性能,我可以給你的唯一真正答案是實現兩者和基準。如果你正在尋找更好的風格:選擇一個你可以更好地理解的風格。 –