2017-02-23 52 views
1

我是haskell的新手,並且對它有誠實的困難時期。但它擴展了我的思想,所以我們走了。 我想運行一個非常簡單的Web服務器,查詢Postgres數據庫,並應該返回結果爲JSON。Haskell Spock和Postgres-Simple - 查詢表並返回爲json

查詢是絕對簡單: 「選擇ID,從MYTABLE數據其中id = 1」

但是哈斯克爾的類型系統是殺害我現在和我的行動的最終類型不匹配。 我使用SpockPostgreSQL-Simple作爲組合。

大多數教程要麼簡單的爲我想要做什麼或困難。我處於中間地帶,錯過了許多Haskell的理解,很多我以前通過簡單的複製和粘貼已經解決了的問題,並且得到了一個簡單的版本。

但是,只要我嘗試傳遞一個路由變量,我就失敗了。 這是我的工作版本。我的數據庫表是這裏所說的「信封」中,重要的電話是在那裏說:get "json"

{-# LANGUAGE OverloadedStrings #-} 
{-# LANGUAGE FlexibleInstances #-} 

module Main where 

import Web.Spock 
import Web.Spock.Config 
import Database.PostgreSQL.Simple 
import Data.Pool 
import Data.Aeson (ToJSON(toJSON), object, (.=),Value) 
import Database.PostgreSQL.Simple.FromRow 

type AppAction a = SpockActionCtx() Connection AppSession AppState a 

data AppState = EmptyState 
data AppSession = EmptySession 

data Envelope = Envelope { envId :: Int, envData :: Value } deriving Show 

instance FromRow Envelope where 
    fromRow = Envelope <$> field <*> field 

instance ToJSON Envelope where 
    toJSON (Envelope envA envB) = object [ "id" .= envA, "data" .= envB ] 

main :: IO() 
main = 
    do pool<-createPool (connect (ConnectInfo "localhost" 5432 "" "" "envelopes")) close 1 10 10 
    spockCfg <- defaultSpockCfg EmptySession (PCPool pool) EmptyState 
    runSpock 8080 (spock spockCfg app) 

app :: SpockM Connection AppSession AppState() 
app = do 
    get root $ 
     text "Hello World!" 
    get "json" $ do 
     xs<-runQuery $ \conn -> 
     query_ conn "select id,data from envelope where id = 1" 
     json (xs::[Envelope]) 

然後我嘗試在信封ID通過具有拉姆達功能,我還需要修改PostgreSQL,簡單query_query

get ("json" <//> var ) $ \eid -> do 
     xs<-runQuery $ \conn -> 
     query conn "select id,data from envelope where id = ?" (eid :: Int) 
     json (xs::[Envelope]) 

我得到的錯誤說:

No instance for (ToRow Int) arising from a use of ‘query’ 
    In the expression: 
     query conn "select id,data from envelope where id = ?" (eid :: Int) 
    In the second argument of ‘($)’, namely 
     ‘\ conn 
     -> query 
       conn "select id,data from envelope where id = ?" (eid :: Int)’ 
    In a stmt of a 'do' block: 
     xs <- runQuery 
      $ \ conn 
       -> query 
        conn "select id,data from envelope where id = ?" (eid :: Int) 

我也有問題,只返回第一伊特m,即使沒有lambda函數。

全部源代碼可以在bitbucket

找到我希望有人有時間幫我在這裏。 感謝您的閱讀。

回答

3

錯誤基本上說的是你不能通過Intquery作爲第三個參數。 query期望具有類型類別ToRowInt的實例不是其中之一。考慮到你只想傳遞一個值到query,你可能想要在你的情況下做的是使用Only。因此該行變爲:

query conn "select id,data from envelope where id = ?" (Only (eid :: Int)) 
+0

什麼,那是什麼?畢竟這種想法令人頭疼? 非常感謝,這是有效的。對於我來說,爲什麼使用兩個參數你可以簡單地做(parA:Int,parB:Int),但使用一個參數你必須使用'Only'符號。但是當你指向我時,我在Postgres-Simple Docs中發現了一些評論,所以我想我以後會理解它。 –

+0

在這種情況下,您可以將「Int」這樣的類型看作一列,把元組看作一行有兩列,將「Only」看作一列一行。 'query'只處理行,所以你需要把你的列放在一行中。這與在sql中的insert語句中使用'values'有點相似。你不能只傳遞1,你需要'values(1)'或'select 1'。 – soupi

+0

現在更有意義。謝謝。 –

1

基於soupis的幫助,我也在Postgres-Simple文檔中找到相應的部分。爲了完成,文檔使用單例列表提及了一種替代語法。因此,而不是使用正常的括號,你可以用簡單的方括號和它的作品太:

query conn "select id,data from envelope where id = ?" [eid :: Int]

此外,特別是在我的例子,它更有意義,從結果只返回第一行,然後事實證明,這只是簡單的head函數。對於任何需要它的人來說,這裏是你如何做的:

Spock和Prelude都包含一個head函數。爲了避免衝突,我決定隱藏Spock函數,因爲我還沒有使用它。

添加到您的腳本頂部:

import Web.Spock hiding(head)

然後get部分更改爲:

get ("json" <//> var ) $ \eid -> do 
     xs<-runQuery $ \conn -> 
     query conn "select id,data from envelope where id = ?" [eid :: Int] 
     json $ head (xs::[Envelope]) 

完成。