0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-12-01 11:41:20 +01:00

[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
This commit is contained in:
dnsl48 2017-04-29 12:37:48 +02:00 committed by Bertrand Bordage
parent 260257d9a5
commit 85bcd5a176
2 changed files with 10 additions and 4 deletions

View File

@ -248,13 +248,17 @@ class PostgresSearchQuery(BaseSearchQuery):
index_entries = self.get_in_index_queryset(queryset, search_query) index_entries = self.get_in_index_queryset(queryset, search_query)
if self.order_by_relevance: if self.order_by_relevance:
index_entries = index_entries.rank(search_query) 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_sql, model_params = get_sql(queryset)
model = queryset.model model = queryset.model
sql = """ sql = """
SELECT obj.* SELECT obj.*
FROM (%s) AS index_entry FROM (%s) AS index_entry
INNER JOIN (%s) AS obj ON obj."%s" = index_entry.typed_pk INNER JOIN (%s) AS obj ON obj."%s" = index_entry.typed_pk
ORDER BY index_entry.rank DESC
OFFSET %%s LIMIT %%s; OFFSET %%s LIMIT %%s;
""" % (index_sql, model_sql, get_pk_column(model)) """ % (index_sql, model_sql, get_pk_column(model))
limits = (start, None if stop is None else stop - start) limits = (start, None if stop is None else stop - start)

View File

@ -35,14 +35,16 @@ class IndexQuerySet(QuerySet):
def rank(self, search_query): def rank(self, search_query):
return self.add_rank(search_query).order_by('-rank') return self.add_rank(search_query).order_by('-rank')
def pks(self): def annotate_typed_pk(self):
cast_field = self.model._meta.pk cast_field = self.model._meta.pk
if isinstance(cast_field, BigAutoField): if isinstance(cast_field, BigAutoField):
cast_field = BigIntegerField() cast_field = BigIntegerField()
elif isinstance(cast_field, AutoField): elif isinstance(cast_field, AutoField):
cast_field = IntegerField() cast_field = IntegerField()
return (self.annotate(typed_pk=Cast('object_id', cast_field)) return self.annotate(typed_pk=Cast('object_id', cast_field))
.values_list('typed_pk', flat=True))
def pks(self):
return self.annotate_typed_pk().values_list('typed_pk', flat=True)
@python_2_unicode_compatible @python_2_unicode_compatible