Yesod 中的缩进查询

Posted

技术标签:

【中文标题】Yesod 中的缩进查询【英文标题】:Indented queries in Yesod 【发布时间】:2014-04-22 14:13:47 【问题描述】:

假设您的模型中有一个简单的 1 -> N 关系。

我想呈现一个表格,其中的行由来自主实体的数据和依赖实体(N)的聚合函数组成,无论是 sum、avg、max 还是 custom。

我已经尝试了几十个处理程序,但没有找到任何可以编译的...

有什么提示吗?

例子:

实体 A

Key Value
a   b
c   d
e   f

实体 B

ForeignKey Num Value
a          1   13.0
a          2   25.4
a          3   10.2
c          1   33.33
c          2   50.0
e          1   100.0
e          2   1000.0

并希望在同一“html 行”中从 A 获取值和从 B 获取最大 Num 的值

b   10.2 
d   50.0
f   1000.0

【问题讨论】:

【参考方案1】:

我不确定您已经尝试过什么,但由于这实际上是一个连接(并且持久化的类型安全 API 不支持它们),运行自定义 SQL 查询应该是最有效的解决方案。如果你的模型看起来像这样

EntityA
    key Text
    value Text
    deriving Show
EntityB
    foreignKey EntityAId
    num Int
    value Double
    deriving Show

使用max 函数连接数据的查询如下所示:

SELECT a.value, max(b.num), b.value
FROM entity_a a LEFT OUTER JOIN entity_b b
ON a.id = b.foreign_key
GROUP BY (a.id);

将它与rawSql 函数结合起来应该可以解决问题。

但是,如果您想使用持久化的类型安全 API 来执行此操作,则可能会导致对数据库的大量查询,具体取决于数据的大小。下面是它的样子:

-- a custom maximum function for EntityB
customFunction :: [EntityB] -> Maybe EntityB
customFunction = foldr customFold Nothing

customFold :: EntityB -> Maybe EntityB -> Maybe EntityB
customFold a Nothing = Just a
customFold a (Just b) = if (entityBNum a) > (entityBNum b) then (Just a) else (Just b)

main :: IO ()
main = runSqlite ":memory:" $ do
    runMigration migrateAll

    -- insert some data
    firstAId <- insert $ EntityA "a" "b"
    secondAId <- insert $ EntityA "c" "d"
    thirdAId <- insert $ EntityA "e" "f"
    -- let's put this one just for demonstration
    fourthAId <- insert $ EntityA "g" "h"

    bId1 <- insert $ EntityB firstAId 1 13.0
    bId2 <- insert $ EntityB firstAId 2 25.4
    bId3 <- insert $ EntityB firstAId 3 10.2
    bId4 <- insert $ EntityB secondAId 1 33.33
    bId5 <- insert $ EntityB secondAId 2 50.0
    bId6 <- insert $ EntityB thirdAId 1 100.0
    bId7 <- insert $ EntityB thirdAId 2 1000.0

    -- select all EntityA's
    as <- selectList [] [Asc EntityAId]
    -- aKeys are used as foreign keys for EntityB's
    let aKeys = map entityKey as
        -- these values will be used for "joining" data together
        aVals = map (entityAValue . entityVal) as
    -- this will produce a number of queries which is
    -- equal to a number of groups (in your simple case, 3)
    bs <- mapM (\bKey -> selectList [EntityBForeignKey ==. bKey] []) aKeys
    -- extract what's needed from a list of lists of EntityB's
    let bEntities = map (customFunction . (map entityVal)) bs
        -- ... and zip them together
        joined = zip aVals bEntities

    liftIO $ print joined

这段代码我得到的输出是:

[("b",Just (EntityB entityBForeignKey = Key unKey = PersistInt64 1, entityBNum = 3, entityBValue = 10.2)),("d",Just (EntityB entityBForeignKey = Key unKey = PersistInt64 2, entityBNum = 2, entityBValue = 50.0)),("f",Just (EntityB entityBForeignKey = Key unKey = PersistInt64 3, entityBNum = 2, entityBValue = 1000.0)),("h",Nothing)]

现在像在您的问题中那样提取表格并将其放入 Handler monad 应该很简单。

我知道这不是最漂亮的代码,但比较此处显示的两种方法可能对您有用。

【讨论】:

rawSQL 的答案让我问 mongoDB-Persistent 是否支持它......?但是,另一种方法为我解决了这个问题,我的问题是对haskell的无知,mapM是关键。

以上是关于Yesod 中的缩进查询的主要内容,如果未能解决你的问题,请参考以下文章

Yesod / Persistent中的外键约束?

Yesod 布局中的站点范围变量(django 上下文处理器模拟)

Yesod:如何在终端或其他工具中记录查询时间?

Yesod persistent-postgresql rawSql 查询带有列列表通配符会产生语法错误

yesod 持久性依赖缺失

Haskell:Yesod 和 Esqueleto