Django ORM поиск в тексте по ключевым словам с подзапросом - PullRequest
0 голосов
/ 16 октября 2018

У меня есть две модели в Джанго.Модель статьи с заголовком поля и модель ключевого слова с заголовком поля.Мне нужно сделать запрос к БД и отфильтровать только те статьи, название которых содержит любое ключевое слово из модели ключевых слов.Я пытаюсь выполнить поиск с помощью Subquery, но он не работает.

Если я пытаюсь filter идентификаторы с in, это работает:

from django.db.models import Subquery

Article.objects.filter(id__in=Subquery(Keyword.objects.values('id')))

Но если я пытаюсь filter с icontains это не работает:

Article.objects.filter(title__icontains=Subquery(Keyword.objects.values('title')))

Последний запрос возвращает пустой набор запросов Как это исправить?

1 Ответ

0 голосов
/ 16 октября 2018

Самый простой способ сделать это с двумя запросами: один запрос для получения всех Keyword с, затем один, который фильтрует Article с этим ключевым словом, например:

from functools import reduce
from operator import or_

kws = Keyword.objects.values_list('title', flat=True)
articles = Article.objects.filter(
    reduce(or_, [Q(title__icontains=kw) for kw in kws])
)

.Однако приведенное выше может привести к большому запросу, поскольку мы сначала загружаем все заголовки.

Альтернативой является создание JOIN с .extra, а затем выполнение «необработанной» аннотации SQL-запроса:

Article.objects.extra(
    tables=[Keyword._meta.db_table]
).annotate(
    key_title=RawSQL('{}.{}'.format(Keyword._meta.db_table, Keyword.title.field_name), ())
).filter(title__icontains=F('key_title'))

В результате будет получен запрос, подобный следующему:

SELECT article.id, article.title, (keyword.title) AS key_title
FROM article , keyword
WHERE article.title LIKE CONCAT('%', REPLACE(REPLACE(REPLACE(((keyword.title)), '\\\\', '\\\\\\\\'), '%', '\\%'), '_', '\\_'), '%')

REPLACE(..) части находятся здесь, чтобы экранировать заголовок ключевого слова, поскольку, если он содержит %, это может привести к «подстановочному знаку» в выражении LIKE.Это все введено поиском __icontains, поэтому нам не нужно беспокоиться об этом.

...