Возвращая `Maybe (Entity a)` из Esqueleto `LeftOuterJoin`
Из надуманного 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 ответ
Это, вероятно, может быть помечено как дубликат Внешних соединений с Esqueleto; однако разница в проекции.
При работе с любыми внешними объединениями все таблицы, которые могут возвращаться нулевыми, должны иметь все проекции, выполненные с ?.
синтаксис. Это заставит сущности таблицы стать 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
Тогда оба i
а также is
(таблица SKU инвентаризации) должен быть спроектирован с таким синтаксисом:
on $ i ?. InventoryId ==. is ?. InventorySkuItem
on $ cs ^. ContainerSlotItem ==. i ?. InventoryId