I have been trying to run a left join using Opaleye in a project but I'm not being able to make the code compile. I start with two "models" which represent tables that are associated:
First:
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"))
And also:
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"))
As the types reflect, ModelA can have no ModelB associated.
I need a query to obtain pairs of (ModelA, Maybe ModelB) given by the left join between the tables on foreignA == primB. I was expecting it to look like:
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)
But this does not work. I've also tried multiple variants, in particular I replaced the type signature in query to explicitly state the nullability of the columns at the right:
query :: Query (ModelAColumn, (Column (Nullable PGUuid), Column (Nullable PGText))
But this fails with:
No instance for Data.Profunctor.Product.Default.Class.Default Opaleye.Internal.Join.NullMaker ModelBColumn (Column (Nullable PGUuid), Column (Nullable PGText).
How can I make this query in Opaleye?
There are a couple of misunderstandings here. I produced a full working version below.
Firstly, the return type of the
leftJoin
is notYou have to do
and then use
Secondly, the return type of the
runQuery
is notYou have to do
and use
The reason for these differences is that
Nullable
andMaybe
must be applied directly to every column and value in theModelBColumn
andModelB
not to the values as a whole.(There are also some weird syntax errors like
which means your code has no hope of compiling. I fixed those, too.)