0
0
mirror of https://github.com/wagtail/wagtail.git synced 2024-11-30 19:20:56 +01:00

Ensure PostgresAutocompleteQueryCompiler consistently uses AutocompleteFields

get_fields_vector relies on self.search_fields, which in the base PostgresSearchQueryCompiler constructor is a lookup of SearchFields - here we need to patch that behaviour to build a lookup of AutocompleteFields instead.
This commit is contained in:
Matt Westcott 2020-05-21 15:04:20 +01:00 committed by Matt Westcott
parent ade6b27f50
commit e5bd7d71f6
3 changed files with 32 additions and 7 deletions

View File

@ -55,6 +55,7 @@ class ObjectIndexer:
self.prepare_value(field.get_value(obj))) self.prepare_value(field.get_value(obj)))
elif isinstance(field, AutocompleteField): elif isinstance(field, AutocompleteField):
# AutocompleteField does not define a boost parameter, so use a base weight of 'D'
yield (field, 'D', self.prepare_value(field.get_value(obj))) yield (field, 'D', self.prepare_value(field.get_value(obj)))
elif isinstance(field, RelatedFields): elif isinstance(field, RelatedFields):
@ -255,26 +256,34 @@ class Index:
class PostgresSearchQueryCompiler(BaseSearchQueryCompiler): class PostgresSearchQueryCompiler(BaseSearchQueryCompiler):
DEFAULT_OPERATOR = 'and' DEFAULT_OPERATOR = 'and'
LAST_TERM_IS_PREFIX = False LAST_TERM_IS_PREFIX = False
TARGET_SEARCH_FIELD_TYPE = SearchField
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.search_fields = self.queryset.model.get_searchable_search_fields() local_search_fields = self.get_search_fields_for_model()
# Due to a Django bug, arrays are not automatically converted # Due to a Django bug, arrays are not automatically converted
# when we use WEIGHTS_VALUES. # when we use WEIGHTS_VALUES.
self.sql_weights = get_sql_weights() self.sql_weights = get_sql_weights()
if self.fields is not None: if self.fields is None:
search_fields = self.queryset.model.get_searchable_search_fields() # search over the fields defined on the current model
self.search_fields = local_search_fields
else:
# build a search_fields set from the passed definition,
# which may involve traversing relations
self.search_fields = { self.search_fields = {
field_lookup: self.get_search_field(field_lookup, fields=search_fields) field_lookup: self.get_search_field(field_lookup, fields=local_search_fields)
for field_lookup in self.fields for field_lookup in self.fields
} }
def get_config(self, backend): def get_config(self, backend):
return backend.config return backend.config
def get_search_fields_for_model(self):
return self.queryset.model.get_searchable_search_fields()
def get_search_field(self, field_lookup, fields=None): def get_search_field(self, field_lookup, fields=None):
if fields is None: if fields is None:
fields = self.search_fields fields = self.search_fields
@ -285,7 +294,7 @@ class PostgresSearchQueryCompiler(BaseSearchQueryCompiler):
sub_field_name = None sub_field_name = None
for field in fields: for field in fields:
if isinstance(field, SearchField) and field.field_name == field_lookup: if isinstance(field, self.TARGET_SEARCH_FIELD_TYPE) and field.field_name == field_lookup:
return field return field
# Note: Searching on a specific related field using # Note: Searching on a specific related field using
@ -465,10 +474,14 @@ class PostgresSearchQueryCompiler(BaseSearchQueryCompiler):
class PostgresAutocompleteQueryCompiler(PostgresSearchQueryCompiler): class PostgresAutocompleteQueryCompiler(PostgresSearchQueryCompiler):
LAST_TERM_IS_PREFIX = True LAST_TERM_IS_PREFIX = True
TARGET_SEARCH_FIELD_TYPE = AutocompleteField
def get_config(self, backend): def get_config(self, backend):
return backend.autocomplete_config return backend.autocomplete_config
def get_search_fields_for_model(self):
return self.queryset.model.get_autocomplete_search_fields()
def get_index_vector(self, search_query): def get_index_vector(self, search_query):
return F('index_entries__autocomplete') return F('index_entries__autocomplete')
@ -477,10 +490,9 @@ class PostgresAutocompleteQueryCompiler(PostgresSearchQueryCompiler):
SearchVector( SearchVector(
field_lookup, field_lookup,
config=search_query.config, config=search_query.config,
weight=get_weight(search_field.boost) weight='D',
) )
for field_lookup, search_field in self.search_fields.items() for field_lookup, search_field in self.search_fields.items()
if search_field.partial_match
) )

View File

@ -187,6 +187,18 @@ class BackendTests(WagtailTestUtils):
"Learning Python", "Learning Python",
]) ])
# Autocomplete should only require an AutocompleteField, not a SearchField with
# partial_match=True
results = self.backend.autocomplete("Georg", models.Author)
self.assertUnsortedListEqual([r.name for r in results], [
"George R.R. Martin",
])
results = self.backend.autocomplete("Georg", models.Author, fields=['name'])
self.assertUnsortedListEqual([r.name for r in results], [
"George R.R. Martin",
])
def test_autocomplete_not_affected_by_stemming(self): def test_autocomplete_not_affected_by_stemming(self):
# If SEARCH_CONFIG is set, stemming will be enabled. # If SEARCH_CONFIG is set, stemming will be enabled.
# But we want to disable this for autocomplete as stemmed words don't always match on prefixes # But we want to disable this for autocomplete as stemmed words don't always match on prefixes

View File

@ -10,6 +10,7 @@ class Author(index.Indexed, models.Model):
search_fields = [ search_fields = [
index.SearchField('name'), index.SearchField('name'),
index.AutocompleteField('name'),
index.FilterField('date_of_birth'), index.FilterField('date_of_birth'),
] ]