Use Django full text instead of custom SQL

This commit is contained in:
Yohan Boniface 2023-05-11 11:33:08 +02:00
parent 005a759b81
commit 1038836a72
5 changed files with 27 additions and 14 deletions

View file

@ -82,8 +82,15 @@ Start the server
UMap uses PostgreSQL tsvector for searching. In case your database is big, you 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: 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 unaccent;
CREATE EXTENSION btree_gin; 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; 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); 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.

View file

@ -215,7 +215,7 @@ UMAP_DEMO_SITE = False
UMAP_EXCLUDE_DEFAULT_MAPS = False UMAP_EXCLUDE_DEFAULT_MAPS = False
UMAP_MAPS_PER_PAGE = 5 UMAP_MAPS_PER_PAGE = 5
UMAP_MAPS_PER_PAGE_OWNER = 10 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 UMAP_FEEDBACK_LINK = "https://wiki.openstreetmap.org/wiki/UMap#Feedback_and_help" # noqa
USER_MAPS_URL = 'user_maps' USER_MAPS_URL = 'user_maps'
DATABASES = { DATABASES = {

View file

@ -94,11 +94,6 @@ SHORT_SITE_URL = "http://s.hort"
# POSTGIS_VERSION = (2, 1, 0) # POSTGIS_VERSION = (2, 1, 0)
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 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) # Put the site in readonly mode (useful for migration or any maintenance)
UMAP_READONLY = False UMAP_READONLY = False

View file

@ -529,3 +529,13 @@ def test_create_readonly(client, user, post_data, settings):
response = client.post(url, post_data) response = client.post(url, post_data)
assert response.status_code == 403 assert response.status_code == 403
assert response.content == b'Site is readonly for maintenance' 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()

View file

@ -10,6 +10,7 @@ from django.contrib import messages
from django.contrib.auth import logout as do_logout from django.contrib.auth import logout as do_logout
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.contrib.gis.measure import D 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.paginator import EmptyPage, PageNotAnInteger, Paginator
from django.core.signing import BadSignature, Signer from django.core.signing import BadSignature, Signer
from django.core.validators import URLValidator, ValidationError from django.core.validators import URLValidator, ValidationError
@ -190,13 +191,13 @@ class Search(TemplateView, PaginatorMixin):
q = self.request.GET.get("q") q = self.request.GET.get("q")
results = [] results = []
if q: if q:
where = "to_tsvector(name) @@ websearch_to_tsquery(%s)" vector = SearchVector("name", config=settings.UMAP_SEARCH_CONFIGURATION)
if getattr(settings, "UMAP_USE_UNACCENT", False): query = SearchQuery(
where = "to_tsvector('umapdict',name) @@ websearch_to_tsquery('umapdict',%s)" # noqa q, config=settings.UMAP_SEARCH_CONFIGURATION, search_type="websearch"
results = Map.objects.filter(share_status=Map.PUBLIC) )
results = results.extra(where=[where], params=[q]) qs = Map.objects.annotate(search=vector).filter(search=query)
results = results.order_by("-modified_at") qs = qs.filter(share_status=Map.PUBLIC).order_by('-modified_at')
results = self.paginate(results) results = self.paginate(qs)
kwargs.update({"maps": results, "q": q}) kwargs.update({"maps": results, "q": q})
return kwargs return kwargs