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 布局中的站点范围变量(django 上下文处理器模拟)