如何使用StringAgg或ArrayAgg注释来自多个子行的一列来注释Django查询集?

时间:2019-07-10 21:53:04

标签: django postgresql annotate django-aggregation

Documents是父表。 段落是子表。

用户根据各种搜索条件过滤文档。 然后,我希望使用通过文本查询过滤的某些段落来注释文档。 相同的文本查询用于过滤文档并对其进行排名(SearchRank)。此排名使得有必要从文档开始并用段落注释,而不是从段落开始并按文档分组。

将段落中多行中的一个文本字段连接起来的postgresql方法如下:

SELECT array_to_string(
ARRAY(
SELECT paragraph.text
FROM paragraph
WHERE document id = '...'
ORDER BY paragraph.number),
', ');

我正在尝试将其翻译为Django编码。

我尝试了许多django方法,但无济于事。 我可以注释1段。 Query_sum是根据用户输入构建的Q()对象。

results = Documents.filter(Query_sum)

sub_paragraphs = Paragraphs.filter(Query_sum).filter(document=OuterRef('id'))

results = results.annotate(paragraphs=Subquery(sub_paragraphs.values('text')[:1], output_field=TextField()))

当我摆脱切片[:1]时问题就开始了。

results = results.annotate(paragraphs=Subquery(sub_paragraphs.values('text'), output_field=TextField()))

然后我得到以下错误: “子查询返回的多于一行用作表达式。”

要解决此问题,我尝试使用ArrayAgg和StringAgg。 我一团糟;-)

“文档”查询集(结果)应使用相关段落列表(ArrayAgg)或由任何定界符(StringAgg)分隔的一串段落来注释。

关于如何进行的任何想法?我将非常感谢

1 个答案:

答案 0 :(得分:0)

我们可以使用annotate with Sum, Case and When

注释和排序具有与查询匹配的段落数的文档。
documents = Document.objects.annotate(
    matches=Sum(Case(
        # This could depend on the related name for the paragraph -> document relationship
        When(paragraphs__text__icontains=search_string, then=Value(1)),
        default=Value(0),
        output_field=IntegerField(),
    )))
).order_by('-matches')

然后,要获取与每个文档的查询匹配的所有段落,我们使用prefetch_related。我们可以使用Prefetch对象来过滤预取操作

documents = documents.prefetch_related(Prefetch(
    'paragraphs',
    queryset=Paragraph.objects.filter(text__icontains=search_string),
    to_attrs='matching_paragraphs'
))

然后您可以按排名顺序遍历文档,它们将具有“ matching_paragraphs”属性,其中包含所有匹配的段落