144
votes

I have data types defined as:

data ComitteeView = CommitteeView { committeeId :: CommitteeId
                                  , committeeMembers :: [Person] 
                                  }

data CommitteesView = CommitteesView { committeeView :: [CommitteeView] }

Now, as it stands, I have a Persistent model defined as:

Person
  name  Text

Committee
  name  Text

CommitteePerson
  personId    PersonId
  committeeId CommitteeId

I can pretty easily create a query to populate a CommitteeView, using Esqueleto. It would go something like this:

getCommitteeView cid = 
  CommitteeView <$> runDB $ 
    select $
      from (person `InnerJoin` pxc `InnerJoin` committee) -> do
        on  (committee ^. CommitteeId ==. pxc ^. CommitteePersonCommitteeId)
        on  (person ^. PersonId       ==. pxc ^. CommitteePersonPersonId)
        where_ (committee ^. CommitteePersonCommitteeId ==. val cid)
        return person

Now, consider the problem of populating CommitteesView. In principle, we get enough data to populate by running subquery in the above query. Okay, fair enough. Now how can I use "group by Haskell-list" like group by in SQL? How can I fold rows so that I can end up with a list of lists of people?

I get the impression that esqueleto can't handle the case as such (i.e., it doesn't have a combinator that would do it). And my underlying database obviously doesn't support Haskell lists as a column. But, surely, I can't be the only person to face this issue. What is an effective strategy? Folding an n-list of lists into a n-list? Or running n+1 queries? Are there any other options?

1
Have you looked at Data.List.groupBy?cdk
@cdk: Yeah, that's what I've been going with. It gets surprisingly hairy, though.nomen

1 Answers

2
votes

Esqueleto is NOT meant to handle list of sublists (multidimensional list) out of the box yet! Data.List.groupBy that 'cdk' advised to you can group only list itself, but not what you was asking for.

For your case I would insistently advise you to use a classic SQL queries. You can run n+1 queries, but do that only if it is rare and not often usable function, that for example prepares cached data (based on your variables names I suppose that it may not be heavy used and it worth a try). For heavy usage you should consider using classic SQL without any doubt.

If you will go to https://github.com/prowdsponsor/esqueleto you will find that:

Not all SQL features are available, but most of them can be easily added (especially functions).

so you can try to ask for a new feature. Good luck!