左加入Opaleye
Left join in Opaleye
我一直在尝试在项目中使用 Opaleye 运行 左连接,但我无法编译代码。我从两个 "models" 开始,它们代表关联的表:
第一个:
data ModelA' a b = Model { primA :: a, foreignA :: b }
type ModelA = ModelA' UUID UUID
type ModelAColumn = ModelA' (Column PGUuid) (Column (Nullable PGUuid))
$(makeAdaptorAndInstance "pModelA" ''ModelA')
table :: Table ModelAColumn ModelAColumn
table = Opaleye.table "model_a" $ pModelA (ModelA (tableColumn "uuid") (tableColumn "foreign"))
还有:
data ModelB' a b = Model { primB :: a, valB :: b }
type ModelB = ModelB' UUID String
type ModelBColumn = ModelB' (Column PGUuid) (Column PGText)
$(makeAdaptorAndInstance "pModelB" ''ModelB')
table :: Table ModelBColumn ModelBColumn
table = Opaleye.table "model_b" $ pModelB (ModelB (tableColumn "uuid") (tableColumn "val"))
正如类型所反映的那样,ModelA 可以没有 ModelB 关联。
我需要一个查询来获取一对 (ModelA, Maybe ModelB) 由 foreignA == primB 上的表之间的左连接给出。我期待它看起来像:
doJoin :: Connection -> IO [(ModelA, Maybe ModelB)]
doJoin conn = runQuery conn query
where
query :: Query (ModelAColumn, Maybe ModelBColumn)
query = leftJoin (queryTable ModelA.table) (queryTable ModelB.table) (\(ma, mb) -> foreignA ma .== primB mb)
但这不起作用。我还尝试了多种变体,特别是我替换了查询中的类型签名以明确声明右侧列的可空性:
query :: Query (ModelAColumn, (Column (Nullable PGUuid), Column (Nullable PGText))
但这失败了:
No instance for Data.Profunctor.Product.Default.Class.Default
Opaleye.Internal.Join.NullMaker ModelBColumn (Column (Nullable
PGUuid), Column (Nullable PGText).
如何在 Opaleye 中进行此查询?
正在尝试更改以下内容:
query :: Query (ModelAColumn, Maybe ModelBColumn)
至
query :: Query ((Column PGUuid) (Column (Nullable PGUuid))
, (Column (Nullable PGUuid)) (Column (Nullable PGText)))
尝试在不调用 runQuery
的情况下对其进行类型检查。一旦成功,请返回解决方案的其余部分。
您需要使用 Functional Join
,特别是 leftJoinF
。
看看这个 https://hackage.haskell.org/package/opaleye-0.5.4.0/docs/Opaleye-FunctionalJoin.html
您需要在您的两个表中提供 select 的结果作为它的第 4 个和第 5 个参数。
相当于 foreignA == primB
作为第三个参数。
对于您的第二个参数,您必须指定一些默认值,只要返回的 Maybe ModelB
是 Nothing
,就应该使用该默认值
这里有几个误解。我在下面制作了一个完整的工作版本。
首先,leftJoin
的return类型不是
Query (ModelAColumn, Maybe ModelBColumn)
你必须做
type ModelBNullableColumn = ModelB' (Column (Nullable PGUuid))
(Column (Nullable PGText))
然后使用
Query (ModelAColumn, ModelBNullableColumn)
其次,runQuery
的return类型不是
IO [(ModelA, Maybe ModelB)]
你必须做
type ModelBMaybe = ModelB' (Maybe UUID) (Maybe String)
并使用
IO [(ModelA, ModelBMaybe)]
造成这些差异的原因是 Nullable
和 Maybe
必须直接应用于 ModelBColumn
和 ModelB
中的每一列和值,而不是作为一个整体。
(还有一些奇怪的语法错误,比如
ModelA { tableColumn "uuid", tableColumn "foreign" }
这意味着你的代码没有编译的希望。我也修复了这些。)
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Opaleye hiding (table)
import qualified Opaleye
import Data.Profunctor.Product.TH
import Database.PostgreSQL.Simple hiding (Query)
import Data.UUID
data ModelA' a b = ModelA { primA :: a, foreignA :: b }
type ModelA = ModelA' UUID (Maybe UUID)
type ModelAColumn = ModelA' (Column PGUuid) (Column (Nullable PGUuid))
$(makeAdaptorAndInstance "pModelA" ''ModelA')
modelAtable :: Table ModelAColumn ModelAColumn
modelAtable = Opaleye.table "model_a" $ pModelA ModelA { primA = tableColumn "uuid", foreignA = tableColumn "foreign" }
data ModelB' a b = ModelB { primB :: a, valB :: b }
type ModelB = ModelB' UUID String
type ModelBMaybe = ModelB' (Maybe UUID) (Maybe String)
type ModelBColumn = ModelB' (Column PGUuid) (Column PGText)
type ModelBNullableColumn = ModelB' (Column (Nullable PGUuid)) (Column (Nullable PGText))
$(makeAdaptorAndInstance "pModelB" ''ModelB')
modelBtable :: Table ModelBColumn ModelBColumn
modelBtable = Opaleye.table "model_b" $ pModelB ModelB { primB = tableColumn "uuid", valB = tableColumn "val" }
doJoin :: Connection -> IO [(ModelA, ModelBMaybe)]
doJoin conn = runQuery conn query
where
query :: Query (ModelAColumn, ModelBNullableColumn)
query = leftJoin (queryTable modelAtable) (queryTable modelBtable) (\(ma, mb) -> matchNullable (pgBool False) (.== primB mb) (foreignA ma))
main :: IO ()
main = return ()
我一直在尝试在项目中使用 Opaleye 运行 左连接,但我无法编译代码。我从两个 "models" 开始,它们代表关联的表:
第一个:
data ModelA' a b = Model { primA :: a, foreignA :: b }
type ModelA = ModelA' UUID UUID
type ModelAColumn = ModelA' (Column PGUuid) (Column (Nullable PGUuid))
$(makeAdaptorAndInstance "pModelA" ''ModelA')
table :: Table ModelAColumn ModelAColumn
table = Opaleye.table "model_a" $ pModelA (ModelA (tableColumn "uuid") (tableColumn "foreign"))
还有:
data ModelB' a b = Model { primB :: a, valB :: b }
type ModelB = ModelB' UUID String
type ModelBColumn = ModelB' (Column PGUuid) (Column PGText)
$(makeAdaptorAndInstance "pModelB" ''ModelB')
table :: Table ModelBColumn ModelBColumn
table = Opaleye.table "model_b" $ pModelB (ModelB (tableColumn "uuid") (tableColumn "val"))
正如类型所反映的那样,ModelA 可以没有 ModelB 关联。
我需要一个查询来获取一对 (ModelA, Maybe ModelB) 由 foreignA == primB 上的表之间的左连接给出。我期待它看起来像:
doJoin :: Connection -> IO [(ModelA, Maybe ModelB)]
doJoin conn = runQuery conn query
where
query :: Query (ModelAColumn, Maybe ModelBColumn)
query = leftJoin (queryTable ModelA.table) (queryTable ModelB.table) (\(ma, mb) -> foreignA ma .== primB mb)
但这不起作用。我还尝试了多种变体,特别是我替换了查询中的类型签名以明确声明右侧列的可空性:
query :: Query (ModelAColumn, (Column (Nullable PGUuid), Column (Nullable PGText))
但这失败了:
No instance for Data.Profunctor.Product.Default.Class.Default Opaleye.Internal.Join.NullMaker ModelBColumn (Column (Nullable PGUuid), Column (Nullable PGText).
如何在 Opaleye 中进行此查询?
正在尝试更改以下内容:
query :: Query (ModelAColumn, Maybe ModelBColumn)
至
query :: Query ((Column PGUuid) (Column (Nullable PGUuid))
, (Column (Nullable PGUuid)) (Column (Nullable PGText)))
尝试在不调用 runQuery
的情况下对其进行类型检查。一旦成功,请返回解决方案的其余部分。
您需要使用 Functional Join
,特别是 leftJoinF
。
看看这个 https://hackage.haskell.org/package/opaleye-0.5.4.0/docs/Opaleye-FunctionalJoin.html
您需要在您的两个表中提供 select 的结果作为它的第 4 个和第 5 个参数。
相当于 foreignA == primB
作为第三个参数。
对于您的第二个参数,您必须指定一些默认值,只要返回的 Maybe ModelB
是 Nothing
这里有几个误解。我在下面制作了一个完整的工作版本。
首先,leftJoin
的return类型不是
Query (ModelAColumn, Maybe ModelBColumn)
你必须做
type ModelBNullableColumn = ModelB' (Column (Nullable PGUuid))
(Column (Nullable PGText))
然后使用
Query (ModelAColumn, ModelBNullableColumn)
其次,runQuery
的return类型不是
IO [(ModelA, Maybe ModelB)]
你必须做
type ModelBMaybe = ModelB' (Maybe UUID) (Maybe String)
并使用
IO [(ModelA, ModelBMaybe)]
造成这些差异的原因是 Nullable
和 Maybe
必须直接应用于 ModelBColumn
和 ModelB
中的每一列和值,而不是作为一个整体。
(还有一些奇怪的语法错误,比如
ModelA { tableColumn "uuid", tableColumn "foreign" }
这意味着你的代码没有编译的希望。我也修复了这些。)
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
import Opaleye hiding (table)
import qualified Opaleye
import Data.Profunctor.Product.TH
import Database.PostgreSQL.Simple hiding (Query)
import Data.UUID
data ModelA' a b = ModelA { primA :: a, foreignA :: b }
type ModelA = ModelA' UUID (Maybe UUID)
type ModelAColumn = ModelA' (Column PGUuid) (Column (Nullable PGUuid))
$(makeAdaptorAndInstance "pModelA" ''ModelA')
modelAtable :: Table ModelAColumn ModelAColumn
modelAtable = Opaleye.table "model_a" $ pModelA ModelA { primA = tableColumn "uuid", foreignA = tableColumn "foreign" }
data ModelB' a b = ModelB { primB :: a, valB :: b }
type ModelB = ModelB' UUID String
type ModelBMaybe = ModelB' (Maybe UUID) (Maybe String)
type ModelBColumn = ModelB' (Column PGUuid) (Column PGText)
type ModelBNullableColumn = ModelB' (Column (Nullable PGUuid)) (Column (Nullable PGText))
$(makeAdaptorAndInstance "pModelB" ''ModelB')
modelBtable :: Table ModelBColumn ModelBColumn
modelBtable = Opaleye.table "model_b" $ pModelB ModelB { primB = tableColumn "uuid", valB = tableColumn "val" }
doJoin :: Connection -> IO [(ModelA, ModelBMaybe)]
doJoin conn = runQuery conn query
where
query :: Query (ModelAColumn, ModelBNullableColumn)
query = leftJoin (queryTable modelAtable) (queryTable modelBtable) (\(ma, mb) -> matchNullable (pgBool False) (.== primB mb) (foreignA ma))
main :: IO ()
main = return ()