2014-02-10 104 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

任何一种方法都可以通过单个数据库事务完成。如果您没有加入,您将避免为每种成分传输配方信息的开销。但是使用连接可以避免额外的数据库往返,对于多个配方来说,这可能会更胜一筹。如果你正在谈论原始性能,我可以给你的唯一真正答案是实现两者和基准。如果你正在寻找更好的风格:选择一个你可以更好地理解的风格。 –