user3541631
user3541631

Reputation: 4008

Do a full text search by using two models

I have two models Item and Owner:

class Item(models.Model):
    name = models.CharField(max_length=255)
    owner = models.ForeignKey(
        Owner, related_name='owner_items',
        on_delete=models.CASCADE,
    )
    is_featured = models.BooleanField(
        choices=CHOICES, default=BOOL_NO
    )
    # ...

I do a full-text search (based on Django docs) in the Item name and description fields. PostgreSQL version is 10.

search_vectors = (
    SearchVector('name', weight='A', config='english') +
    SearchVector('description', weight='B', config='english')
)
terms = [SearchQuery(term) for term in keyword.split()]
search_query = functools.reduce(operator.or_, terms)
search_rank = SearchRank(
    search_vectors, search_query, weights=[0.2, 0.4, 0.6, 1]
)
qs = Item.objects.all().annotate(
    rank=search_rank
).filter(rank__gte=0.2).order_by('-rank')

I want to introduce also the Owner, name field in the equation, also give a little boost to the rank if is_featured is true.

I have the Owner instance model before doing this search.

Upvotes: 1

Views: 499

Answers (1)

Paolo Melchiorre
Paolo Melchiorre

Reputation: 6122

You can add the field name from class Owner in the search_vector, maybe with a different weight and the same config (it's only an hypothesis because you didn't specified the Owner model definition or data).

A way to use is_featured as a boost for your rank can be annotate a 1 (you can use other values) if it's True and then add it to the SearchRank result.

from django.db import models
from django.db.models import Case, Value, When
from django.contrib.postgres.search import (
    SearchQuery, SearchRank, SearchVector,
)

search_vectors = (
    SearchVector('name', weight='A', config='english') +
    SearchVector('description', weight='B', config='english') +
    SearchVector('owner__name', weight='C', config='english')
)
terms = [SearchQuery(term) for term in keyword.split()]
search_query = functools.reduce(operator.or_, terms)
search_rank = SearchRank(
    search_vectors, search_query, weights=[0.2, 0.4, 0.6, 1]
)
qs = Item.objects.all().annotate(
    featured_boost=Case(
        When(is_featured=True, then=Value(1)),
        default=Value(0),
        output_field=models.IntegerField(),
    )
).annotate(
    rank=search_rank + featured_boost
).filter(rank__gte=0.2).order_by('-rank')

Upvotes: 2

Related Questions