我正在尝试构建一个与以下SQL匹配的Opaleye查询:
select * ,
(select array_agg(tags.tagname)
from articles_tags
inner join tags on tags.id = articles_tags.tag_fk
where articles_tags.article_fk = articles.id
)
from articles所涉及的表格(简化)如下:
articles: (id, title, content)
articles_tags: (article_fk, tag_fk)
tags: (id, tagname)我的目标是查询带有一个或多个标签的文章,并作为数组检索所有附加的标记。
到目前为止,我收到了以下基本查询:
-- | Query all article-tag relations.
allTaggedArticlesQ :: OE.Select TaggedArticleR
allTaggedArticlesQ = OE.selectTable taggedArticlesTable
-- | Query article-tag relations for the given articles.
taggedArticlesQ :: OE.SelectArr PA.ArticleIdField TaggedArticleR
taggedArticlesQ = proc articleId -> do
ta <- allTaggedArticlesQ -< ()
OE.restrict -< articleFk ta .=== articleId
returnA -< ta
-- | Join article-ids and tag names for the given subset of articles.
articleTagNamesQ :: OE.SelectArr PA.ArticleIdField ArticleTagR
articleTagNamesQ = proc articleIds -> do
ta <- taggedArticlesQ -< articleIds
tags <- PT.allTagsQ -< ()
OE.restrict -< PT.tagKey tags .=== tagFk ta
returnA -< ArticleTag (articleFk ta) (PT.tagName tags)但是,我无法让聚合工作:下面不进行类型检查,我也不知道如何用上面的查询组合这个聚合:
-- | Aggregate all tag names for all given articles
articleTagsQ :: PA.ArticleIdField -> OE.Select (PA.ArticleIdField, F (OE.SqlArray OE.SqlText))
articleTagsQ = OE.aggregate
( pArticleTag
ArticleTag
{ atArticleFk = OE.groupBy,
atTagname = OE.arrayAgg
}
) OE.selectTable articleTagNamesQ在一些博客文章和GitHub问题中,我发现聚合不适合Product-Pro函子和arrow,因此不能包含在箭头查询中。然而,我对Haskell还比较陌生,还没有真正理解这两个库背后的理论(似乎没有一个初学者友好的文档);因此,我无法提出如何将查询与聚合相结合的一般结构。这里有一些例子,但是我不理解一般的概念,所以我不能把这些例子应用到我的问题上。
如果有人能提供关于如何用Opaleye中的常规查询组成聚合的见解,我将非常感激,谢谢!
发布于 2020-08-03 13:59:29
在分析了几个示例之后,下面是我最终成功构建并运行的解决方案:
import Control.Arrow
import qualified Opaleye as OE
import qualified Data.Profunctor.Product as PP
type F field = OE.Field field
-- | Query all tags.
allTagsQ :: OE.Select TagR
allTagsQ = OE.selectTable tagsTable
-- | Query all article-tag relations.
allTaggedArticlesQ :: OE.Select TaggedArticleR
allTaggedArticlesQ = OE.selectTable taggedArticlesTable
-- | Join article-ids and tag names for all articles.
articleTagNamesQ :: OE.Select (F OE.SqlInt8, F OE.SqlText)
articleTagNamesQ = proc () -> do
TaggedArticle {articleFk = aId, tagFk = tFk} <- allTaggedArticlesQ -< ()
Tag {tagKey = tId, tagName = tn} <- allTagsQ -< ()
OE.restrict -< tFk OE.(.===) tId -- INNER JOIN ON
returnA -< (aId, tn)
-- | Aggregate all tag names for all articles
articleTagsQ :: OE.Select (F OE.SqlInt8, F (OE.SqlArray OE.SqlText))
articleTagsQ =
OE.aggregate (PP.p2 (OE.groupBy, OE.arrayAgg)) $
arr (first) <<< articleTagNamesQarticles_tags表的一行在Haskell中由多态TaggedArticle* Opaleye表示,而标记行的Tag*类似。
关键是选择两个表的所有行,然后执行连接,最后进行聚合。因为Opaley中的聚合函数既不是Arrow也不是ProductProfunctor,但是OE.aggregate函数需要Select a,所以我不能将聚合作为用箭头符号编写的查询的一部分。相反,我必须编写一个以Select a作为输入的单独函数。
请注意,不能在更一般的SelectArr上执行聚合。在pacakge文档中:“根据设计,不存在Aggregator b b' -> \S.SelectArr a b -> S.SelectArr a b'类型的聚合函数,这样的函数将允许违反SQL的作用域规则并导致无效的查询。”
我上面的代码有点简化了。我试着用多态类型作为键。但是,我不知道如何用这些newtype包装器编写所有代码;相反,我不得不多次展开和重新包装字段。
我遇到的另一个问题是从联接中产生的行类型的定义。最初,我定义了一个新的多态行类型。但是,我无法正确地打开该类型的字段,以便将它们输入到OE.Aggregator中。因此,我选择了上面更详细的元组表示法。
发布于 2020-08-08 07:45:03
我对您的答案中的代码做了一些修改,以便将其编译为独立的文件:
OE..===而不是OE.(.===)arr first{-# LANGUAGE Arrows #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TemplateHaskell #-}
import Control.Arrow
import qualified Opaleye as OE
import qualified Data.Profunctor.Product as PP
import Data.Profunctor.Product.TH (makeAdaptorAndInstance')
type F field = OE.Field field
data TaggedArticle a b = TaggedArticle { articleFk :: a, tagFk :: b }
type TaggedArticleR = TaggedArticle (F OE.SqlInt8) (F OE.SqlInt8)
data Tag a b = Tag { tagKey :: a, tagName :: b }
type TagR = Tag (F OE.SqlInt8) (F OE.SqlText)
$(makeAdaptorAndInstance' ''TaggedArticle)
$(makeAdaptorAndInstance' ''Tag)
tagsTable :: OE.Table TagR TagR
tagsTable = error "Fill in the definition of tagsTable"
taggedArticlesTable :: OE.Table TaggedArticleR TaggedArticleR
taggedArticlesTable = error "Fill in the definition of taggedArticlesTable"
-- | Query all tags.
allTagsQ :: OE.Select TagR
allTagsQ = OE.selectTable tagsTable
-- | Query all article-tag relations.
allTaggedArticlesQ :: OE.Select TaggedArticleR
allTaggedArticlesQ = OE.selectTable taggedArticlesTable
-- | Join article-ids and tag names for all articles.
articleTagNamesQ :: OE.Select (F OE.SqlInt8, F OE.SqlText)
articleTagNamesQ = proc () -> do
TaggedArticle {articleFk = aId, tagFk = tFk} <- allTaggedArticlesQ -< ()
Tag {tagKey = tId, tagName = tn} <- allTagsQ -< ()
OE.restrict -< tFk OE..=== tId -- INNER JOIN ON
returnA -< (aId, tn)
-- | Aggregate all tag names for all articles
articleTagsQ :: OE.Select (F OE.SqlInt8, F (OE.SqlArray OE.SqlText))
articleTagsQ =
OE.aggregate (PP.p2 (OE.groupBy, OE.arrayAgg)) articleTagNamesQhttps://stackoverflow.com/questions/63191868
复制相似问题