Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell Persistent Joins with Esqueleto

Tags:

sql

haskell

I've been looking into the Persistent library to interface with sql databases. Suppose I have a database containing recipes, with Recipe, Ingredient, and RecIng tables.

My (admittedly limited) understanding of persistent leads me to believe I should define the tables like this:

share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase|
Recipe 
    title String
Ingredient 
    name String
RecIng
    recId RecipeId
    ingId IngredientId
    quantity Int
|]

With this, it is possible to use Esqueleto to get the inner join between these tables:

select $
from $ \(i `InnerJoin ` ri `InnerJoin` r) -> do
    on (r ^. RecipeId ==. ri ^. RecIngIngId)
    on (i ^. IngredientId ==. ri ^. RegIngRecId)
    return (r, ri, i)

This returns tuple of (Recipe, RecIng, Ingredient).

What I really want is a way to query recipes that results in the following:

data Recipe = Recipe { title :: String
                     , ingredients :: [Ingredient]
                     }

data Ingredient = Ingredient { name :: String
                             , quantity :: Integer
                             }

Other than defining an additional set of data types, and converting the tuples, is there a best practice for doing this kind of thing?

like image 491
oneway Avatar asked Feb 10 '14 19:02

oneway


1 Answers

+1 to Adam's comment, it's the right answer IMO.

A separate approach you could take would be to use embedded entities, which would essentially mean JSON-encoding the list of ingredients into each recipe. But this would be bad SQL design, would cause table locking issues for updates, and wouldn't scale well for large numbers of ingredients.

In other words, there's a mismatch between the Haskell representation you want to work with and the right way to store the data in the database. This doesn't mean there's a problem with either your database format or your Haskell datatypes: it's a logical difference. The right response to this gap is to have two datatypes and an intelligent way to convert between them.

like image 78
Michael Snoyman Avatar answered Oct 16 '22 17:10

Michael Snoyman