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])

Готово.

Другие вопросы по тегам