Haskell Spock и Postgres-Simple - таблица запросов и возврат в виде json
Я новичок в Haskell и честно переживаю трудные времена. Но это расширяет мое мышление, поэтому здесь мы идем. Я пытаюсь запустить действительно простой веб-сервер, который запрашивает базу данных Postgres и должен вернуть результат в виде JSON.
Запрос абсолютно прост: "Выберите id, данные из MYTABLE, где id = 1"
Но система типов haskell убивает меня прямо сейчас, и окончательный тип моих действий не совпадает. Я использую Spock и PostgreSQL-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])
Затем я пытаюсь передать идентификатор конверта с помощью лямбда-функции, для этого мне также нужно изменить PostgreSQL-Simple 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)
У меня также есть проблема, чтобы вернуть только первый элемент из запроса, даже без лямбда-функции.
Полный исходный код можно найти на bitbucket
Я надеюсь, что у кого-то есть время, чтобы помочь мне здесь. Спасибо за чтение уже.
2 ответа
Что ошибка в основном говорит о том, что вы не можете передать Int
в query
в качестве третьего аргумента. query
ожидает что-то, что имеет экземпляр класса типов ToRow
а также Int
не один. Что вы, возможно, захотите сделать в вашем случае, учитывая, что вы хотите передать только одно значение query
это использовать Only
, Таким образом, эта строка становится:
query conn "select id,data from envelope where id = ?" (Only (eid :: Int))
На основании помощи супа я также нахожу соответствующую часть в документации Postgres- Simple. Просто для завершения, документы упоминают альтернативный синтаксис, используя одноэлементный список. Так что вместо обычных скобок вы можете использовать просто квадратные скобки, и это тоже работает:
query conn "select id,data from envelope where id = ?" [eid :: Int]
Кроме того, в частности, в моем примере, имеет больше смысла возвращать только первый ряд из результата, который затем оказался просто прямым с head
функция. Для тех, кому это нужно, вот как вы это делаете:
Спок и Прелюдия включают в себя head
функция. Чтобы избежать конфликтов, я решил скрыть функцию Spocks, так как я все равно ее не использую.
Добавьте к вашему сценарию вверху:
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])
Готово.