mirror of
https://code.eliotberriot.com/funkwhale/funkwhale.git
synced 2025-10-06 10:19:55 +02:00
Revert "Revert "Fix #994: use PostgreSQL full-text-search""
This reverts commit 7b0db234e2
.
This commit is contained in:
parent
c2cb510eb9
commit
57949c02c1
14 changed files with 368 additions and 38 deletions
|
@ -1,5 +1,6 @@
|
|||
import django_filters
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.core.serializers.json import DjangoJSONEncoder
|
||||
from django.db import models
|
||||
|
||||
|
@ -33,12 +34,18 @@ def privacy_level_query(user, lookup_field="privacy_level", user_field="user"):
|
|||
class SearchFilter(django_filters.CharFilter):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.search_fields = kwargs.pop("search_fields")
|
||||
self.fts_search_fields = kwargs.pop("fts_search_fields", [])
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def filter(self, qs, value):
|
||||
if not value:
|
||||
return qs
|
||||
query = search.get_query(value, self.search_fields)
|
||||
if settings.USE_FULL_TEXT_SEARCH and self.fts_search_fields:
|
||||
query = search.get_fts_query(
|
||||
value, self.fts_search_fields, model=self.parent.Meta.model
|
||||
)
|
||||
else:
|
||||
query = search.get_query(value, self.search_fields)
|
||||
return qs.filter(query)
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import re
|
||||
|
||||
from django.contrib.postgres.search import SearchQuery
|
||||
from django.db.models import Q
|
||||
|
||||
from . import utils
|
||||
|
||||
|
||||
QUERY_REGEX = re.compile(r'(((?P<key>\w+):)?(?P<value>"[^"]+"|[\S]+))')
|
||||
|
||||
|
@ -56,6 +59,41 @@ def get_query(query_string, search_fields):
|
|||
return query
|
||||
|
||||
|
||||
def get_fts_query(query_string, fts_fields=["body_text"], model=None):
|
||||
if query_string.startswith('"') and query_string.endswith('"'):
|
||||
# we pass the query directly to the FTS engine
|
||||
query_string = query_string[1:-1]
|
||||
else:
|
||||
parts = query_string.replace(":", "").split(" ")
|
||||
parts = ["{}:*".format(p) for p in parts if p]
|
||||
if not parts:
|
||||
return Q(pk=None)
|
||||
|
||||
query_string = "&".join(parts)
|
||||
|
||||
if not fts_fields or not query_string.strip():
|
||||
return Q(pk=None)
|
||||
query = None
|
||||
for field in fts_fields:
|
||||
if "__" in field and model:
|
||||
# When we have a nested lookup, we switch to a subquery for enhanced performance
|
||||
fk_field_name, lookup = (
|
||||
field.split("__")[0],
|
||||
"__".join(field.split("__")[1:]),
|
||||
)
|
||||
fk_field = model._meta.get_field(fk_field_name)
|
||||
related_model = fk_field.related_model
|
||||
subquery = related_model.objects.filter(
|
||||
**{lookup: SearchQuery(query_string, search_type="raw")}
|
||||
).values_list("pk", flat=True)
|
||||
new_query = Q(**{"{}__in".format(fk_field_name): list(subquery)})
|
||||
else:
|
||||
new_query = Q(**{field: SearchQuery(query_string, search_type="raw")})
|
||||
query = utils.join_queries_or(query, new_query)
|
||||
|
||||
return query
|
||||
|
||||
|
||||
def filter_tokens(tokens, valid):
|
||||
return [t for t in tokens if t["key"] in valid]
|
||||
|
||||
|
|
|
@ -234,3 +234,10 @@ def get_updated_fields(conf, data, obj):
|
|||
final_data[obj_field] = data_value
|
||||
|
||||
return final_data
|
||||
|
||||
|
||||
def join_queries_or(left, right):
|
||||
if left:
|
||||
return left | right
|
||||
else:
|
||||
return right
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue