简体   繁体   English

Haskell Esqueleto将列的子集项目列为自定义记录列表

[英]Haskell Esqueleto project subset of columns to list of custom records

In all the examples I have seen the results from esqueleto are projected into a list of tuples or to entities records . 在所有示例中,我看到esqueleto的结果被投影到元组列表或实体记录中

For example: 例如:

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

Is there any way in esqueleto to project a subset of the columns to custom records (different than the entity) instead of tuples? 在esqueleto中是否有任何方法可以将列的子集投影到自定义记录(不同于实体)而不是元组? This being done without an extra projection from tuples to custom records. 这样做没有从元组到自定义记录的额外投影。

As an example, let's say that it would be inefficient to get all the data from the database so we only want to project columns WindowTitle and BeginTime from the database into a custom record with adequate names for those columns. 例如,假设从数据库中获取所有数据是低效的,因此我们只想将数据库中的WindowTitle和BeginTime列投影到具有足够名称的自定义记录中。

Update 更新

Example of not working code: 不工作代码的示例:

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

Error: 错误:

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

Update 更新

Example of not working code: 不工作代码的示例:

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

Error: 错误:

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

What esqueleto is actually doing here is a little complicated. esqueleto实际上在这里做的事情有点复杂。 Here is the type for select : 以下是select的类型:

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

This takes an SqlQuery a (a monad wrapping the value you return ), and returns an SqlReadT m [r] (a monad wrapping a list of results). 这需要一个SqlQuery a (一个monad包装你return的值),并返回一个SqlReadT m [r] (包含结果列表的monad)。 When you return your Custom type, the following happens: return Custom类型时,会发生以下情况:

  1. esqueleto converts your a type to persistent's internal SQL representation esqueleto将您a类型转换为持久性的内部SQL表示
  2. persistent executes the query and gets back a results list persistent执行查询并返回结果列表
  3. esqueleto converts the results list from persistent's internal SQL representation to [r] esqueleto将结果列表从持久性的内部SQL表示转换为[r]

To make this work for custom types, you'll need to instantiate SqlSelect , and define conversion functions to and from the persistent types: 要使这适用于自定义类型,您需要实例化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"

(note that I wasn't actually able to test any of the above, so it might have bugs) (请注意,我实际上无法测试上述任何内容,因此可能存在错误)

One thing to note is that in the above example, the a and r types are not the same ( Custom' vs Custom ). 需要注意的一点是,在上面的例子中, ar类型不相同( Custom'Custom )。 This is because inside of a select , all of the values you're working with are of type Value , not their actual types. 这是因为在select ,您使用的所有值都是Value类型,而不是它们的实际类型。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM