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

时间:2017-04-02 01:49:39

标签: haskell yesod persistent esqueleto

来自脚手架网站中的人为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 个答案:

答案 0 :(得分:1)

这可能被标记为Outer Joins with Esqueleto的副本;但不同之处在于预测。

在处理任何外连接时,所有可能返回null的表都应该使用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

此外,如果链接了多个表; e.g。

i

然后 is on $ i ?. InventoryId ==. is ?. InventorySkuItem on $ cs ^. ContainerSlotItem ==. i ?. InventoryId (库存SKU表)应使用该语法进行投影:

Route::any('/home/createuser/{id}','HomeController@createuser');