1

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.

eZ_Harry
  • 816
  • 9
  • 25
  • Possible duplicate of [How to get more than one field with django filter icontains](http://stackoverflow.com/questions/15045101/how-to-get-more-than-one-field-with-django-filter-icontains). It's not quite the same but in essence: loop manually. – dhke Feb 01 '16 at 08:48

4 Answers4

2

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.

nima
  • 6,566
  • 4
  • 45
  • 57
  • This has kind of solved my issue, while the code works it does have a problem. It looks for any listing which has any of the keywords, i'd like it to find listings which has all of the keywords. Would you know how to modify it for this? – eZ_Harry Feb 01 '16 at 09:05
  • You can use `&=` instead of `|=` in the loop. – nima Feb 01 '16 at 10:53
2

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

Mehran Torki
  • 977
  • 1
  • 9
  • 37
  • This worked perfectly! Initially I was put off by this answer purely because i am not competent enough to understand the reduce function and it looks extremely difficult. Despite this I tried the code and it is flawless, very impressive. Thanks! – eZ_Harry Feb 01 '16 at 09:21
  • Thanks, `reduce` is a built-in function and you can read about it [here](https://docs.python.org/2/library/functions.html#reduce) – Mehran Torki Feb 01 '16 at 09:36
1

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

Community
  • 1
  • 1
Obsidian
  • 515
  • 3
  • 10
  • This has kind of solved my issue, while the code works it does have a problem. It looks for any listing which has any of the keywords, i'd like it to find listings which has all of the keywords. Would you know how to modify it for this? – eZ_Harry Feb 01 '16 at 09:05
0
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')
Geo Jacob
  • 5,909
  • 1
  • 36
  • 43