我正在编写一个带有 Yesod 和 Persistent 的 webapp。我有一个带有多个表的 SQL 数据库,其中包含我的“项目”的特征。我有一个主表和与 id 链接的具有多个值的附加表的选项。
用户应该能够选择他想要过滤的那些特征并指定过滤值。如果用户过滤操作系统,SQL-Query 的许可证和编码将如下所示:
runquery :: (YesodPersist site, YesodPersistBackend site ~ SqlBackend) =>
String -> String -> String
-> HandlerT site IO [Entity Project]
runquery os license coding = runDB
$ select $ distinct
$ from $ \(p `InnerJoin` pl `InnerJoin` l `InnerJoin` pc
`InnerJoin` c `InnerJoin` o `InnerJoin` po) -> do
on $ p ^. ProjectId ==. pl ^. ProjectLicenseFkProjectId
on $ p ^. ProjectId ==. pc ^. ProjectCodingFkProjectId
on $ p ^. ProjectId ==. po ^. ProjectOsFkProjectId
on $ l ^. LicenseId ==. pl ^. ProjectLicenseFkLicenseId
on $ o ^. OsId ==. po ^. ProjectOsFkOsId
on $ c ^. CodingId ==. pc ^. ProjectCodingFkCodingId
where_ ( o ^. OsName ==. val (Just (pack os)))
where_ ( l ^. LicenseName ==. val (Just (pack license)))
where_ ( c ^. CodingName ==. val (Just (pack coding)))
limit 50
return p
但我不想总是加入所有表,因为当有很多表但用户只过滤少数表时,这对性能非常不利。但我也不想为每个可查询功能组合编写一个查询,因为这意味着编写 N² 大部分相同的查询。
'on' 和 'where' 子句可以根据我们是否要过滤而动态完成。但是连接在 Lambda 函数的参数范围内。我发现没有办法建立这个依赖于外部变量。
所以我认为 Template Haskell 可能会成功……我开始学习 TH 并将查询的核心转换为 TH。但是现在我被卡住了,不确定 TH 是否可以帮助我以及它是否是正确的方法?
所以这是我使用 Template Haskell 的进展:
foo os license coding = lamE [pat] (code)
where
p = mkName "p"
po = mkName "po"
pl = mkName "pc"
pc = mkName "pl"
pat = pat' [os, license, coding] [varP po, varP pl, varP pc]
pat' [] [] = varP p
pat' ((Just _):ws) (q:qs) = infixP q (mkName "InnerJoin") (pat' ws qs)
pat' (Nothing:ws) (q:qs) = pat' ws qs
code = do
case os of
Just _ -> [|
on $ $(varE p) ^. ProjectId ==. $(varE po) ^. ProjectOsFkProjectId
|]
Nothing -> [| return () |]
case license of
Just _ -> [|
on $ $(varE p) ^. ProjectId ==. $(varE pl) ^. ProjectLicenseFkProjectId
|]
Nothing -> [| return () |]
case coding of
Just _ -> [|
on $ $(varE p) ^. ProjectId ==. $(varE pc) ^. ProjectCodingFkProjectId
|]
Nothing -> [| return () |]
[| do
limit 50
return $(varE p) |]
所以我希望你能得到帮助:
- 我可以/应该使用 Template Haskell 来做到这一点吗?
- 如果是这样:我怎样才能用参数调用函数 foo ?
- 如果不是:什么是正确的解决方案?