Search code examples
djangowagtail

Adding index.FilterField for Page object in Wagtail?


I need to implement a language-specific search in Wagtail, so I modified the default search/views.py like this:

def search(request):
    search_query = request.GET.get("query", None)
    page = request.GET.get("page", 1)
    language = request.LANGUAGE_CODE

    if search_query:
        search_results = Page.objects.live().filter(locale__language_code=language).search(search_query)

The result is an error message:

Please add index.FilterField('language_code') to Page.search_fields

Adding index.FilterField('language_code') to my classes derived from Page does not work – so how can I add that filter field index to wagtailcore.Page?


Solution

  • You can also use Locale.get_active() as a shortcut to get the current language. It's worth looking at defer_streamfields(), this will reduce the amount of data you're loading into memory also.

    Page.objects.live().defer_streamfields().filter(locale=Locale.get_active()).search(search_query)
    

    If you're using the Wagtail search backend, it only supports one SEARCH_CONFIG even though you can add multiple in the settings. All that happens is each backend listed overwrites the previous and you end up with the last in the list. There is nothing in the wagtailsearch_indexentry table that allows for different backends.

    If you have the following:

    WAGTAILSEARCH_BACKENDS = {
        'default': {
          'BACKEND': 'wagtail.search.backends.database',
          'SEARCH_CONFIG': 'english_extended',
        },
        'es': {
          'BACKEND': 'wagtail.search.backends.database',
          'SEARCH_CONFIG': 'spanish',
        },
        'fr': {
          'BACKEND': 'wagtail.search.backends.database',
          'SEARCH_CONFIG': 'french',
        },
    }
    

    In this case, the search backend will always be the French one, even if you specify the Spanish backend in your code with

    from wagtail.search.backends import get_search_backend
    s = get_search_backend('es')
    scope = Page.objects.live().defer_streamfields().filter(locale=Locale.get_active())
    search_results = s.search(search_query, scope)
    

    s will be using the French SEARCH_CONFIG setting with French NLP (stemming and stop words) according to that dictionary instead.

    Best solution I've come up with is to use the simple dictionary and extend it with PostgrSQL unaccent support (adds full character set support for plain ascii, e.g. typing "hotel" will return results with "hôtel" etc.). https://enzedonline.com/en/tech-blog/how-to-add-unaccent-support-in-postgresql-search/#6-test-stemming-with-unaccent

    That disables the NLP, but at least makes it language neutral.

    I raised this as an issue in 2022, but it never got any traction.