Skip to content

Commit

Permalink
Fix autocompletion on sqlite FTS backend
Browse files Browse the repository at this point in the history
* Fix incorrect signatures for `SQLiteAutocompleteQueryCompiler`
* Syntax for an autocomplete search should have the `*` outside of quotes - `"foo"*` rather than `"foo*"`
* Query compiler was hardcoded to search on the 'title' and 'body' fields of the FTS table, rather than 'autocomplete'

Full support for searching on specified fields is still lacking - the 'title' and 'body' fields are special cased, and other column names will return a SQL error.
  • Loading branch information
gasman committed Apr 18, 2023
1 parent 5c65ac9 commit cc223da
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 36 deletions.
12 changes: 4 additions & 8 deletions wagtail/search/backends/database/sqlite/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,16 +71,12 @@ def __init__(self, value, output_field=None, *, prefix=False, weight=None):
super().__init__(value, output_field=output_field)

def as_sql(self, compiler, connection):
param = "%s" % self.value.replace("'", "''").replace("\\", "\\\\")
param = self.value.replace("'", "''").replace("\\", "\\\\")

template = '"%s"'

label = ""
if self.prefix:
label += "*"

if label:
param = "{}{}".format(param, label)
template = '"%s"*'
else:
template = '"%s"'

return template, [param]

Expand Down
19 changes: 9 additions & 10 deletions wagtail/search/backends/database/sqlite/sqlite.py
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,7 @@ class SQLiteSearchQueryCompiler(BaseSearchQueryCompiler):
DEFAULT_OPERATOR = "AND"
LAST_TERM_IS_PREFIX = False
TARGET_SEARCH_FIELD_TYPE = SearchField
FTS_TABLE_FIELDS = ["title", "body"]

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
Expand Down Expand Up @@ -516,7 +517,7 @@ def search(self, config, start, stop, score_field=None):
) # We add the subsequent vectors to the combined vector.

# Build the FTS match expression.
expr = MatchExpression(self.fields or ["title", "body"], search_query)
expr = MatchExpression(self.fields or self.FTS_TABLE_FIELDS, search_query)
# Perform the FTS search. We'll get entries in the SQLiteFTSIndexEntry model.
objs = (
SQLiteFTSIndexEntry.objects.filter(expr)
Expand Down Expand Up @@ -589,19 +590,17 @@ def _connect_filters(self, filters, connector, negated):
class SQLiteAutocompleteQueryCompiler(SQLiteSearchQueryCompiler):
LAST_TERM_IS_PREFIX = True
TARGET_SEARCH_FIELD_TYPE = AutocompleteField
FTS_TABLE_FIELDS = ["autocomplete"]

def get_config(self, backend):
return backend.autocomplete_config

def get_search_fields_for_model(self):
return self.queryset.model.get_autocomplete_search_fields()

def get_index_vectors(self, search_query):
def get_index_vectors(self):
return [(F("index_entries__autocomplete"), 1.0)]

def get_fields_vectors(self, search_query):
raise NotImplementedError()


class SQLiteSearchResults(BaseSearchResults):
def get_queryset(self, for_count=False):
Expand Down Expand Up @@ -656,10 +655,7 @@ def facet(self, field_name):

class SQLiteSearchBackend(BaseSearchBackend):
query_compiler_class = SQLiteSearchQueryCompiler

# FIXME: the implementation of SQLiteAutocompleteQueryCompiler is incomplete -
# leave this undefined so that we get a clean NotImplementedError from BaseSearchBackend
# autocomplete_query_compiler_class = SQLiteAutocompleteQueryCompiler
autocomplete_query_compiler_class = SQLiteAutocompleteQueryCompiler

results_class = SQLiteSearchResults
rebuilder_class = SQLiteSearchRebuilder
Expand All @@ -668,7 +664,10 @@ class SQLiteSearchBackend(BaseSearchBackend):
def __init__(self, params):
super().__init__(params)
self.index_name = params.get("INDEX", "default")
self.config = params.get("SEARCH_CONFIG")

# SQLite backend currently has no config options
self.config = None
self.autocomplete_config = None

if params.get("ATOMIC_REBUILD", False):
self.rebuilder_class = self.atomic_rebuilder_class
Expand Down
19 changes: 1 addition & 18 deletions wagtail/search/tests/test_sqlite_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

from wagtail.search.backends.database.sqlite.utils import fts5_available
from wagtail.search.tests.test_backends import BackendTests
from wagtail.test.search import models


@unittest.skipUnless(
Expand Down Expand Up @@ -44,23 +43,7 @@ def test_annotate_score(self):
def test_annotate_score_with_slice(self):
return super().test_annotate_score_with_slice()

def test_autocomplete_raises_not_implemented_error(self):
with self.assertRaises(NotImplementedError):
self.backend.autocomplete("Py", models.Book)

@skip("The SQLite backend doesn't support autocomplete.")
def test_autocomplete(self):
return super().test_autocomplete()

@skip("The SQLite backend doesn't support autocomplete.")
def test_autocomplete_not_affected_by_stemming(self):
return super().test_autocomplete_not_affected_by_stemming()

@skip("The SQLite backend doesn't support autocomplete.")
def test_autocomplete_uses_autocompletefield(self):
return super().test_autocomplete_uses_autocompletefield()

@skip("The SQLite backend doesn't support autocomplete.")
@skip("The SQLite backend doesn't support searching on specified fields.")
def test_autocomplete_with_fields_arg(self):
return super().test_autocomplete_with_fields_arg()

Expand Down

0 comments on commit cc223da

Please sign in to comment.