从 Esqueleto `LeftOuterJoin` 返回 `Maybe (Entity a)`

Posted

技术标签:

【中文标题】从 Esqueleto `LeftOuterJoin` 返回 `Maybe (Entity a)`【英文标题】:Returning `Maybe (Entity a)` from Esqueleto `LeftOuterJoin` 【发布时间】:2017-04-02 01:49:38 【问题描述】:

来自一个人为的config/models 脚手架网站:

Inventory
  name        Text
  description Text
Container
  name        Text
ContainerSlot
  container   ContainerId
  item        InventoryId Maybe

现在,使用 Esqueleto,我想使用 LeftOuterJoin 获取容器中的插槽,如果尚未分配实际库存,则实际库存为空。

selectContainerSlots containerKey = do
  stuff <- select $ from $ \(cs `LeftOuterJoin` i) -> do
    on $ cs ^. ContainerSlotItem ==. just (i ^. InventoryId)
    where_ $ cs ^. ContainerSlotContainer ==. val containerKey
    return (cs, i)
  return $ uncurry buildStuff <$> stuff

由于联接的“外部”性质,我预计 buildStuff 需要以下签名:

buildStuff :: Entity ContainerSlot -> Maybe (Entity Inventory) -> Result

但发现它需要以下内容:

buildStuff :: Entity ContainerSlot -> Entity Inventory -> Result

Inventory 字段填充有NULL 值时,这会导致运行时失败。

PersistMarshalError "field id: int64 Expected Integer, received: PersistNull"

有没有办法将Entity Inventory 投影为Maybe (Entity Inventory)

【问题讨论】:

【参考方案1】:

这可能被标记为Outer Joins with Esqueleto 的重复;但不同之处在于投影。

在处理任何外连接时,所有可能返回 null 的表都应具有使用 ?. 语法完成的 all 投影。这将强制表的实体变为Maybe (Entity a) 所以上面的解决方案是

selectContainerSlots containerKey = do
  stuff <- select $ from $ \(cs `LeftOuterJoin` i) -> do
    on $ cs ^. ContainerSlotItem ==. i ?. InventoryId
    where_ $ cs ^. ContainerSlotContainer ==. val containerKey
    return (cs, i)
  return $ uncurry buildStuff <$> stuff

此外,如果链接了多个表;例如

select $ from $ \(cs `LeftOuterJoin` (i `InnerJoin` is)) -> do

然后 both iis(库存 SKU 表)应该使用该语法进行投影:

  on $ i ?. InventoryId ==. is ?. InventorySkuItem
  on $ cs ^. ContainerSlotItem ==. i ?. InventoryId

【讨论】:

以上是关于从 Esqueleto `LeftOuterJoin` 返回 `Maybe (Entity a)`的主要内容,如果未能解决你的问题,请参考以下文章

Esqueleto:如何使用联接删除项目

Haskell:Yesod 和 Esqueleto

用 esqueleto 计算行数

使用 Esqueleto 处理列表类型

在 Esqueleto 中获取聚合函数的结果

在 Esqueleto 中更新具有特定 ID 的行