Esqueleto leftJoin类型与文档不一致问题咨询
Esqueleto Experimental LeftJoin 类型不匹配问题排查
问题场景
我在使用Esqueleto Experimental进行多表LeftJoin查询时遇到类型错误,代码如下:
select $ do (event :& _ :& tags) <- from $ table @EventRow `leftJoin` table @EventTagRow `on` ( \(event :& links) -> event ^. EventRowId ==. links ?. EventTagRowEventId ) `leftJoin` table @TagRow `on` ( \(_ :& link :& tag) -> tag ?. TagRowId ==. link ?. EventTagRowTagId ) where_ (event ^. EventRowTitle ==. val "one") pure tags
报错集中在event ^. EventRowId ==. links ?. EventTagRowEventId这一行:EventRowId返回普通ID类型,但==.要求两边均为Maybe <id type>。
查看相关运算符定义:
(?.) :: (PersistEntity val, PersistField typ) => SqlExpr (Maybe (Entity val)) -> EntityField val typ -> SqlExpr (Value (Maybe typ)) (==.) :: PersistField typ => SqlExpr (Value typ) -> SqlExpr (Value typ) -> SqlExpr (Value Bool)
从定义看==.要求左右类型完全一致,但官方文档(Database.Esqueleto.Experimental的"A New Syntax"章节"Example 2: Select with join")中的示例却允许普通类型与Maybe类型比较:
select $ do (people :& blogPosts) <- from $ table @Person `leftJoin` table @BlogPost `on` (\(people :& blogPosts) -> people ^. PersonId ==. blogPosts ?. BlogPostAuthorId) where_ (people ^. PersonAge >. val 18) pure (people, blogPosts)
疑问:是文档有误还是我的用法存在问题?
问题分析与解决方案
核心原因:连续Join的参数结构错误
文档示例是正常的,问题出在你对连续LeftJoin的参数结构理解错误:
- 单次LeftJoin中,
blogPosts的类型是SqlExpr (Maybe (Entity BlogPost)),blogPosts ?. BlogPostAuthorId得到SqlExpr (Value (Maybe BlogPostAuthorId));Esqueleto支持隐式将SqlExpr (Value typ)提升为SqlExpr (Value (Maybe typ)),所以示例中的==.能正常编译。 - 但连续LeftJoin时,第二次
on函数的参数是前一次Join的完整结果与当前表的Maybe Entity组合,而不是你写的_ :& link :& tag这种扁平结构——错误的模式匹配导致links/link的类型不是Maybe Entity,而是普通Entity,进而引发类型不匹配。
修正后的代码
select $ do (event :& maybeEventTag :& maybeTag) <- from $ table @EventRow `leftJoin` table @EventTagRow `on` (\(e :& et) -> e ^. EventRowId ==. et ?. EventTagRowEventId) `leftJoin` table @TagRow `on` (\((_ :& maybeEt) :& t) -> maybeEt ?. EventTagRowTagId ==. t ?. TagRowId) where_ (event ^. EventRowTitle ==. val "one") pure maybeTag
关键注意点
- 连续Join的参数结构:每次
leftJoin的on函数接收的是(前一次Join结果) :& 当前表的Maybe Entity,需要用嵌套的:&模式匹配拆分。 - 关联条件逻辑:原代码中第二次Join的条件逻辑反转,正确的关联应该是用
EventTagRow的TagId匹配TagRow的Id,同时保证两边都是Maybe类型(或通过隐式提升兼容)。 - 隐式类型提升:Esqueleto确实支持普通
Value到Maybe Value的隐式转换,文档示例不存在错误。
内容的提问来源于stack exchange,提问作者GTF
相关产品推荐
相关产品推荐

