From 85bcd5a176f54b628e6022511a25c10345fd522f Mon Sep 17 00:00:00 2001 From: dnsl48 Date: Sat, 29 Apr 2017 12:37:48 +0200 Subject: [PATCH] [Postgres search] Orders by ranking in a more reliable way. PostgreSQL does not guarantee the outer queries having the same ordering as subqueries after joining other tables. For more details see: https://www.postgresql.org/message-id/4CA16EE8.5040406@postnewspapers.com.au --- wagtail/contrib/postgres_search/backend.py | 6 +++++- wagtail/contrib/postgres_search/models.py | 8 +++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/wagtail/contrib/postgres_search/backend.py b/wagtail/contrib/postgres_search/backend.py index dcec8f7afb..7c5ec575e7 100644 --- a/wagtail/contrib/postgres_search/backend.py +++ b/wagtail/contrib/postgres_search/backend.py @@ -248,13 +248,17 @@ class PostgresSearchQuery(BaseSearchQuery): index_entries = self.get_in_index_queryset(queryset, search_query) if self.order_by_relevance: index_entries = index_entries.rank(search_query) - index_sql, index_params = get_sql(index_entries.pks()) + index_sql, index_params = get_sql( + index_entries.annotate_typed_pk() + .values('typed_pk', 'rank') + ) model_sql, model_params = get_sql(queryset) model = queryset.model sql = """ SELECT obj.* FROM (%s) AS index_entry INNER JOIN (%s) AS obj ON obj."%s" = index_entry.typed_pk + ORDER BY index_entry.rank DESC OFFSET %%s LIMIT %%s; """ % (index_sql, model_sql, get_pk_column(model)) limits = (start, None if stop is None else stop - start) diff --git a/wagtail/contrib/postgres_search/models.py b/wagtail/contrib/postgres_search/models.py index 8826d91288..85d7105789 100644 --- a/wagtail/contrib/postgres_search/models.py +++ b/wagtail/contrib/postgres_search/models.py @@ -35,14 +35,16 @@ class IndexQuerySet(QuerySet): def rank(self, search_query): return self.add_rank(search_query).order_by('-rank') - def pks(self): + def annotate_typed_pk(self): cast_field = self.model._meta.pk if isinstance(cast_field, BigAutoField): cast_field = BigIntegerField() elif isinstance(cast_field, AutoField): cast_field = IntegerField() - return (self.annotate(typed_pk=Cast('object_id', cast_field)) - .values_list('typed_pk', flat=True)) + return self.annotate(typed_pk=Cast('object_id', cast_field)) + + def pks(self): + return self.annotate_typed_pk().values_list('typed_pk', flat=True) @python_2_unicode_compatible