Haskell Esqueleto проект подмножество столбцов в список пользовательских записей - PullRequest
0 голосов
/ 03 мая 2018

Во всех примерах, которые я видел, результаты из esqueleto проецируются в список кортежей или записей сущностей .

Например:

previousLogItems <- select $ from $ \li -> do
        orderBy [desc (li ^. LogItemId)]
        limit 10
        return (li ^. LogItemId, li ^. LogItemTitle)

Есть ли способ в esqueleto проецировать подмножество столбцов в пользовательские записи (отличные от сущности) вместо кортежей? Это делается без дополнительной проекции кортежей на пользовательские записи.

В качестве примера предположим, что было бы неэффективно получать все данные из базы данных, поэтому мы хотим проецировать только столбцы WindowTitle и BeginTime из базы данных в пользовательскую запись с соответствующими именами для этих столбцов.

Обновление

Пример не работающего кода:

data Custom = Custom
  { title :: Text
  , id    :: Int
  } deriving (Eq, Show, Generic)

daily :: Servant.Handler [Custom]
daily = do
  lis <- liftIO $ runDB $
            select $ from $ \li -> do
                    orderBy [desc (li ^. LogItemId)]
                    limit 25
                    return (Custom (li ^. LogItemTitle) (li ^. LogItemId))
  return lis

Ошибка:

• Couldn't match expected type ‘Text’
              with actual type ‘SqlExpr (Database.Esqueleto.Value Text)’
• In the first argument of ‘Custom’, namely ‘(li ^. LogItemTitle)’
  In the first argument of ‘return’, namely
    ‘(Custom (li ^. LogItemTitle) (li ^. LogItemId))’
  In a stmt of a 'do' block:
    return (Custom (li ^. LogItemTitle) (li ^. LogItemId))

Обновление

Пример не работающего кода:

daily :: Servant.Handler [Custom]
daily = do
  lis <- liftIO $ runDB $
            select $ from $ \li -> do
                    orderBy [desc (li ^. LogItemId)]
                    limit 25
                    return (Custom <$> (li ^. LogItemTitle) <*> (li ^. LogItemId))
  return lis

Ошибка:

• Couldn't match type ‘Database.Esqueleto.Value Text’ with ‘Text’
  Expected type: SqlExpr Text
    Actual type: SqlExpr (Database.Esqueleto.Value Text)
• In the second argument of ‘(<$>)’, namely ‘(li ^. LogItemTitle)’
  In the first argument of ‘(<*>)’, namely
    ‘Custom <$> (li ^. LogItemTitle)’
  In the first argument of ‘return’, namely
    ‘(Custom <$> (li ^. LogItemTitle) <*> (li ^. LogItemId))’


• Couldn't match type ‘Database.Esqueleto.Value (Key LogItem)’
                 with ‘Int’
  Expected type: SqlExpr Int
    Actual type: SqlExpr (Database.Esqueleto.Value (Key LogItem))
• In the second argument of ‘(<*>)’, namely ‘(li ^. LogItemId)’
  In the first argument of ‘return’, namely
    ‘(Custom <$> (li ^. LogItemTitle) <*> (li ^. LogItemId))’
  In a stmt of a 'do' block:
    return (Custom <$> (li ^. LogItemTitle) <*> (li ^. LogItemId))

1 Ответ

0 голосов
/ 03 мая 2018

То, что на самом деле здесь делает эскелето, немного сложно. Вот тип для select:

select :: (SqlSelect a r, MonadIO m) => SqlQuery a -> SqlReadT m [r]

Это принимает SqlQuery a (монаду, заключающую в себе значение return), и возвращает SqlReadT m [r] (монаду, заключающую в себе список результатов). Когда вы return набираете Custom, происходит следующее:

  1. esqueleto преобразует ваш тип a во внутреннее представление персистентного SQL
  2. persistent выполняет запрос и возвращает список результатов
  3. esqueleto преобразует список результатов из внутреннего представления SQL персистенции в [r]

Чтобы это работало для пользовательских типов, вам нужно создать экземпляр SqlSelect и определить функции преобразования в и из постоянных типов:

data Custom' = Custom' (Value Text) (Value Int)

data Custom = Custom
  { title :: Text
  , id    :: Int
  }

instance SqlSelect Custom' Custom where
  sqlSelectCols esc (Custom' a b) = (mconcat [ta, ", ", tb], va ++ vb)
    where
      (ta, va) = sqlSelectCols esc a
      (tb, vb) = sqlSelectCols esc b
  sqlSelectColCount _ = 2
  sqlSelectProcessRow [PersistText a, PersistInt64 b] = Right $ Custom a b
  sqlSelectProcessRow _ = Left "Error: Incorrect rows to translate to Custom"

(обратите внимание, что на самом деле я не смог протестировать ничего из вышеперечисленного, поэтому в нем могут быть ошибки)

Следует отметить, что в приведенном выше примере типы a и r не совпадают (Custom' против Custom). Это потому, что внутри select все значения, с которыми вы работаете, имеют тип Value, а не их фактические типы.

...