Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django 1.10 full text search across multiple fields. How to get distinct records?

Given these models:

class Listing(models.Model):
  features = models.ManyToManyField('Feature', related_name='listing_details')
  comments = models.TextField()

class Feature(models.Model):
  feature = models.CharField(max_length=100, unique=True)

How do I do a full-text search for Listings with text in either comments or one of the related Features?

I tried this:

In[28]: Listing.objects.annotate(search=SearchVector('comments', 'features__feature')).filter(search='something').count()
Out[28]: 
1215

So, I know not all those records contain the text something.

However, the number is "right" in the sense that a regular non-full-text query comes up with the same number:

In[33]: Listing.objects.filter(Q(comments__icontains='something') | Q(features__feature__icontains='something')).count()
Out[33]: 
1215

I can get down to just the Listing objects containing the text something in the comments field or in features__feature like so:

In[34]: Listing.objects.filter(Q(comments__icontains='something') | Q(features__feature__icontains='something')).distinct().count()
Out[34]: 
25

The real question boils down to how do I get those same 25 records back with full text search?

like image 413
Dustin Wyatt Avatar asked Aug 25 '16 21:08

Dustin Wyatt


1 Answers

I used ManyToManyField in SearchVector with StringAgg to avoid strange duplication and have correct results.

In your example the correct query should be:

from django.contrib.postgres.aggregates import StringAgg
from django.contrib.postgres.search import SearchVector

Listing.objects.annotate(
    search=SearchVector('comments') + SearchVector(StringAgg('features__feature', delimiter=' '))
).filter(search='something')
like image 52
Paolo Melchiorre Avatar answered Nov 01 '22 08:11

Paolo Melchiorre