Skip to content

Commit 3994bfa

Browse files
committed
Added a range filter type. Thanks to davisp & lukesneeringer for the suggestion!
Note that integer ranges are broken on the current Whoosh (1.1.1). However, date & character ranges seem to work fine.
1 parent 4897a5c commit 3994bfa

File tree

7 files changed

+33
-13
lines changed

7 files changed

+33
-13
lines changed

docs/searchqueryset_api.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,7 @@ The following lookup types are supported:
579579
* lte
580580
* in
581581
* startswith
582+
* range
582583

583584
These options are similar in function to the way Django's lookup types work.
584585
The actual behavior of these lookups is backend-specific.
@@ -603,6 +604,7 @@ Example::
603604
# Other usages look like:
604605
SearchQuerySet().filter(pub_date__gte=datetime.date(2008, 1, 1), pub_date__lt=datetime.date(2009, 1, 1))
605606
SearchQuerySet().filter(author__in=['daniel', 'john', 'jane'])
607+
SearchQuerySet().filter(view_count__range=[3, 5])
606608

607609

608610
``EmptySearchQuerySet``

haystack/backends/solr_backend.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -401,15 +401,19 @@ def build_query_fragment(self, field, filter_type, value):
401401
'startswith': "%s:%s*",
402402
}
403403

404-
if filter_type != 'in':
405-
result = filter_types[filter_type] % (index_fieldname, value)
406-
else:
404+
if filter_type == 'in':
407405
in_options = []
408406

409407
for possible_value in value:
410408
in_options.append('%s:"%s"' % (index_fieldname, self.backend.conn._from_python(possible_value)))
411409

412410
result = "(%s)" % " OR ".join(in_options)
411+
elif filter_type == 'range':
412+
start = self.backend.conn._from_python(value[0])
413+
end = self.backend.conn._from_python(value[1])
414+
return "%s:[%s TO %s]" % (index_fieldname, start, end)
415+
else:
416+
result = filter_types[filter_type] % (index_fieldname, value)
413417

414418
return result
415419

haystack/backends/whoosh_backend.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -596,7 +596,7 @@ def build_query_fragment(self, field, filter_type, value):
596596
if hasattr(value, 'strftime'):
597597
is_datetime = True
598598

599-
if filter_type != 'in':
599+
if not filter_type in ('in', 'range'):
600600
# 'in' is a bit of a special case, as we don't want to
601601
# convert a valid list/tuple to string. Defer handling it
602602
# until later...
@@ -622,12 +622,7 @@ def build_query_fragment(self, field, filter_type, value):
622622
'startswith': "%s:%s*",
623623
}
624624

625-
if filter_type != 'in':
626-
if is_datetime is True:
627-
value = self._convert_datetime(value)
628-
629-
result = filter_types[filter_type] % (index_fieldname, value)
630-
else:
625+
if filter_type == 'in':
631626
in_options = []
632627

633628
for possible_value in value:
@@ -644,5 +639,21 @@ def build_query_fragment(self, field, filter_type, value):
644639
in_options.append('%s:"%s"' % (index_fieldname, pv))
645640

646641
result = "(%s)" % " OR ".join(in_options)
642+
elif filter_type == 'range':
643+
start = self.backend._from_python(value[0])
644+
end = self.backend._from_python(value[1])
645+
646+
if hasattr(value[0], 'strftime'):
647+
start = self._convert_datetime(start)
648+
649+
if hasattr(value[1], 'strftime'):
650+
end = self._convert_datetime(end)
651+
652+
return "%s:[%s TO %s]" % (index_fieldname, start, end)
653+
else:
654+
if is_datetime is True:
655+
value = self._convert_datetime(value)
656+
657+
result = filter_types[filter_type] % (index_fieldname, value)
647658

648659
return result

haystack/constants.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
DEFAULT_OPERATOR = getattr(settings, 'HAYSTACK_DEFAULT_OPERATOR', 'AND')
55

66
# Valid expression extensions.
7-
VALID_FILTERS = set(['exact', 'gt', 'gte', 'lt', 'lte', 'in', 'startswith'])
7+
VALID_FILTERS = set(['exact', 'gt', 'gte', 'lt', 'lte', 'in', 'startswith', 'range'])
88
FILTER_SEPARATOR = '__'
99

1010
# The maximum number of items to display in a SearchQuerySet.__repr__

tests/core/tests/query.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def test_split_expression(self):
3131
self.assertEqual(sq.split_expression('foo__gte'), ('foo', 'gte'))
3232
self.assertEqual(sq.split_expression('foo__in'), ('foo', 'in'))
3333
self.assertEqual(sq.split_expression('foo__startswith'), ('foo', 'startswith'))
34+
self.assertEqual(sq.split_expression('foo__range'), ('foo', 'range'))
3435

3536
# Unrecognized filter. Fall back to exact.
3637
self.assertEqual(sq.split_expression('foo__moof'), ('foo', 'exact'))

tests/solr_tests/tests/solr_query.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ def test_build_query_multiple_filter_types(self):
6363
self.sq.add_filter(SQ(created__lt='2009-02-12 12:13:00'))
6464
self.sq.add_filter(SQ(title__gte='B'))
6565
self.sq.add_filter(SQ(id__in=[1, 2, 3]))
66-
self.assertEqual(self.sq.build_query(), u'(why AND pub_date:[* TO "2009-02-10 01:59:00"] AND author:{daniel TO *} AND created:{* TO "2009-02-12 12:13:00"} AND title:[B TO *] AND (id:"1" OR id:"2" OR id:"3"))')
66+
self.sq.add_filter(SQ(rating__range=[3, 5]))
67+
self.assertEqual(self.sq.build_query(), u'(why AND pub_date:[* TO "2009-02-10 01:59:00"] AND author:{daniel TO *} AND created:{* TO "2009-02-12 12:13:00"} AND title:[B TO *] AND (id:"1" OR id:"2" OR id:"3") AND rating:[3 TO 5])')
6768

6869
def test_build_query_in_filter_multiple_words(self):
6970
self.sq.add_filter(SQ(content='why'))

tests/whoosh_tests/tests/whoosh_query.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ def test_build_query_multiple_filter_types(self):
7272
self.sq.add_filter(SQ(created__lt=datetime.datetime(2009, 2, 12, 12, 13)))
7373
self.sq.add_filter(SQ(title__gte='B'))
7474
self.sq.add_filter(SQ(id__in=[1, 2, 3]))
75-
self.assertEqual(self.sq.build_query(), u'(why AND pub_date:[TO 20090210T015900] AND author:{daniel TO} AND created:{TO 20090212T121300} AND title:[B TO] AND (id:"1" OR id:"2" OR id:"3"))')
75+
self.sq.add_filter(SQ(rating__range=[3, 5]))
76+
self.assertEqual(self.sq.build_query(), u'(why AND pub_date:[TO 20090210T015900] AND author:{daniel TO} AND created:{TO 20090212T121300} AND title:[B TO] AND (id:"1" OR id:"2" OR id:"3") AND rating:[3 TO 5])')
7677

7778
def test_build_query_in_filter_multiple_words(self):
7879
self.sq.add_filter(SQ(content='why'))

0 commit comments

Comments
 (0)