Reputation: 3678
From a contrived config/models
in a scaffolded site:
Inventory
name Text
description Text
Container
name Text
ContainerSlot
container ContainerId
item InventoryId Maybe
Now, using Esqueleto, I want to use LeftOuterJoin
to get the slots in a container, with the actual inventory empty if it has not been assigned.
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
I would expect buildStuff
to need the following signature due to the "outer" nature of the join:
buildStuff :: Entity ContainerSlot -> Maybe (Entity Inventory) -> Result
but find that it needs the following:
buildStuff :: Entity ContainerSlot -> Entity Inventory -> Result
Which causes runtime failures when (predictably) the the Inventory
fields are filled with NULL
values.
PersistMarshalError "field id: int64 Expected Integer, received: PersistNull"
Is there a way to project the Entity Inventory
as an Maybe (Entity Inventory)
?
Upvotes: 3
Views: 306
Reputation: 3678
This could probably be marked a duplicate of Outer Joins with Esqueleto; however the difference is in the projection.
When dealing with any outer joins, all tables which may come back null should have all projections done with the ?.
syntax. This will force the table's entities to become Maybe (Entity a)
So the solution to the above is
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
Additionally, if more than one table is chained; e.g.
select $ from $ \(cs `LeftOuterJoin` (i `InnerJoin` is)) -> do
Then both i
and is
(inventory SKU table) should be projected with that syntax:
on $ i ?. InventoryId ==. is ?. InventorySkuItem
on $ cs ^. ContainerSlotItem ==. i ?. InventoryId
Upvotes: 2