4

I need a search that searches multiple fields and returns a "headline" which highlights the matching words. My understanding is that SearchVector is the appropriate choice for searching across multiple fields. But all of the examples I've seen of SearchHeadline use only a single field! What's the best way to use SearchHeadline with multiple fields? For example, this works:

return (
    Author.objects
    .annotate(search_vectors=SearchVector('name', 'location'), )
    .filter(search_vectors=SearchQuery(search_string))
)

Easy. So the next step is to add SearchHeadline... Here was my guess, but it causes an error in PostgreSQL:

return (
    Author.objects
    .annotate(search_vectors=SearchVector('name', 'location'), )
    .annotate(headline=SearchHeadline(
        SearchVector('name', 'location'),
        SearchQuery(search_string),
        start_sel='<strong>', stop_sel='</strong>'))
    .filter(search_vectors=SearchQuery(search_string))
)

Error:

Traceback (most recent call last):
  File "/vagrant/venv/lib/python3.8/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
psycopg2.errors.UndefinedFunction: function ts_headline(tsvector, tsquery, unknown) does not exist
LINE 1: ...app_author"."location", '')) AS "search_vectors", ts_headlin...
                                                             ^
HINT:  No function matches the given name and argument types. You might need to add explicit type casts.

Is SearchVector() not a valid expression?

Using Concat() gets the job done, but this doesn't allow me to leverage the built in indexing capabilities of a ts_vector. It feels like a hack solution to me.

return (
    Author.objects
    .annotate(search_vectors=SearchVector('name', 'location'), )
    .annotate(headline=SearchHeadline(
        Concat(F('name'), Value(' '), F('location')),
        SearchQuery(search_string),
        start_sel='<strong>', stop_sel='</strong>'))
    .filter(search_vectors=SearchQuery(search_string))
)

What's the best way to do this?

Tristan
  • 1,730
  • 3
  • 20
  • 25

0 Answers0