forked from gregplaysguitar/django-simple-search
-
Notifications
You must be signed in to change notification settings - Fork 0
/
simple_search.py
101 lines (69 loc) · 2.77 KB
/
simple_search.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# -*- coding: utf-8 -*-
from django.db.models import Q
from django import forms
from django.utils.text import smart_split
from django.conf import settings
if not locals().get('reduce'):
from functools import reduce
__version__ = '1.0.2'
VERSION = tuple(map(int, __version__.split('.')))
STOPWORDS = getattr(
settings,
'SEARCH_STOPWORDS',
'a,able,about,across,after,all,almost,also,am,among,an,'
'and,any,are,as,at,be,because,been,but,by,can,cannot,'
'could,dear,did,do,does,either,else,ever,every,for,from,'
'get,got,had,has,have,he,her,hers,him,his,how,however,i,'
'if,in,into,is,it,its,just,least,let,like,likely,may,me,'
'might,most,must,my,neither,no,nor,not,of,off,often,on,'
'only,or,other,our,own,rather,said,say,says,she,should,'
'since,so,some,than,that,the,their,them,then,there,'
'these,they,this,tis,to,too,twas,us,wants,was,we,were,'
'what,when,where,which,while,who,whom,why,will,with,'
'would,yet,you,your').split(',')
NULL_FILTER = Q(pk=None)
def search_filter(search_fields, query_string):
"""search_fields example: ['name', 'category__name', '@description', '=id']
"""
query_string = query_string.strip()
filters = []
first = True
for bit in split_text_query(query_string):
queries = [Q(**{search_param(field_name, first): bit})
for field_name in search_fields]
filters.append(reduce(Q.__or__, queries))
first = False
return reduce(Q.__and__, filters) if len(filters) else NULL_FILTER
def search_param(field_name, is_first_word):
if field_name.startswith('^') and is_first_word:
return "%s__istartswith" % field_name[1:]
elif field_name.startswith('@'):
return "%s__search" % field_name[1:]
elif field_name.startswith('='):
return "%s__iexact" % field_name[1:]
else:
return "%s__icontains" % field_name
def split_text_query(query):
"""Filter out stopwords but only if there are useful words"""
split_query = list(smart_split(query))
filtered_query = [bit for bit in split_query if bit not in STOPWORDS]
return filtered_query if len(filtered_query) else split_query
class SearchForm(forms.Form):
queryset = None
search_fields = None
q = forms.CharField(label='Search', required=False)
def clean_q(self):
return self.cleaned_data['q'].strip()
def get_queryset(self):
qs = self.queryset
query = self.cleaned_data.get('q')
if query:
qs = qs.filter(search_filter(self.search_fields, query))
return qs
def search_form_factory(queryset, search_fields):
_queryset = queryset
_search_fields = search_fields
class _Form(SearchForm):
queryset = _queryset
search_fields = _search_fields
return _Form