【问题标题】:Haskell Esqueleto project subset of columns to list of custom recordsHaskell Esqueleto 项目列子集到自定义记录列表
【发布时间】:2018-10-13 02:39:09
【问题描述】:

在我看到的所有示例中,esqueleto 的结果被投影到元组列表或entities records

例如:

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

【问题讨论】:

  • 我们不能在这里执行fmap吗?
  • 我认为您可能必须实例化SqlSelect。我可能会看看元组实例的定义方式并从那里开始。
  • @WillemVanOnsem 我要求一种直接返回自定义记录的方法。没有去元组然后去自定义类型的低效率。
  • 是的,但我在想Custom &lt;$&gt; (li ^. LogItemTitle) &lt;*&gt; (li ^. LogItemId)。但现在我无法测试它:(
  • @WillemVanOnsem 我认为提升它还不够。 select :: (SqlSelect a r, MonadIO m) =&gt; SqlQuery a -&gt; SqlReadT m [r],据我所知,有一个从返回类型 a 到 SQL 表达式到最终类型 r 的实际转换。

标签: haskell tuples record esqueleto


【解决方案1】:

esqueleto 在这里实际做的事情有点复杂。这是select 的类型:

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

这需要一个SqlQuery a(一个包装你return的值的monad),并返回一个SqlReadT m [r](一个包装结果列表的monad)。当您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"

(请注意,我实际上无法测试上述任何内容,因此可能存在错误)

需要注意的是,在上面的示例中,ar 类型并不相同(Custom'Custom)。这是因为在 select 内部,您使用的所有值都是 Value 类型,而不是它们的实际类型。

【讨论】:

    猜你喜欢
    • 2018-10-13
    • 1970-01-01
    • 2016-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-02
    • 2017-09-21
    相关资源
    最近更新 更多