Haskell Persistent 加入 Esqueleto

Posted

技术标签:

【中文标题】Haskell Persistent 加入 Esqueleto【英文标题】:Haskell Persistent Joins with Esqueleto 【发布时间】:2014-02-10 19:50:18 【问题描述】:

我一直在研究 Persistent 库以与 sql 数据库交互。假设我有一个包含食谱的数据库,其中包含食谱、成分和 RecIng 表。

我(诚然有限)对持久性的理解使我相信我应该像这样定义表:

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
                             

除了定义一组额外的数据类型和转换元组之外,还有做这种事情的最佳实践吗?

【问题讨论】:

据我所知,您需要手动在持久表示和自定义数据类型之间进行映射。这有点像样板,但在这种情况下,我有点喜欢它,您可以清楚地映射到数据库中的内容,并且将其转换为另一种类型是非常安全的 :-) 也可能存在您不这样做的情况不想通过连接获取数据。 【参考方案1】:

+1 对亚当的评论,这是 IMO 的正确答案。

可以采取的另一种方法是使用嵌入式实体,这实际上意味着将 JSON 编码成分列表到每个配方中。但这会是糟糕的 SQL 设计,会导致更新时出现表锁定问题,并且不能很好地扩展大量成分。

换句话说,您要使用的 Haskell 表示与在数据库中存储数据的正确方法之间存在不匹配。这并不意味着您的数据库格式或 Haskell 数据类型存在问题:这是逻辑上的差异。对这一差距的正确回应是拥有两种数据类型以及一种在它们之间进行转换的智能方式。

【讨论】:

您是否对通过 esqueleto 联接(可能会使用一个数据库事务,但可能占用大量内存)在一个查询中获取所有内容或使用标准持久以获得特定结果? 这两种方法都可以通过单个数据库事务来完成。如果您没有加入,您将避免为每种成分传输配方信息的开销。但是使用连接避免了额外的数据库往返,并且对于多个配方可能更多的是性能上的胜利。如果您谈论的是原始性能,我能给您的唯一真正答案是同时实现和基准测试。如果您正在寻找更好的风格:选择您能更好理解的风格。

以上是关于Haskell Persistent 加入 Esqueleto的主要内容,如果未能解决你的问题,请参考以下文章

“类型变量不明确”在 Haskell Yesod 中使用 Persistent

Haskell Persistent 上的 CRUD 模式

Haskell的Persistent sometmes返回500内部服务器错误

Haskell Persistent:可以按包含指定值的字段选择所有行

Haskell Persistent Library - 如何从我的数据库中获取数据到我的前端?

使用 Persistent 输入与数据库的关系