Support searching `none()` querysets ()

pull/12875/head
Matt Westcott 2025-02-13 00:44:31 +00:00
rodzic e01aaed00c
commit 88abe5872c
9 zmienionych plików z 31 dodań i 2 usunięć
docs/releases
wagtail/search

Wyświetl plik

@ -22,7 +22,7 @@ Changelog
~~~~~~~~~~~~~~~~~~
* Fix: Prevent error when filtering by locale and searching with Elasticsearch (Sage Abdullah)
* Fix: Support searching `none()` querysets (Matt Westcott)
6.4 (03.02.2025)
~~~~~~~~~~~~~~~~

Wyświetl plik

@ -14,3 +14,4 @@ depth: 1
### Bug fixes
* Prevent error when filtering by locale and searching with Elasticsearch (Sage Abdullah)
* Support searching `none()` querysets (Matt Westcott)

Wyświetl plik

@ -5,7 +5,7 @@ from django.db.models.functions.datetime import Extract as ExtractDate
from django.db.models.functions.datetime import ExtractYear
from django.db.models.lookups import Lookup
from django.db.models.query import QuerySet
from django.db.models.sql.where import SubqueryConstraint, WhereNode
from django.db.models.sql.where import NothingNode, SubqueryConstraint, WhereNode
from wagtail.search.index import class_is_indexed, get_indexed_models
from wagtail.search.query import MATCH_ALL, PlainText
@ -69,6 +69,9 @@ class BaseSearchQueryCompiler:
def _process_lookup(self, field, lookup, value):
raise NotImplementedError
def _process_match_none(self):
raise NotImplementedError
def _connect_filters(self, filters, connector, negated):
raise NotImplementedError
@ -179,6 +182,9 @@ class BaseSearchQueryCompiler:
field_attname, lookup, value, check_only=check_only
)
elif isinstance(where_node, NothingNode):
return self._process_match_none()
elif isinstance(where_node, SubqueryConstraint):
raise FilterError(
"Could not apply filter on search results: Subqueries are not allowed."

Wyświetl plik

@ -56,6 +56,9 @@ class DatabaseSearchQueryCompiler(BaseSearchQueryCompiler):
**{field.get_attname(self.queryset.model) + "__" + lookup: value}
)
def _process_match_none(self):
return models.Q(pk__in=[])
def _connect_filters(self, filters, connector, negated):
if connector == "AND":
q = models.Q(*filters)

Wyświetl plik

@ -522,6 +522,9 @@ class MySQLSearchQueryCompiler(BaseSearchQueryCompiler):
lhs = field.get_attname(self.queryset.model) + "__" + lookup
return Q(**{lhs: value})
def _process_match_none(self):
return Q(pk__in=[])
def _connect_filters(self, filters, connector, negated):
if connector == "AND":
q = Q(*filters)

Wyświetl plik

@ -561,6 +561,9 @@ class PostgresSearchQueryCompiler(BaseSearchQueryCompiler):
lhs = field.get_attname(self.queryset.model) + "__" + lookup
return Q(**{lhs: value})
def _process_match_none(self):
return Q(pk__in=[])
def _connect_filters(self, filters, connector, negated):
if connector == "AND":
q = Q(*filters)

Wyświetl plik

@ -580,6 +580,9 @@ class SQLiteSearchQueryCompiler(BaseSearchQueryCompiler):
lhs = field.get_attname(self.queryset.model) + "__" + lookup
return Q(**{lhs: value})
def _process_match_none(self):
return Q(pk__in=[])
def _connect_filters(self, filters, connector, negated):
if connector == "AND":
q = Q(*filters)

Wyświetl plik

@ -565,6 +565,9 @@ class Elasticsearch7SearchQueryCompiler(BaseSearchQueryCompiler):
}
}
def _process_match_none(self):
return {"bool": {"mustNot": {"match_all": {}}}}
def _connect_filters(self, filters, connector, negated):
if filters:
if len(filters) == 1:

Wyświetl plik

@ -638,6 +638,13 @@ class BackendTests(WagtailTestUtils):
],
)
def test_filter_none(self):
results = self.backend.search(MATCH_ALL, models.Book.objects.none())
self.assertListEqual(list(results), [])
results = self.backend.search("JavaScript", models.Book.objects.none())
self.assertListEqual(list(results), [])
# FACET TESTS
def test_facet(self):