Reputation: 826
I am trying to make a search refiner on my website which filters keywords, the problem is that sometimes people may want to search multiple keywords. The problem is by default if they entered "hello world" it would search for that exact phrase however I want it to separate it so that it searches for "hello" and "world". So far I have added .split()
to the keywords and that did divide it however it prevented me from using __icontains
in the query. Could anyone suggest the best way to do this? Cheers!
Code -
def browse(request):
business_industry = request.GET.get('business_industry', '')
business_address_region = request.GET.get('business_address_region', '')
employment_type = request.GET.get('employment_type', '')
pay_rate = request.GET.get('pay_rate', '')
keywords = request.GET.get('keywords', '').split()
form = JobSearchForm(initial=request.GET)
filters = Q(active_listing=True)
if business_industry:
filters &= Q(business_industry=business_industry)
if business_address_region:
filters &= Q(business_address_region=business_address_region)
if employment_type:
filters &= Q(employment_type=employment_type)
if pay_rate:
filters &= Q(pay_rate=pay_rate)
if keywords:
filters &= Q(job_description__icontains=keywords) | Q(job_title__icontains=keywords)
job_listings = JobListing.objects.filter(filters).distinct().order_by('-listing_date')
context_dict = {
'joblistings': job_listings,
'form': form
}
return render(request, 'browse.html', context_dict)
Edit: I have been asked to explain why this post is unique, the other question is asking how to compare his query to all his model fields. This is asking how to filter multiple keywords from a single field.
Upvotes: 1
Views: 1462
Reputation: 987
You can use Reduce function :
reduce(lambda x, y: x | y, [Q(name__icontains=word) for word in list])
Change this code :
if keywords:
filters &= Q(job_description__icontains=keywords) | Q(job_title__icontains=keywords)
To :
if keywords:
fliters &= reduce(lambda x, y: x | y, [Q(job_description__icontains=word) for word in keywords]) | reduce(lambda x, y: x | y, [Q(job_title__icontains=word) for word in keywords])
you can change lambda definition as you like. For example if you want to find listings which have all of the keywords, replace lambda x, y: x | y
with lambda x, y: x & y
.
Edit: Modified code as there were two unnecessary brackets
Upvotes: 2
Reputation: 6009
filters = {'active_listing':True}
q = Q()
if business_industry:
filters['business_industry'] = business_industry
if business_address_region:
filters['business_address_region'] = business_address_region
if employment_type:
filters['employment_type'] = employment_type
if pay_rate:
filters['pay_rate'] = pay_rate
if keywords:
q = Q(job_description__icontains=keywords) | Q(job_title__icontains=keywords)
job_listings = JobListing.objects.filter(q, **filters).distinct().order_by('-listing_date')
Upvotes: 0
Reputation: 515
You might need to use iteration for this
keyword_filter = Q()
for keyword in keywords:
keyword_filter|=Q(job_description__icontains=keyword) | Q(job_title__icontains=keyword)
filters &= keyword_filter
You can take a look at this similar question https://stackoverflow.com/a/5956422/2599266
Upvotes: 1
Reputation: 6733
You can make the query in a loop over keywords
:
job_description_q = Q()
for keyword in keywords:
job_description_q |= Q(job_description__icontains=keyword)
filters &= job_description_q
And a similar loop for jobtitle
.
Upvotes: 2