matt
matt

Reputation: 2049

Haskell: convert String to Field for mongoDB

What is the best way to convert a string "[\"id\"=:5977, \"brandId\"=:87]" to a field for mongoDB ["id"=:5977, "brandId"=:87]

or even better if I have a datatype

data Body = Body
 { body_id :: int
   brandId :: Int } deriving (Show)

giving

Body {body_id = 5977, brandId = 87}

What is the best way to convert that to

["id"=:5977, "brandId"=:87]

Thanks.

Upvotes: 0

Views: 284

Answers (1)

epsilonhalbe
epsilonhalbe

Reputation: 15959

If I understand this correctly you are using hackage: mongoDB to interact with a database.

So the only thing to do is convert a haskell value to a BSON-value this is what hackage: bson provides, notably in the typeclass Val

class (Typeable a, Show a, Eq a) => Val a where

Haskell types of this class correspond to BSON value types

Minimal complete definition
val, cast'

Methods
val :: a -> Value 
...
cast' :: Value -> Maybe a 
...

so it remains to implement val and cast'

but first of all we need to derive a few more properties than you have:

Body.hs

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
module Body where

import Data.Bson
import Data.Typeable

data Body = Body { bodyID :: Int
                 , brandID :: Int }
                 deriving (Show,Typeable,Eq)


instance Val Body where
  val (Body body brand) = Doc [ "bodyID"  := (Int64 $ fromIntegral body)
                              , "brandID" := (Int64 $ fromIntegral brand)]

  cast' (Doc bson) = case (bson !? "bodyID") of
                          Just (Int64 x) ->
                               case (bson !? "brandID") of
                                         Just (Int64 y) -> Just (Body (fromIntegral x)
                                                                      (fromIntegral y))
                                         Just _  -> Nothing
                                         Nothing -> Nothing
                          Just _ -> Nothing
                          Nothing -> Nothing
  cast' (Array x) = undefined
  cast' _ = Nothing

this is a bit clustered but rather straightforward.


Now I gave this thing a bit more thought - the previous cast' is really clustered and haskell having nice abstractions should do better

  val (Body body brand) = Doc [ "bodyID"  := (val body)
                              , "brandID" := (val brand)]
  cast' (Doc bson) = do body  <- (bson !? "bodyID")
                        brand <- (bson !? "brandID")
                        Body <$> cast' body <*> cast' brand

  cast' _ = Nothing

So you really could write this a bit shorter accepting that <$> (called fmap) and <*> (called ap do the "right" thing.

Moreover I used do notation to simplify the job, now using this

> cast' $ val (Body 30 4) :: Maybe Body
Just (Body {bodyID = 30, brandID = 4})

Note in the last example you really need the annotation :: Maybe Body otherwise the compiler cannot figure out what to cast and you'll get a Nothing.

Upvotes: 4

Related Questions