Yesod中的缩进查询

时间:2014-04-22 14:13:48

标签: haskell yesod persistent

假设一个简单的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的值,其中包含最大数量

b   10.2 
d   50.0
f   1000.0

1 个答案:

答案 0 :(得分:2)

我不确定您已尝试过什么,但由于这实际上是一个连接(并且持久性的类型安全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应该很简单。

我知道这不是最好看的代码片段,但是比较这里显示的两种方法可能对你有用。