wagtail: PostgreSQL partial_match not working in PageChooserPanel and SnippetChooserPanel

We just updated to Wagtail 2.15.1 and updated to use new database search backend. Previously we have used wagtail.contrib.postgres_search.backend and partial match has never worked us, thus we have actually overridden search for PageChooserPanel and SnippetChooserPanel with our own implementation to make the search usable without using Elasticsearch. In the newest 2.15.1 there is no mention that partial match isn’t supported with PostgreSQL, so I assume there is a bug, or then documentation should be updated.

Issue Summary

We have an ArticlePage model which is using Wagtail’s default search_fields, for ‘title’ as following:

index.SearchField('title', partial_match=True, boost=2),
index.AutocompleteField('title'),

When trying to select ArticlePage using PageChooserPanel, search will not match partial words, for example page with title “Hello world” will only match with “Hello” or “world”, but not with “Hel”. The same is true for SnippetChooserPanel when selecting snippets. Autocomplete works, for example if there are “Hi world” and “Hello world” pages, searching “world” will show both pages immediately when “world” is written, but nothing with “worl” like expected.

To overcome this problem, we have patched Wagtail’s default search with our own search implementation, us such:

def search(search_query, items, model):
   """Search override for Wagtail's default search to support partial matching"""
   conditions = Q()
   for term in search_query.split():
       for field in model.search_fields:
           # Ensure only SearchFields are used for filtering
           if isinstance(field, index.SearchField):
               conditions |= Q(**{'%s__icontains' % field.field_name: term})
   return items.filter(conditions)

With that override, the search for PageChooserPanel and SnippetChooserPanel works as expected what comes to partial matching, but this doesn’t support other features like boost. Still, this gives us much better user experience, but we would like to get rid of our ugly hacks.

Maybe we are missing something or then PostgreSQL really doesn’t support partial matching. In either case, I feel like documentation should be improved to make it clear whether it works or how to make it work.

Technical details

  • Python version: Python 3.9.4
  • Django version: 3.2.6
  • Wagtail version: 2.15.1
  • Browser version: Safari 15

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 3
  • Comments: 17 (10 by maintainers)

Commits related to this issue

Most upvoted comments

I was having the same issue and looks like this is happening because Lexeme prefix is False. The Lexeme prefix option is set by the class constant LAST_TERM_IS_PREFIX, and there is no option to change this behavior.

My dirty hack/fix:

from wagtail.search.backends.database.postgres.postgres import PostgresSearchQueryCompiler
PostgresSearchQueryCompiler.LAST_TERM_IS_PREFIX = True

@gasman Don’t really have an opinion either way on what the default should be, and your points in support of exact matching make a lot of sense to me. However, as someone who just lost an afternoon trying to figure this out before finding this issue, I’d like to reiterate @m5seppal’s original point that Wagtail’s default behavior really should be documented properly.

As it stands:

  • The default behaviour varies by backend: elasticsearch does partial matching while database doesn’t, yet this is documented nowhere
  • Instead, search/indexing.html creates the expectation that partial matching wil just work:

    “partial_match (boolean) - Setting this to true allows results to be matched on parts of words. For example, this is set on the title field by default, so a page titled Hello World! will be found if the user only types Hel into the search box.”

This is fixed for the PostgreSQL search backend in #9900. Autocompletion does not currently work at all on the sqlite or mysql FTS backends, and so these retain the existing non-autocompleting search - #9903 has been opened to cover this.