Search code examples
ihp

Selecting some of One where there're none of Many


I'm trying to find domains that don't have any usernames with a given name, that is free domains. Hopefully there's a solution to this that doesn't require 2 separate queries, but I also ran into a problem with type system.

-- error Couldn't match type 'IO [Domain]' with '[[Domain]]', points to beginning of filter

            Just term -> do
                usernames <- query @Username
                    |> filterWhere (#name, term) |> fetch >>= collectionFetchRelated #domainId
                domains <- search |> filter \x->case find (\y->get #domainId x == get #domainId y) usernames of
                    Just _ -> True
                    Nothing -> False
                render IndexView {..}

Solution

  • The error -- error Couldn't match type 'IO [Domain]' with '[[Domain]]', points to beginning of filter is happening because of the arrow notation here:

                    domains <- search |> filter \x->case find (\y->get #domainId x == get #domainId y) usernames of
                        Just _ -> True
                        Nothing -> False
    

    As this is not doing any IO, it needs to use a let:

                    let domains = search |> filter \x->case find (\y->get #domainId x == get #domainId y) usernames of
                        Just _ -> True
                        Nothing -> False
    

    Now the type of domains is [[Domain]] (a list of list of domains). Likely we want [Domain] (just a normal list of domains).

    We can use concat for that purpose:

                    let domains = search
                            |> filter ( \x-> case find (\y->get #domainId x == get #domainId y) usernames of
                                    Just _ -> True
                                     Nothing -> False
                                )
                            |> concat
    

    Now the type error should be gone.


    Hopefully there's a solution to this that doesn't require 2 separate queries

    Yes this is doable as well with a handwritten query:

    domains <- sqlQuery "SELECT domains.* FROM domains WHERE (SELECT COUNT(*) FROM usernames WHERE usernames.domain_id = domains.id AND usernames.name = ?) = 0" [term]