Эффективный способ поиска в базе данных, где значение поля экземпляра модели влияет на то, что возвращать

У меня есть база данных с 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
Вернуться на верх