{"id":300,"date":"2016-12-14T20:58:34","date_gmt":"2016-12-14T11:58:34","guid":{"rendered":"http:\/\/mitsuji.org\/?p=300"},"modified":"2020-04-18T15:34:56","modified_gmt":"2020-04-18T06:34:56","slug":"haskellwai-%e3%81%ab%e3%82%88%e3%82%8b-web%e3%82%a2%e3%83%97%e3%83%aa%e3%82%b1%e3%83%bc%e3%82%b7%e3%83%a7%e3%83%b3%e9%96%8b%e7%99%ba%e3%81%ae%e5%ae%9f%e9%9a%9b%ef%bc%88db%e7%b7%a8%ef%bc%89","status":"publish","type":"post","link":"https:\/\/mitsuji.org\/?p=300","title":{"rendered":"Haskell(wai) \u306b\u3088\u308b Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u306e\u5b9f\u969b\uff08DB\u7de8\uff09"},"content":{"rendered":"<p>\u3053\u306e\u8a18\u4e8b\u306f\u3001<a href=\"http:\/\/qiita.com\/advent-calendar\/2016\/stk\" target=\"_blank\" rel=\"noopener noreferrer\">\u30b9\u30bf\u30fc\u30c8\u30c8\u30a5\u30c7\u30a4\u5de5\u52d9\u5e97 Advent Calendar<\/a> 14\u65e5\u76ee\u306e\u8a18\u4e8b\u3067\u3059\u3002<\/p>\n<p><a href=\"https:\/\/hackage.haskell.org\/package\/wai\" target=\"_blank\" rel=\"noopener noreferrer\">wai<\/a>\uff08Haskell\u88fd\u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u898f\u683c\uff09\u306b\u6e96\u62e0\u3057\u3001<a href=\"https:\/\/hackage.haskell.org\/package\/warp\" target=\"_blank\" rel=\"noopener noreferrer\">warp<\/a>\uff08wai\u6e96\u62e0\u306eHaskell\u88fdWeb\u30b5\u30fc\u30d0\uff09\u4e0a\u3067\u52d5\u4f5c\u3059\u308bWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u958b\u767a\u306b\u3064\u3044\u3066\u7d39\u4ecb\u3059\u308b\u3002\u57fa\u672c\u6027\u80fd\u3092\u969b\u7acb\u305f\u305b\u308b\u305f\u3081\u3001\u4fbf\u5229\u306a\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u306f\u3042\u3048\u3066\u4f7f\u7528\u3057\u306a\u3044\u3002<\/p>\n<p>\u4ee5\u524d\u306b<a href=\"http:\/\/mitsuji.org\/?p=17\" target=\"_blank\" rel=\"noopener noreferrer\">\u975e\u5e38\u306b\u5358\u7d14\u306a\u30b5\u30f3\u30d7\u30eb<\/a>\u3092\u7d39\u4ecb\u3057\u305f\u8a18\u4e8b\u306e\u7d9a\u7de8\u3068\u306a\u308b\u3002<br \/>\n\u4eca\u56de\u306f\u3001\u672c\u683c\u7684\u306aWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306b\u6b20\u304b\u305b\u306a\u3044\u3001\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3068\u306e\u9023\u643a\u3092\u8a66\u3057\u3066\u307f\u305f\u3002<br \/>\n\u30d0\u30c3\u30af\u30a8\u30f3\u30c9\u306b\u306fMySQL\u3092\u4f7f\u7528\u3057\u305f\u3002<\/p>\n<p>\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9\u306f <a href=\"https:\/\/github.com\/mitsuji\/wai-example-mysql\" target=\"_brank\" rel=\"noopener noreferrer\">https:\/\/github.com\/mitsuji\/wai-example-mysql<\/a> \u306b\u3042\u308b\u3002<\/p>\n<h2>1. \u30b5\u30f3\u30d7\u30eb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u6982\u8981<\/h2>\n<p>\u4e0b\u8a18\u306e\u3088\u3046\u306a\u30c7\u30fc\u30bf\u3092\u6271\u3046JSON-API\u3092\u5b9f\u88c5\u3057\u305f\u3002<br \/>\n1\u4ef6\u306e\u30b3\u30fc\u30c7\u30a3\u30cd\u30fc\u30c8\u306b\u5bfe\u3057\u3066\u3001\u30b8\u30e3\u30f3\u30eb\u304c1\u3064\u3001\u30bf\u30b0\u304c\u8907\u6570(0..N)\u7d10\u3065\u304f\u3082\u306e\u3068\u3059\u308b\u3002<br \/>\n<a href=\"http:\/\/mitsuji.org\/wp-content\/uploads\/2016\/12\/class-diagram.png\" target=\"_brank\" rel=\"noopener noreferrer\"><img src=\"\/wp-content\/uploads\/2016\/12\/class-diagram.png\"><\/a><\/p>\n<p>\u30aa\u30d6\u30b8\u30a7\u30af\u30c8\u56f3\u306f\u4e0b\u8a18\u306e\u3088\u3046\u306b\u306a\u308b\u3060\u308d\u3046\u3002<br \/>\n<a href=\"http:\/\/mitsuji.org\/wp-content\/uploads\/2016\/12\/object-diagram.png\" target=\"_brank\" rel=\"noopener noreferrer\"><img src=\"\/wp-content\/uploads\/2016\/12\/object-diagram.png\"><\/a><\/p>\n<p>\u307e\u305f\u3001MySQL\u306eCREATE\u6587\u306f\u4e0b\u8a18\u306e\u3088\u3046\u306b\u306a\u308b\u3002<\/p>\n<pre>CREATE TABLE genre (\n        genre_id INT NOT NULL AUTO_INCREMENT\n       ,genre_title nvarchar(31) NOT NULL\n       ,PRIMARY KEY (genre_id)\n);\n\n\nCREATE TABLE tag (\n        tag_id INT NOT NULL AUTO_INCREMENT\n       ,tag_title nvarchar(31) NOT NULL\n       ,PRIMARY KEY (tag_id)\n);\n\n\nCREATE TABLE look (\n        look_id INT NOT NULL AUTO_INCREMENT\n       ,look_create_dt DATETIME NOT NULL\n       ,look_update_dt DATETIME NOT NULL\n       ,look_title nvarchar(31) NOT NULL\n       ,look_description nvarchar(400) NOT NULL\n       ,look_genre_id INT NOT NULL\n       ,PRIMARY KEY (look_id)\n       ,FOREIGN KEY (look_genre_id) REFERENCES genre (genre_id)\n);\n\nCREATE TABLE look_has_tag (\n        lht_look_id INT NOT NULL\n       ,lht_tag_id INT NOT NULL\n       ,PRIMARY KEY (lht_look_id,lht_tag_id)\n       ,FOREIGN KEY (lht_look_id) REFERENCES look (look_id)\n       ,FOREIGN KEY (lht_tag_id) REFERENCES tag (tag_id)\n);\n<\/pre>\n<p>Haskell\u306e\u30c7\u30fc\u30bf\u5b9a\u7fa9\u306f\u4e0b\u8a18\u306e\u3088\u3046\u306b\u306a\u3063\u305f\u3002<\/p>\n<pre>data Genre = Genre GenreId Title [Look]\n           deriving (Eq)\n\ndata Tag = Tag TagId Title [Look]\n         deriving (Eq)\n\ndata Look = Look { lookId :: LookId\n                 , lookCreateDt :: CreateDT\n                 , lookUpdateDt :: UpdateDT\n                 , lookTitle :: Title\n                 , lookDescription :: Description\n                 , lookGenre :: Genre'\n                 , lookTags :: [Tag']\n                 }\n          deriving (Eq)\n\n\nnewtype Genre' = Genre' Genre\n               deriving (Eq)\n                        \nnewtype Tag' = Tag' Tag\n               deriving (Eq)\n\n<\/pre>\n<h2>2. \u5b9f\u88c5\u306e\u65b9\u91dd<\/h2>\n<p>\u4e0b\u8a18\u306e\u65b9\u91dd\u3067\u5b9f\u88c5\u3057\u305f\u3002<br \/>\n* <a href=\"https:\/\/hackage.haskell.org\/package\/mysql-simple\" target=\"_brank\" rel=\"noopener noreferrer\">mysql-simple<\/a>\u3092\u4f7f\u7528\u3057\u3066MySQL\u306e\u30c7\u30fc\u30bf\u3092Haskell\u306e\u30c7\u30fc\u30bf\u306b\u5909\u63db\u3059\u308b\u3002<br \/>\n* <a href=\"https:\/\/hackage.haskell.org\/package\/aeson\" target=\"_brank\" rel=\"noopener noreferrer\">aeson<\/a>\u3092\u4f7f\u7528\u3057\u3066Haskell\u306e\u30c7\u30fc\u30bf\u3092JSON\u306b\u5909\u63db\u3059\u308b\u3002<br \/>\n* <a href=\"https:\/\/hackage.haskell.org\/package\/wai\" target=\"_brank\" rel=\"noopener noreferrer\">wai<\/a>\u3092\u4f7f\u7528\u3057\u3066Web\u306e\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9\u3092\u63d0\u4f9b\u3059\u308b\u3002<\/p>\n<p>\u307e\u305f\u3001\u5b9f\u7528\u7684\u306a\u30b5\u30f3\u30d7\u30eb\u3068\u3059\u308b\u305f\u3081\u3001\u4e0b\u8a18\u3092\u76db\u308a\u3053\u3093\u3060\u3002<br \/>\n* \u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u51e6\u7406<br \/>\n* \u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u30d7\u30fc\u30eb<br \/>\n* N\u5bfeN\u306e\u30ea\u30ec\u30fc\u30b7\u30e7\u30f3<\/p>\n<h2>3. MySQL\u306e\u30c7\u30fc\u30bf\u3092Haskell\u306e\u30c7\u30fc\u30bf\u306b\u5909\u63db\u3059\u308b\u3002<\/h2>\n<p>mysql-simple \u3067\u306f QueryResult \u3068\u3044\u3046\u578b\u30af\u30e9\u30b9\u304c\u7528\u610f\u3055\u308c\u3066\u304a\u308a\u3001<br \/>\n\u4efb\u610f\u306e\u30c7\u30fc\u30bf\u578b\u3092\u3053\u306e\u578b\u30af\u30e9\u30b9\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u3059\u308b\u3053\u3068\u3067\u3001<br \/>\nMySQL\u306b\u6295\u3052\u305f\u30af\u30a8\u30ea\u306e\u7d50\u679c\u3092Haskell\u306e\u30c7\u30fc\u30bf\u306b\u5909\u63db\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u308b\u3002<br \/>\n\u4e0b\u8a18\u306e\u3088\u3046\u306b convertResults \u3092\u5b9f\u88c5\u3059\u308c\u3070\u3088\u3044\u3002<\/p>\n<pre>instance QueryResults Genre' where\n  convertResults [fa,fb] [va,vb] = Genre' $ Genre a b []\n        where !a = convert fa va\n              !b = convert fb vb\n  convertResults fs vs  = convertError fs vs 2\n<\/pre>\n<p>\u4e0a\u8a18\u3092\u5b9a\u7fa9\u3059\u308b\u3068\u3001\u4e0b\u8a18\u306e\u3088\u3046\u306b\u30af\u30a8\u30ea\u3092\u6295\u3052\u308b\u3053\u3068\u304c\u3067\u304d\u308b\u3002<br \/>\n\u7d50\u679c\u306f\u3001\u95a2\u6570\u306e\u578b\u304b\u3089\u3082\u5206\u304b\u308b\u3088\u3046\u306b Genre&#8217; \u306e\u30ea\u30b9\u30c8\u3068\u3057\u3066\u53d6\u308a\u51fa\u3059\u3053\u3068\u304c\u3067\u304d\u308b\u3002<\/p>\n<pre>select_genre :: Connection -&gt; IO [Genre']\nselect_genre conn =\n  query_ conn [r|\n    SELECT\n       genre_id\n      ,genre_title\n    FROM genre\n    ORDER BY genre_id\n  |]\n<\/pre>\n<h2>4. Haskell\u306e\u30c7\u30fc\u30bf\u3092JSON\u306b\u5909\u63db\u3059\u308b\u3002<\/h2>\n<p>aeson \u3067\u306f ToJSON \u3068\u3044\u3046\u578b\u30af\u30e9\u30b9\u304c\u7528\u610f\u3055\u308c\u3066\u304a\u308a\u3001<br \/>\n\u4efb\u610f\u306e\u30c7\u30fc\u30bf\u578b\u3092\u3053\u306e\u578b\u30af\u30e9\u30b9\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u3059\u308b\u3053\u3068\u3067\u3001<br \/>\nJSON\u3078\u306e\u5909\u63db\u3092\u5b9a\u7fa9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u308b\u3002<br \/>\n\u4e0b\u8a18\u306e\u3088\u3046\u306b toJSON \u3092\u5b9f\u88c5\u3059\u308c\u3070\u3088\u3044\u3002<\/p>\n<pre>instance ToJSON Tag where\n  toJSON (Tag id t ls) =\n    object [\"id\" .= id\n           ,\"title\" .= t\n           ,\"looks\" .= ls\n           ]\n<\/pre>\n<p>\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306bREPL\u3067\u5165\u308b\u3068\u3001<br \/>\n\u4e0b\u8a18\u306e\u3088\u3046\u306baeson\u306e\u52d5\u4f5c\u3092\u8a66\u3059\u3053\u3068\u304c\u3067\u304d\u308b\u3002<\/p>\n<pre>*Main Data JSON MySQL&gt; AE.encode $ Tag 100 \"\u30bf\u30b0100\" []\n\"{\\\"looks\\\":[],\\\"id\\\":100,\\\"title\\\":\\\"\\227\\130\\191\\227\\130\\176\\&amp;100\\\"}\"\n<\/pre>\n<h2>5. \u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u30d7\u30fc\u30eb<\/h2>\n<p>\u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u30d7\u30fc\u30eb\u306f<a href=\"https:\/\/hackage.haskell.org\/package\/resource-pool\" target=\"_blank\" rel=\"noopener noreferrer\">resouce-pool<\/a>\u3068\u3044\u3046\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u4f7f\u7528\u3057\u3066\u5b9f\u88c5\u3057\u305f\u3002<br \/>\n\u3053\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u4f7f\u3046\u3068\u3001DB\u306e\u63a5\u7d9a\u306b\u9650\u3089\u305a\u4efb\u610f\u306e\u30ea\u30bd\u30fc\u30b9\u306e\u30d7\u30fc\u30eb\u3092\u7c21\u5358\u306b\u5b9f\u88c5\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u308b\u3088\u3046\u3060\u3002<\/p>\n<p>\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u8d77\u52d5\u6642\u306b createPool \u3067\u30d7\u30fc\u30eb\u3092\u4f5c\u3063\u3066\u304a\u304f\u3002<br \/>\ncreatePool \u306b\u306f\u30d7\u30fc\u30eb\u306b\u30ea\u30bd\u30fc\u30b9\u304c\u78ba\u4fdd\u3055\u308c\u308b\u3068\u304d\u306e\u51e6\u7406\u3001\u30d7\u30fc\u30eb\u304b\u3089\u30ea\u30bd\u30fc\u30b9\u304c\u958b\u653e\u3055\u308c\u308b\u3068\u304d\u306e\u51e6\u7406\u3068\u3001\u30d7\u30fc\u30eb\u306e\u30c1\u30e5\u30fc\u30cb\u30f3\u30b0\u306b\u95a2\u3059\u308b\u3044\u304f\u3064\u304b\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u3092\u8a2d\u5b9a\u3059\u308b\u3002<\/p>\n<pre>main :: IO ()\nmain = do\n  host:port:_ &lt;- getArgs\n  cp &lt;- createPool connect close 10 10 10\n  Warp.runSettings (\n    Warp.setHost (fromString host) $\n    Warp.setPort (read port) $\n    Warp.defaultSettings\n    ) $ routerApp cp\n  where\n    connect :: IO MySQL.Connection\n    connect = MySQL.connect MySQL.defaultConnectInfo {\n       MySQL.connectHost = \"localhost\"\n      ,MySQL.connectUser = \"wai_exam_admin\"\n      ,MySQL.connectPassword = \"abcd1234\"\n      ,MySQL.connectDatabase = \"wai_exam\"\n      }\n    close = MySQL.close\n<\/pre>\n<p>\u30ea\u30bd\u30fc\u30b9\u30d7\u30fc\u30eb\u306e\u5909\u6570\u3092\u5f15\u304d\u6e21\u3057\u3066\u304a\u3051\u3070\u3001\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u4efb\u610f\u306e\u7b87\u6240\u3067<br \/>\nwithResource \u3092\u4f7f\u7528\u3057\u3066\u3001\u30d7\u30fc\u30eb\u5185\u306e\u30ea\u30bd\u30fc\u30b9\u3092\u4f7f\u3046\u3053\u3068\u304c\u3067\u304d\u308b\u3002<br \/>\n\u95a2\u6570\u306e\u547c\u3073\u51fa\u3057\u304c\u6df1\u304f\u306a\u308b\u5834\u5408\u306f\u3001\u30ea\u30bd\u30fc\u30b9\u30d7\u30fc\u30eb\u306e\u5171\u6709\u306b<br \/>\nReader\u30e2\u30ca\u30c9\u306a\u3069\u3092\u4f7f\u3046\u3068\u3088\u3044\u3060\u308d\u3046\u3002<\/p>\n<pre>  -- GET \/v1\/genre\n  (Right M.GET, [_,_]) -&gt; do\n    bs &lt;- AE.encode &lt;$&gt; (withResource cp $ \\conn -&gt; select_genre conn)\n    respond $ response200 bs\n<\/pre>\n<h2>6. \u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u51e6\u7406<\/h2>\n<p>\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u51e6\u7406\u306f\u4e0b\u8a18\u306e\u3088\u3046\u306b withTransaction \u3092\u4f7f\u3046\u3060\u3051\u3067\u5b9f\u73fe\u3055\u308c\u308b\u3088\u3046\u3060\u3002<\/p>\n<pre>delete_look :: Connection -&gt; LookId -&gt; IO ()\ndelete_look conn id =\n  withTransaction conn $ do\n    execute conn\n      \"DELETE FROM look_has_tag WHERE lht_look_id = ?\" (Only id)\n    execute conn\n      \"DELETE FROM look WHERE look_id = ?\" (Only id)\n    return ()\n<\/pre>\n<h2>7. \u30eb\u30fc\u30c6\u30a3\u30f3\u30b0<\/h2>\n<p>URL\u306e\u30d1\u30b9\u3084HTTP\u30e1\u30bd\u30c3\u30c9\u306e\u7a2e\u985e\u3068\u3001\u8a55\u4fa1\u3055\u308c\u308b\u95a2\u6570\u3092\u3069\u306e\u3088\u3046\u306b\u7d10\u3065\u3051\u308b\u304b\u3002<br \/>\nwai \u304c Text\u578b\u306e\u30ea\u30b9\u30c8\u3068\u3057\u3066URL\u306e\u30d1\u30b9\u3092\u63d0\u4f9b\u3057\u3066\u304f\u308c\u3066\u3044\u308b\u306e\u3067\u3001<br \/>\n\u4e0b\u8a18\u306e\u3088\u3046\u306b\u30d1\u30bf\u30fc\u30f3\u30de\u30c3\u30c1\u3092\u4f7f\u3063\u3066\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u3092\u5b9a\u7fa9\u3059\u308b\u3053\u3068\u304c\u3067\u304d\u305f\u3002<br \/>\n\u30d5\u30ec\u30fc\u30e0\u30ef\u30fc\u30af\u3092\u4f7f\u3063\u305f\u5834\u5408\u307b\u3069\u3067\u306f\u306a\u3044\u304c\u3001\u6bd4\u8f03\u7684\u7c21\u6f54\u306b\u3067\u304d\u305f\u3002<\/p>\n<pre>routerApp :: Pool MySQL.Connection -&gt; Wai.Application\nrouterApp cp req respond = case Wai.pathInfo req of\n  \"v1\" : _ -&gt; (v1App cp req respond) `catch` onException -- \/v1{var}\n  _ -&gt; staticApp req respond -- static html\/js\/css files\n  where\n    onException :: SomeException -&gt; IO Wai.ResponseReceived\n    onException (SomeException e) = respond $ responseNG 5000 \"unknown error\"\n--    onException (SomeException e) = respond $ responseNG 5000 $ displayException e\n\n\nv1App :: Pool MySQL.Connection -&gt; Wai.Application\nv1App cp req respond = case Wai.pathInfo req of\n  _ : \"genre\"  : _ -&gt; genreApp cp req respond -- \/v1\/genre{var}\n  _ : \"tag\"    : _ -&gt; tagApp cp req respond   -- \/v1\/tag{var}\n  _ : \"look\"   : _ -&gt; lookApp cp req respond  -- \/v1\/look{var}\n  _ -&gt; staticApp req respond -- static html\/js\/css files\n<\/pre>\n<p>HTTP\u30e1\u30bd\u30c3\u30c9\u3082\u5408\u308f\u305b\u3066\u30d1\u30bf\u30fc\u30f3\u30de\u30c3\u30c1\u3059\u308c\u3070\u3001REST\u3063\u307d\u3044\u3053\u3068\u3082\u7c21\u5358\u306b\u3067\u304d\u308b\u3002<\/p>\n<pre>genreApp :: Pool MySQL.Connection -&gt; Wai.Application\ngenreApp cp req respond = case (M.parseMethod (Wai.requestMethod req), Wai.pathInfo req) of\n  \n  -- POST \/v1\/genre\n  (Right M.POST, [_,_]) -&gt; do\n    ps &lt;- parseForm req\n    case lookupParam \"title\" ps of\n      Nothing -&gt; respond $ responseNG 5101 \"invalid title\"\n      Just t -&gt; do\n        bs &lt;- AE.encode &lt;$&gt; (withResource cp $ \\conn -&gt; create_genre conn t)\n        respond $ response200 bs\n             \n  -- DELETE \/v1\/genre\/{id}\n  (Right M.DELETE, [_,_,id]) -&gt;\n    case readMaybe (T.unpack id) of\n      Nothing -&gt; respond $ responseNG 5102 \"invalid id\"\n      Just id -&gt; do\n        withResource cp $ \\conn -&gt; delete_genre conn id\n        respond responseOK\n    \n<\/pre>\n<h2>8. SQL\u30a4\u30f3\u30b8\u30a7\u30af\u30b7\u30e7\u30f3\u9632\u6b62\u6a5f\u80fd<\/h2>\n<p>mysql-simple \u3067\u306f SQL\u30a4\u30f3\u30b8\u30a7\u30af\u30b7\u30e7\u30f3\u3092\u9632\u6b62\u3059\u308b\u305f\u3081\u3001query \u3084 query_ \u306b\u306f<br \/>\n\u6587\u5b57\u5217\u306e\u30ea\u30c6\u30e9\u30eb\u3067SQL\u6587\u3092\u6e21\u3055\u306a\u3051\u308c\u3070\u306a\u3089\u306a\u3044\u3088\u3046\u306b\u3057\u3066\u3042\u308b\u3088\u3046\u3060\u3002<\/p>\n<p>\u6587\u5b57\u5217\u7d50\u5408\u3092\u4f7f\u3063\u3066SQL\u6587\u3092\u7d44\u307f\u7acb\u3066\u308b\u3053\u3068\u304c\u3067\u304d\u306a\u3044\u305f\u3081\u3001<br \/>\n\u6761\u4ef6\u3092\u4efb\u610f\u3067\u6307\u5b9a\u3059\u308bSELECT\u6587\u304c\u5c11\u3057\u4f5c\u308a\u306b\u304f\u304b\u3063\u305f\u304c\u3001<br \/>\n\u4e0b\u8a18\u306e\u3088\u3046\u306b\u3059\u308b\u3053\u3068\u3067\u5bfe\u5fdc\u3067\u304d\u305f\u3002<\/p>\n<pre>-- [TODO] sanitize param of LIKE phrase \nselect_look :: Connection -&gt; Maybe Title -&gt; Maybe Description -&gt; Maybe GenreId -&gt; Maybe TagId -&gt; IO [Look]\nselect_look conn mt md mgid mtid = do\n\n  (ft,t) &lt;- case mt of\n    Nothing -&gt; return (1::Int,\"%\")\n    Just t -&gt;  return (0::Int,t)\n    \n  (fd,d) &lt;- case md of\n    Nothing -&gt; return (1::Int,\"%\")\n    Just d -&gt;  return (0::Int,d)\n\n  (fgid,gid) &lt;- case mgid of\n    Nothing -&gt;  return (1::Int,0)\n    Just gid -&gt; return (0::Int,gid)\n\n  (ftid,tid) &lt;- case mtid of\n    Nothing -&gt;  return (1::Int,0)\n    Just tid -&gt; return (0::Int,tid)\n\n  ls &lt;- query conn [r|\n    SELECT\n       look_id\n      ,look_create_dt\n      ,look_update_dt\n      ,look_title\n      ,look_description\n      ,genre_id\n      ,genre_title\n    FROM look\n      LEFT OUTER JOIN genre ON look_genre_id = genre_id\n    WHERE 1 = 1\n      AND (1=? OR look_title LIKE ?)\n      AND (1=? OR look_description LIKE ?)\n      AND (1=? OR look_genre_id = ?)\n      AND (1=? OR look_id IN\n                  (SELECT lht_look_id FROM look_has_tag WHERE lht_tag_id =?))\n    ORDER BY look_id\n  |](ft,t,fd,d,fgid,gid,ftid,tid)\n\n  tags &lt;- select_tag_for_looks conn ls\n  return $ merge_tags tags ls\n<\/pre>\n<h2>9. \u307e\u3068\u3081<\/h2>\n<ul>\n<li>mysql-simple \u306a\u3069\u306e\u30e9\u30a4\u30d6\u30e9\u30ea\u3092\u4f7f\u3048\u3070 DB\u306e\u30c7\u30fc\u30bf\u3068Haskell\u306e\u30c7\u30fc\u30bf\u306e\u30de\u30c3\u30d4\u30f3\u30b0\u306f\u7c21\u5358\u3002<\/li>\n<li>aeson \u3092\u4f7f\u3048\u3070 Haskell\u306e\u30c7\u30fc\u30bf\u3092 JSON\u5316\u3059\u308b\u306e\u306f\u7c21\u5358\u3002<\/li>\n<li>resource-pool \u3092\u4f7f\u3048\u3070\u30b3\u30cd\u30af\u30b7\u30e7\u30f3\u30d7\u30fc\u30eb\u306e\u5c0e\u5165\u306f\u7c21\u5358\u3002<\/li>\n<li>mysql-simple \u306e\u30c8\u30e9\u30f3\u30b6\u30af\u30b7\u30e7\u30f3\u51e6\u7406\u306f\u7c21\u5358\u78ba\u5b9f\u3002<\/li>\n<li>Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u306e\u30d1\u30b9\/\u30e1\u30bd\u30c3\u30c9\u30eb\u30fc\u30c6\u30a3\u30f3\u30b0\u306f\u5358\u7d14\u306a\u30d1\u30bf\u30fc\u30f3\u30de\u30c3\u30c1\u3067\u3082\u3042\u308b\u7a0b\u5ea6\u53ef\u80fd\u3002<\/li>\n<li>mysql-simple \u3092\u4f7f\u3048\u3070 SQL\u30a4\u30f3\u30b8\u30a7\u30af\u30b7\u30e7\u30f3\u306e\u5371\u967a\u6027\u304c\u4f4e\u4e0b\u3002<\/li>\n<\/ul>\n<h2>10. \u4eca\u5f8c\u306e\u8ab2\u984c<\/h2>\n<ul>\n<li>mysql-simple \u4ee5\u5916\u306eDB\u30e9\u30a4\u30d6\u30e9\u30ea\u3082\u3044\u308d\u3044\u308d\u8a66\u3057\u3066\u307f\u305f\u3044\u3002<\/li>\n<li>\u5165\u529b\u306e\u30d0\u30ea\u30c7\u30fc\u30b7\u30e7\u30f3\u3092\u3082\u3063\u3068\u3061\u3083\u3093\u3068\u5b9f\u88c5\u3057\u3066\u307f\u305f\u3044\u3002<\/li>\n<li>\u30d5\u30a1\u30a4\u30eb\u306e\u30a2\u30c3\u30d7\u30ed\u30fc\u30c9\u3082\u8a66\u3057\u3066\u307f\u305f\u3044\u3002<\/li>\n<li>\u30d5\u30ed\u30f3\u30c8\u30a8\u30f3\u30c9\u3092\u5b9f\u88c5\u3057\u3066\u3001\u5b9f\u969b\u306b\u4f7f\u3063\u3066\u307f\u305f\u3044\u3002<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>\u3053\u306e\u8a18\u4e8b\u306f\u3001\u30b9\u30bf\u30fc\u30c8\u30c8\u30a5\u30c7\u30a4\u5de5\u52d9\u5e97 Advent Calendar 14\u65e5\u76ee\u306e\u8a18\u4e8b\u3067\u3059\u3002 wai\uff08Haskell\u88fd\u306eWeb\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u898f\u683c\uff09\u306b\u6e96\u62e0\u3057\u3001warp\uff08wai\u6e96\u62e0\u306eHaskell\u88fdWeb\u30b5\u30fc\u30d0\uff09\u4e0a\u3067\u52d5\u4f5c\u3059\u308bW &hellip; <a href=\"https:\/\/mitsuji.org\/?p=300\" class=\"more-link\">\u7d9a\u304d\u3092\u8aad\u3080 <span class=\"screen-reader-text\">Haskell(wai) \u306b\u3088\u308b Web\u30a2\u30d7\u30ea\u30b1\u30fc\u30b7\u30e7\u30f3\u958b\u767a\u306e\u5b9f\u969b\uff08DB\u7de8\uff09<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[7],"tags":[],"_links":{"self":[{"href":"https:\/\/mitsuji.org\/index.php?rest_route=\/wp\/v2\/posts\/300"}],"collection":[{"href":"https:\/\/mitsuji.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/mitsuji.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/mitsuji.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/mitsuji.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=300"}],"version-history":[{"count":18,"href":"https:\/\/mitsuji.org\/index.php?rest_route=\/wp\/v2\/posts\/300\/revisions"}],"predecessor-version":[{"id":484,"href":"https:\/\/mitsuji.org\/index.php?rest_route=\/wp\/v2\/posts\/300\/revisions\/484"}],"wp:attachment":[{"href":"https:\/\/mitsuji.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=300"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/mitsuji.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=300"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/mitsuji.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=300"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}