diff --git a/backend/timed/tracking/filters.py b/backend/timed/tracking/filters.py index 9f2408e9..58fa47ae 100644 --- a/backend/timed/tracking/filters.py +++ b/backend/timed/tracking/filters.py @@ -5,6 +5,7 @@ from functools import wraps from typing import TYPE_CHECKING +from django.contrib.postgres.search import SearchQuery from django.db.models import Q from django_filters.constants import EMPTY_VALUES from django_filters.rest_framework import ( @@ -113,7 +114,7 @@ class ReportFilterSet(FilterSet): user = NumberFilter(field_name="user_id") cost_center = NumberFilter(method="filter_cost_center") rejected = NumberFilter(field_name="rejected") - comment = CharFilter(field_name="comment", lookup_expr="search") + comment = CharFilter(method="filter_comment") def filter_has_reviewer( self, queryset: QuerySet[models.Report], _name: str, value: int @@ -231,6 +232,11 @@ def filter_cost_center( | Q(task__project__cost_center=value) & Q(task__cost_center__isnull=True) ) + def filter_comment( + self, queryset: QuerySet[models.Report], _name: str, value: str + ) -> QuerySet[models.Report]: + return queryset.filter(search_vector=SearchQuery(value, config="english")) + class Meta: """Meta information for the report filter set.""" diff --git a/backend/timed/tracking/migrations/0019_remove_report_search_vector_idx_report_search_vector_and_more.py b/backend/timed/tracking/migrations/0019_remove_report_search_vector_idx_report_search_vector_and_more.py new file mode 100644 index 00000000..5f1883bb --- /dev/null +++ b/backend/timed/tracking/migrations/0019_remove_report_search_vector_idx_report_search_vector_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.16 on 2024-09-17 08:50 + +import django.contrib.postgres.indexes +import django.contrib.postgres.search +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('tracking', '0018_report_search_vector_idx'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='report', + name='search_vector_idx', + ), + migrations.AddField( + model_name='report', + name='search_vector', + field=django.contrib.postgres.search.SearchVectorField(null=True), + ), + migrations.AddIndex( + model_name='report', + index=django.contrib.postgres.indexes.GinIndex(fields=['search_vector'], name='tracking_re_search__35ab7c_gin'), + ), + ] diff --git a/backend/timed/tracking/migrations/0020_report_search_vector_trigger.py b/backend/timed/tracking/migrations/0020_report_search_vector_trigger.py new file mode 100644 index 00000000..15fd883e --- /dev/null +++ b/backend/timed/tracking/migrations/0020_report_search_vector_trigger.py @@ -0,0 +1,22 @@ +from django.contrib.postgres.search import SearchVector +from django.db import migrations + + +def compute_search_vector(apps, schema_editor): + Report = apps.get_model("tracking", "Report") + Report.objects.update(search_vector=SearchVector("comment")) + + +class Migration(migrations.Migration): + dependencies = [ + ( + "tracking", + "0019_remove_report_search_vector_idx_report_search_vector_and_more", + ), + ] + + operations = [ + migrations.RunPython( + compute_search_vector, reverse_code=migrations.RunPython.noop + ), + ] diff --git a/backend/timed/tracking/models.py b/backend/timed/tracking/models.py index 92e242db..1044ed4d 100644 --- a/backend/timed/tracking/models.py +++ b/backend/timed/tracking/models.py @@ -7,7 +7,7 @@ from django.conf import settings from django.contrib.postgres.indexes import GinIndex -from django.contrib.postgres.search import SearchVector +from django.contrib.postgres.search import SearchVectorField from django.db import models from timed.utils import round_timedelta @@ -100,15 +100,14 @@ class Report(models.Model): rejected = models.BooleanField(default=False) remaining_effort = models.DurationField(default=timedelta(0), null=True) + search_vector = SearchVectorField(null=True) + class Meta: """Meta information for the report model.""" indexes = ( models.Index(fields=["date"]), - GinIndex( - SearchVector("comment", config="english"), - name="search_vector_idx", - ), + GinIndex(fields=["search_vector"]), ) def __str__(self) -> str: diff --git a/backend/timed/tracking/signals.py b/backend/timed/tracking/signals.py index cf1894e6..38660583 100644 --- a/backend/timed/tracking/signals.py +++ b/backend/timed/tracking/signals.py @@ -1,4 +1,5 @@ -from django.db.models import Sum +from django.contrib.postgres.search import SearchVector +from django.db.models import Sum, Value from django.db.models.signals import pre_save from django.dispatch import receiver @@ -15,6 +16,12 @@ def update_rejected_on_reports(sender, instance, **kwargs): # noqa: ARG001 instance.rejected = False +@receiver(pre_save, sender=Report) +def update_search_vector(sender, instance, **kwargs): # noqa: ARG001 + """Update comment search vector.""" + instance.search_vector = SearchVector(Value(instance.comment), config="english") + + @receiver(pre_save, sender=Report) def update_most_recent_remaining_effort(sender, instance, **kwargs): # noqa: ARG001 """Update remaining effort on task, if remaining effort tracking is active.