Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Highlight search terms on a Django/PostgreSQL search results page

How can I create a search results page in Django 1.11, using PostgreSQL full text search, where the terms searched for are highlighted?

like image 780
jmn Avatar asked Sep 24 '17 00:09

jmn


1 Answers

Even though Django doesn't support ts_headline feature from postgresql, You can manually apply it as a Function on a QuerySet to annotate:


We need additional function to operate with django ORM. Here is a sample for ts_headline. [original_source for this sample function is linked here]

Headline function sample:

from django.db import models
from django.contrib.postgres.search import Value, Func


class Headline(Func):
    function = 'ts_headline'

    def __init__(self, field, query, config=None, options=None, **extra):
        expressions = [field, query]
        if config:
            expressions.insert(0, Value(config))
        if options:
            expressions.append(Value(options))
        extra.setdefault('output_field', models.TextField())
        super().__init__(*expressions, **extra)

Using the above function you can use it on a QuerySet to annotate

Example Model Definition

class Video(Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    title = models.CharField(max_length=128, verbose_name="Title")

Steps for getting highlighted search results on model title

  1. Filter and get the QuerySet needed to annotated
  2. Annotate using Headline function
  3. Get Values of your document

Filtering Objects

Video.objects.filter(filter_query)

filter_query is a Q() over title filter_query = Q(title__contains=term)


Annotation with Headline data

Video.objects.filter(filter_query).annotate(title_highlight=Headline(F('title'), text_search_query))

ts_headline directly take the input from the document rather than from ts_vector, So we have to pass the information about which field it should access and what SearchQuery it should perform on it.

text_Search_query is SearchQuery Object with same input as the filter_query text_search_query = SearchQuery(term)

Now after annotation, this queryset with include a extra field in all objects called title_highlight which would contain the result you wanted like:

these <b>loans</b> not being repaired


Get the values from the annotation field

using values_list over the QuerySet you can get the values from these annotated fields.

final code:

Video.objects.filter(filter_query).annotate(title_highlight=Headline(F('title'), text_search_query)).values_from('title','title_highlight')
like image 146
StarLord Avatar answered Nov 01 '22 10:11

StarLord