From 16d2b3625ebf3a413ff345d2207024f8e93f85f5 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Sat, 16 Jan 2021 15:33:45 +0100 Subject: [PATCH 2/4] authentic2: add full text search to AttributeValue (#49957) --- src/authentic2/managers.py | 4 ++ .../0031_add_search_vector_to_attributes.py | 45 +++++++++++++++++++ .../0032_initialize_search_vectors.py | 19 ++++++++ src/authentic2/models.py | 6 +++ 4 files changed, 74 insertions(+) create mode 100644 src/authentic2/migrations/0031_add_search_vector_to_attributes.py create mode 100644 src/authentic2/migrations/0032_initialize_search_vectors.py diff --git a/src/authentic2/managers.py b/src/authentic2/managers.py index 6efba54c..c90d6658 100644 --- a/src/authentic2/managers.py +++ b/src/authentic2/managers.py @@ -23,6 +23,7 @@ from django.db.models.query import QuerySet from django.utils.timezone import now from django.conf import settings from django.contrib.contenttypes.models import ContentType +from django.contrib.postgres.search import SearchVector from django_rbac.utils import get_ou_model from model_utils import managers @@ -86,6 +87,9 @@ class AttributeValueQuerySet(QuerySet): raise AttributeValue.DoesNotExist return self.get(content_type=ct, object_id=owner.pk, attribute=at) + def update_search_vectors(self): + self.update(search_vector=SearchVector('content')) + class ServiceQuerySet(managers.InheritanceQuerySetMixin, GetBySlugQuerySet): pass diff --git a/src/authentic2/migrations/0031_add_search_vector_to_attributes.py b/src/authentic2/migrations/0031_add_search_vector_to_attributes.py new file mode 100644 index 00000000..2013407b --- /dev/null +++ b/src/authentic2/migrations/0031_add_search_vector_to_attributes.py @@ -0,0 +1,45 @@ +import django.contrib.postgres.indexes +import django.contrib.postgres.search +from django.db import migrations + + +def create_trigger(apps, schema_editor): + with schema_editor.connection.cursor() as cursor: + cursor.execute('SHOW default_text_search_config') + default_text_search_config, = cursor.fetchone() + cursor.execute('''CREATE OR REPLACE FUNCTION authentic2_update_atv_search_vector() RETURNS TRIGGER AS $$ +BEGIN + IF TG_OP = 'INSERT' OR (TG_OP = 'UPDATE' AND NEW.content <> OLD.content) THEN + NEW.search_vector = to_tsvector(NEW.content); + END IF; + RETURN NEW; +END; $$ LANGUAGE plpgsql''') + cursor.execute('''CREATE TRIGGER authentic2_attributevalue_search_vector_trigger +BEFORE INSERT OR UPDATE OF content +ON authentic2_attributevalue +FOR EACH ROW EXECUTE PROCEDURE authentic2_update_atv_search_vector()''') + + +def drop_trigger(apps, schema_editor): + with schema_editor.connection.cursor() as cursor: + cursor.execute('DROP TRIGGER authentic2_attributevalue_search_vector_trigger') + + +class Migration(migrations.Migration): + dependencies = [ + ('authentic2', '0030_clean_admin_tools_tables'), + ] + + operations = [ + migrations.AddField( + model_name='attributevalue', + name='search_vector', + field=django.contrib.postgres.search.SearchVectorField(editable=False, null=True), + ), + migrations.AddIndex( + model_name='attributevalue', + index=django.contrib.postgres.indexes.GinIndex(fields=['search_vector'], name='authentic2_atv_tsvector_idx') + ), + migrations.RunPython(create_trigger, drop_trigger), + + ] diff --git a/src/authentic2/migrations/0032_initialize_search_vectors.py b/src/authentic2/migrations/0032_initialize_search_vectors.py new file mode 100644 index 00000000..7426f5b0 --- /dev/null +++ b/src/authentic2/migrations/0032_initialize_search_vectors.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.17 on 2021-01-16 14:22 + +from django.contrib.postgres.search import SearchVector +from django.db import migrations + + +def initialize_search_vector(apps, schema_editor): + AttributeValue = apps.get_model('authentic2', 'AttributeValue') + AttributeValue.all_objects.update(search_vector=SearchVector('content')) + + +class Migration(migrations.Migration): + dependencies = [ + ('authentic2', '0031_add_search_vector_to_attributes'), + ] + + operations = [ + migrations.RunPython(initialize_search_vector, reverse_code=migrations.RunPython.noop), + ] diff --git a/src/authentic2/models.py b/src/authentic2/models.py index d914424f..12c39357 100644 --- a/src/authentic2/models.py +++ b/src/authentic2/models.py @@ -29,6 +29,8 @@ from django.utils.six.moves.urllib import parse as urlparse from django.core.exceptions import ValidationError from django.contrib.contenttypes.models import ContentType from django.contrib.postgres.fields import jsonb +from django.contrib.postgres.search import SearchVectorField +from django.contrib.postgres.indexes import GinIndex from model_utils.managers import QueryManager @@ -319,6 +321,7 @@ class AttributeValue(models.Model): multiple = models.BooleanField(default=False, null=True) content = models.TextField(verbose_name=_('content'), db_index=True) + search_vector = SearchVectorField(null=True, editable=False) verified = models.BooleanField(default=False) all_objects = managers.AttributeValueManager() @@ -341,6 +344,9 @@ class AttributeValue(models.Model): unique_together = ( ('content_type', 'object_id', 'attribute', 'multiple', 'content'), ) + indexes = [ + GinIndex(fields=['search_vector'], name='authentic2_atv_tsvector_idx'), + ] class PasswordReset(models.Model): -- 2.29.2