Как аннотировать набор запросов django с помощью StringAgg или ArrayAgg, объединяющих один столбец из нескольких дочерних строк? - PullRequest
0 голосов
/ 11 июля 2019

Документы - это родительская таблица.Paragraphs - это дочерняя таблица.

Пользователи фильтруют документы на основе различных критериев поиска.Затем я хочу аннотировать документы с определенными параграфами, отфильтрованными по текстовому запросу.Тот же текстовый запрос используется для фильтрации документов и ранжирования их (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 голосов
/ 11 июля 2019

Мы можем аннотировать и упорядочивать документы с количеством абзацев, соответствующих запросу, с помощью аннотировать с суммой, регистром и когда

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'
))

Затем вы можете перебирать документы в ранжированном порядке, и они будут иметь атрибут «Match_paragraphs», который содержит все соответствующие параграфы

...