{-# LANGUAGE CPP #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} import Control.Monad.Base import Control.Monad.Logger import Control.Monad.Trans.Resource import Data.ByteString (ByteString) import Data.Conduit import qualified Data.Conduit.List as CL import Data.Maybe import Data.Text (Text) import Data.Time import Database.Persist import Database.Persist.MySQL import Database.Persist.Relational #if MIN_VERSION_relational_query(0, 10, 0) import Database.Relational as HRR hiding (fromMaybe, ($$)) #else import Database.Relational.Query as HRR hiding (fromMaybe) #endif import System.Environment import qualified Image import qualified ImageTag import Model import qualified Tag import Types selectImageByTagNameList :: Bool -- ^ match any -> [Text] -- ^ list of tag name -> Relation () Image.Image selectImageByTagNameList matchAny tagNames = relation $ do img <- query Image.image imgids <- query $ imageIdFromTagNameList matchAny tagNames on $ img ! Image.id' .=. imgids return img -- ^ query ImageId by tag name list -- -- @ -- SELECT image_id FROM image_tag -- INNER JOIN tag ON tag.id = image_tag.tag_id -- WHERE tag.name IN (<>) -- GROUP BY image_tag.image_id -- HAVING COUNT(image_tag.image_id) = <> -- @ imageIdFromTagNameList :: Bool -- ^ match any -> [Text] -- ^ list of tag name -> Relation () ImageId imageIdFromTagNameList matchAny tagNames = aggregateRelation $ do imgtag <- query $ ImageTag.imageTag tag <- query $ Tag.tag on $ tag ! Tag.id' .=. imgtag ! ImageTag.tagId' wheres $ tag ! Tag.name' `in'` values tagNames g <- groupBy $ imgtag ! ImageTag.imageId' let c = HRR.count $ imgtag ! ImageTag.imageId' having $ if matchAny then c .>. value (0 :: Int) else c .=. value (fromIntegral . length $ tagNames) return g tagListOfImage :: Relation ImageId Tag.Tag tagListOfImage = relation' $ placeholder $ \ph -> do tag <- query Tag.tag imgtag <- query ImageTag.imageTag on $ tag ! Tag.id' .=. imgtag ! ImageTag.tagId' wheres $ imgtag ! ImageTag.imageId' .=. ph return tag addImages :: TagId -> [Image] -> SqlPersistT (LoggingT IO) () addImages tagId images = do imgIds <- mapM insert images mapM_ (\imgId -> insert $ ImageTag imgId tagId) imgIds printImage :: ByteString -> SqlPersistT (LoggingT IO) () printImage hkey = getBy (UniqueImageHash hkey) >>= \case Just (Entity k val) -> do liftBase $ print val runResourceT $ runQuery (relationalQuery tagListOfImage) k $$ CL.mapM_ (liftBase . print) Nothing -> liftBase $ putStrLn "Image not found" sample :: SqlPersistT (LoggingT IO) () sample = do runMigration migrateAll now <- liftBase getCurrentTime tagShinkuId <- insert $ Tag "shinku" (Just "二階堂真紅") addImages tagShinkuId [ Image "4f4221435f9c5c430db2b093c91b8f1f" PNG now now , Image "11eb1ee2b8f9b471f15d85fb784a8fd6" PNG now now , Image "7e11b84e04f181179cde72a5d0a5731f" PNG now now , Image "e10f6af40a80a7f5794ea0bdc66d4ae3" PNG now now ] tagMareId <- insert $ Tag "mare" Nothing addImages tagMareId [ Image "fbc717314b90afe6819d4593c583109a" PNG now now , Image "dc593d1c551f2a1ea85c2dc5521c7fdf" PNG now now ] runResourceT $ runQuery (relationalQuery $ selectImageByTagNameList False ["shinku"]) () $$ CL.mapM_ (liftBase . print) runResourceT $ runQuery (relationalQuery $ imageIdFromTagNameList True ["shinku", "mare"]) () $$ CL.mapM_ (liftBase . print) printImage "11eb1ee2b8f9b471f15d85fb784a8fd6" printImage "fbc717314b90afe6819d4593c583109a" mapM_ (liftBase . print) =<< run run :: SqlPersistT (LoggingT IO) [Entity Image] run = runResourceT $ runQuery (relationalQuery $ selectImageByTagNameList False ["shinku", "mare"]) () $$ CL.consume getConnectInfo :: IO ConnectInfo getConnectInfo = do host <- fromMaybe "localhost" `fmap` lookupEnv "MYSQL_HOST" user <- fromMaybe "travis" `fmap` lookupEnv "MYSQL_USER" pass <- fromMaybe "" `fmap`lookupEnv "MYSQL_PASS" return defaultConnectInfo { connectHost = host , connectUser = user , connectPassword = pass , connectDatabase = "test" } main :: IO () main = do connInfo <- getConnectInfo runStderrLoggingT $ withMySQLPool connInfo 10 $ runSqlPool sample