Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusing existing row decoder on aliased queries #332

Open
aveltras opened this issue May 19, 2022 · 1 comment
Open

Reusing existing row decoder on aliased queries #332

aveltras opened this issue May 19, 2022 · 1 comment

Comments

@aveltras
Copy link

Hi,

I'm trying to factorize some of my row decoding code and I'm wondering if something like the following is possible.

The following currently doesn't work because of overlapping instances (and maybe further errors down the road).

decodeUser ::
  forall pfx.
  ( KnownSymbol (AppendSymbol pfx "uid"),
    KnownSymbol (AppendSymbol pfx "email"),
    KnownSymbol (AppendSymbol pfx "fullname"),
    KnownSymbol (AppendSymbol pfx "position"),
    KnownSymbol (AppendSymbol pfx "location")
  ) =>
  DecodeRow
    '[ (AppendSymbol pfx "uid") ::: 'NotNull 'PGuuid,
       (AppendSymbol pfx "email") ::: 'NotNull 'PGtext,
       (AppendSymbol pfx "fullname") ::: 'NotNull 'PGtext,
       (AppendSymbol pfx "position") ::: 'Null 'PGtext,
       (AppendSymbol pfx "location") ::: 'Null 'PGtext
     ]
    User
decodeUser = do
  userId :: UserId <- fromLabel @(AppendSymbol pfx "uid")
  email <- unsafeAddress <$> fromLabel @(AppendSymbol pfx "email")
  fullName :: FullName <- fromLabel @(AppendSymbol pfx "fullname")
  position <- fromLabel @(AppendSymbol pfx "position")
  location <- fromLabel @(AppendSymbol pfx "location")
  pure $ User userId email $ PersonalInfo fullName (fromMaybe "" position) (fromMaybe "" location)

With the following schema

type UserTable =
  '[ "user_pk" ::: 'PrimaryKey '["uid"],
     "user_uk_email" ::: 'Unique '["email"],
     "user_fk_picture" ::: 'ForeignKey '["picture"] "public" "file" '["uid"]
   ]
    :=> '[ "uid" ::: 'NoDef :=> 'NotNull 'PGuuid,
           "email" ::: 'NoDef :=> 'NotNull 'PGtext,
           "created_at" ::: 'NoDef :=> 'NotNull 'PGtimestamptz,
           "updated_at" ::: 'NoDef :=> 'NotNull 'PGtimestamptz,
           "fullname" ::: 'NoDef :=> 'NotNull 'PGtext,
           "picture" ::: 'NoDef :=> 'Null 'PGuuid,
           "position" ::: 'NoDef :=> 'Null 'PGtext,
           "location" ::: 'NoDef :=> 'Null 'PGtext
         ]

And the following type

data User = User
  { id :: UserId,
    email :: EmailAddress,
    info :: PersonalInfo
  }

The intended use case is to be able to decode two different users for the same row, one would have all its columns prefixed by otheruser_ for example and the other one would have no prefix or "" as prefix.

In the decoder for the final type, I would then be able to decode both users as follow:

decoder = do
  userA <- decoderUser @""
  userB <- decoderUser @"otheruser_"
  pure $ MyFinalType userA userB
@echatav echatav added question and removed question labels May 19, 2022
@echatav
Copy link
Contributor

echatav commented May 19, 2022

Hmmm, that looks tricky and a little complex with the use of AppendSymbol, but perhaps worth a try. I might instead use the row (or even rowStar) function to store the two users as exactly two columns, depending on how the query looked, so you don't have to worry about duplicated field names. And then decode those columns with a FromPG User instance defined with rowValue.

instance IsPG User where
  type PG User = 'PGcomposite (TableToRow UserTable)
instance FromPG User where
  fromPG = rowValue $ do
    userId <- #uid
    email <- unsafeAddress <$> #email
    fullName <- #fullname
    position <- #position
    location <- #location
    pure $ User userId email $ PersonalInfo fullName (fromMaybe "" position) (fromMaybe "" location)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants