Эффективный способ поиска в базе данных, где значение поля экземпляра модели влияет на то, что возвращать
У меня есть база данных с 2 таблицами данных: Сообщения и Аннотации. Ниже приведена краткая информация о моделях. Включены только важные поля.
message
text = models.CharField(max_length=250, blank=False)
accepted_annotation = models.OneToOneField(
to='Annotation',
related_name='original_msg',
on_delete=models.SET_DEFAULT,
default=None,
null=True
)
...other fields
annotation
message = models.ForeignKey(to=Message, on_delete=models.CASCADE)
user = models.ForeignKey(to=User, to_field='username',
on_delete=models.SET_NULL, null=True)
text = models.CharField(max_length=249, blank=False)
reviewed = models.BooleanField(default=False)
...other fields
Я хотел бы осуществлять поиск по тексту, а также по другим полям.
Сложность заключается в том, что в зависимости от того, кто запросил данные, для каждого сообщения в базе данных возвращается разная информация. Следующие части информации определяют, что будет возвращено:
- привилегия пользователя
- отмечена ли аннотация как рассмотренная
- существует ли принятая аннотация
- сделал ли пользователь аннотацию
Поэтому, когда я ищу по тексту, я не могу просто найти текст сообщения, потому что логика может определить, что для этого пользователя должна быть показана аннотация с совершенно другим текстом.
В настоящее время я использую предварительную выборку и аннотации к набору запросов для выполнения логики и заполнения поля "text_to_search" для каждого сообщения перед сортировкой и фильтрацией. Однако это требует подзапросов и будет очень медленным, когда количество сообщений достигнет миллионов.
Есть ли лучший способ поиска? Или кто-то может предложить совершенно другой дизайн таблицы?
Если вы хотите просто фильтровать сообщения, вы можете написать простой набор запросов, используя Q
объекты.
def get_queryset(self, search_term: str=""):
user = self.request.user
project = Project.objects.get(slug=self.kwargs['pj_slug'])
messages = Message.objects.filter(project=project).filter(
Q(
Q(text__icontains=search_term)
) | Q(
Q(annotation_set__user=user, annotation_set__text__icontains=search_term)
) | Q(
Q(annotation_set__reviewed=True, annotation_set__text__icontains=search_term)
) | Q(
Q(accepted_annotation__text__icontains=search_term)
)
)
return messages