From 1038836a725ec19026cb49a15a22a7e6c107dc24 Mon Sep 17 00:00:00 2001 From: Yohan Boniface Date: Thu, 11 May 2023 11:33:08 +0200 Subject: [PATCH] Use Django full text instead of custom SQL --- docs/install.md | 9 ++++++++- umap/settings/base.py | 2 +- umap/settings/local.py.sample | 5 ----- umap/tests/test_map_views.py | 10 ++++++++++ umap/views.py | 15 ++++++++------- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/docs/install.md b/docs/install.md index 02f13236..1aa9c8e3 100644 --- a/docs/install.md +++ b/docs/install.md @@ -82,8 +82,15 @@ Start the server UMap uses PostgreSQL tsvector for searching. In case your database is big, you may want to add an index. For that, you should do so: + # Create a basic search configuration + CREATE TEXT SEARCH CONFIGURATION umapdict (COPY=simple); + + # If you also want to deal with accents and case, add this before creating the index CREATE EXTENSION unaccent; CREATE EXTENSION btree_gin; - CREATE TEXT SEARCH CONFIGURATION umapdict (COPY=simple); ALTER TEXT SEARCH CONFIGURATION umapdict ALTER MAPPING FOR hword, hword_part, word WITH unaccent, simple; + + # Now create the index CREATE INDEX IF NOT EXISTS search_idx ON umap_map USING GIN(to_tsvector('umapdict', name), share_status); + +And change `UMAP_SEARCH_CONFIGURATION = "umapdict"` in your settings. diff --git a/umap/settings/base.py b/umap/settings/base.py index 07bb2270..23881b68 100644 --- a/umap/settings/base.py +++ b/umap/settings/base.py @@ -215,7 +215,7 @@ UMAP_DEMO_SITE = False UMAP_EXCLUDE_DEFAULT_MAPS = False UMAP_MAPS_PER_PAGE = 5 UMAP_MAPS_PER_PAGE_OWNER = 10 -UMAP_USE_UNACCENT = False +UMAP_SEARCH_CONFIGURATION = "simple" UMAP_FEEDBACK_LINK = "https://wiki.openstreetmap.org/wiki/UMap#Feedback_and_help" # noqa USER_MAPS_URL = 'user_maps' DATABASES = { diff --git a/umap/settings/local.py.sample b/umap/settings/local.py.sample index f50c3d1a..a22a7dff 100644 --- a/umap/settings/local.py.sample +++ b/umap/settings/local.py.sample @@ -94,11 +94,6 @@ SHORT_SITE_URL = "http://s.hort" # POSTGIS_VERSION = (2, 1, 0) EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' -# You need to unable accent extension before using UMAP_USE_UNACCENT -# python manage.py dbshell -# CREATE EXTENSION unaccent; -UMAP_USE_UNACCENT = False - # Put the site in readonly mode (useful for migration or any maintenance) UMAP_READONLY = False diff --git a/umap/tests/test_map_views.py b/umap/tests/test_map_views.py index 8c2b2279..aa237517 100644 --- a/umap/tests/test_map_views.py +++ b/umap/tests/test_map_views.py @@ -529,3 +529,13 @@ def test_create_readonly(client, user, post_data, settings): response = client.post(url, post_data) assert response.status_code == 403 assert response.content == b'Site is readonly for maintenance' + + +def test_search(client, map): + # Very basic search, that do not deal with accent nor case. + # See install.md for how to have a smarter dict + index. + map.name = "Blé dur" + map.save() + url = reverse("search") + response = client.get(url + "?q=Blé") + assert "Blé dur" in response.content.decode() diff --git a/umap/views.py b/umap/views.py index df17c18b..1228853e 100644 --- a/umap/views.py +++ b/umap/views.py @@ -10,6 +10,7 @@ from django.contrib import messages from django.contrib.auth import logout as do_logout from django.contrib.auth import get_user_model from django.contrib.gis.measure import D +from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector from django.core.paginator import EmptyPage, PageNotAnInteger, Paginator from django.core.signing import BadSignature, Signer from django.core.validators import URLValidator, ValidationError @@ -190,13 +191,13 @@ class Search(TemplateView, PaginatorMixin): q = self.request.GET.get("q") results = [] if q: - where = "to_tsvector(name) @@ websearch_to_tsquery(%s)" - if getattr(settings, "UMAP_USE_UNACCENT", False): - where = "to_tsvector('umapdict',name) @@ websearch_to_tsquery('umapdict',%s)" # noqa - results = Map.objects.filter(share_status=Map.PUBLIC) - results = results.extra(where=[where], params=[q]) - results = results.order_by("-modified_at") - results = self.paginate(results) + vector = SearchVector("name", config=settings.UMAP_SEARCH_CONFIGURATION) + query = SearchQuery( + q, config=settings.UMAP_SEARCH_CONFIGURATION, search_type="websearch" + ) + qs = Map.objects.annotate(search=vector).filter(search=query) + qs = qs.filter(share_status=Map.PUBLIC).order_by('-modified_at') + results = self.paginate(qs) kwargs.update({"maps": results, "q": q}) return kwargs