Reputation: 314
I am trying to write a search feature and can't figure out how to combine the results of a rank-annotated search and a non-rank-annotated search.
Let's say I have the following model:
class Song(models.Model):
filename=models.CharField(max_length=120, db_index=True)
title=models.CharField(max_length=120, db_index=True)
title_vector=SearchVectorField(null=True, blank=True)
I would like to let the user enter a single search term and search both title and filename. So if a user enters "space" as a search term, it should search for any song that has space in the title or filename.
The query on the title vector looks like this. The idea is to order the results by rank so I can put the most relevant search results at the top.
title_query_results = Song.objects.annotate(
rank=SearchRank(F('title_vector'), query)
).filter(
title_vector=query
)
However, the filename query looks like this:
file_query_results = Song.objects.filter(
filename__icontains=query
)
Because the file query results do not contain the rank annotation, I can't do a union on the results, because they have a different number of columns.
Is there a way to get rank annotation for a query on the filename column?
If not, what are my options for doing a union on the search results? For example, if I just wanted all the filename results to appear at the top of the list, how can I make that happen?
My last resort option is to just send the results to the template as separate lists, but ideally the template will only have a single list of query results to work with.
Upvotes: 2
Views: 42
Reputation: 477598
You can work with:
from django.db.models import Q
query_results = Song.objects.annotate(
rank=SearchRank(F('title_vector'), query)
).filter(Q(title_vector=query) | Q(filename__icontains=query))
or the same, but perhaps more elegant:
from django.db.models import Q
query_results = Song.objects.annotate(
rank=SearchRank(F('title_vector'), query)
).filter(title_vector=query, filename__icontains=query, _connector=Q.OR)
Upvotes: 0