diff --git a/tests/xapian_tests/tests/xapian_query.py b/tests/xapian_tests/tests/xapian_query.py index 1a3783f..05fdd6f 100644 --- a/tests/xapian_tests/tests/xapian_query.py +++ b/tests/xapian_tests/tests/xapian_query.py @@ -120,16 +120,21 @@ class XapianSearchQueryTestCase(TestCase): # self.sq.add_filter(SQ(id__in=[1, 2, 3])) # self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query(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"))') + def test_build_query_in_filter_single_words(self): + self.sq.add_filter(SQ(content='why')) + self.sq.add_filter(SQ(title__in=["Dune", "Jaws"])) + self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND (XTITLEdune OR XTITLEjaws)))') + def test_build_query_in_filter_multiple_words(self): self.sq.add_filter(SQ(content='why')) self.sq.add_filter(SQ(title__in=["A Famous Paper", "An Infamous Article"])) - self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND (XTITLEa famous paper OR XTITLEan infamous article)))') + self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND ((XTITLEa PHRASE 3 XTITLEfamous PHRASE 3 XTITLEpaper) OR (XTITLEan PHRASE 3 XTITLEinfamous PHRASE 3 XTITLEarticle))))') # def test_build_query_not_in_filter_multiple_words(self): # self.sq.add_filter(SQ(content='why')) # self.sq.add_filter(~SQ(title__in=["A Famous Paper", "An Infamous Article"])) # self.assertEqual(self.sq.build_query().get_description(), 'Xapian::Query((why AND_NOT (XTITLEa famous paper OR XTITLEan infamous article)))') - + # # def test_build_query_in_filter_datetime(self): # self.sq.add_filter(SQ(content='why')) # self.sq.add_filter(SQ(pub_date__in=[datetime.datetime(2009, 7, 6, 1, 56, 21)])) diff --git a/xapian_backend.py b/xapian_backend.py index 70e0f3f..b4f2716 100755 --- a/xapian_backend.py +++ b/xapian_backend.py @@ -952,19 +952,7 @@ class SearchQuery(BaseSearchQuery): elif filter_type == 'startswith': pass elif filter_type == 'in': - query_list.append( - xapian.Query( - xapian.Query.OP_OR, [ - xapian.Query( - '%s%s%s' % ( - DOCUMENT_CUSTOM_TERM_PREFIX, - field.upper(), - _marshal_value(possible_value) - ) - ) for possible_value in value - ] - ) - ) + query_list.append(self._filter_in(value, field, is_not)) if search_node.connector == 'OR': @@ -976,50 +964,75 @@ class SearchQuery(BaseSearchQuery): if ' ' in value: if is_not: return xapian.Query( - xapian.Query.OP_AND_NOT, - xapian.Query(''), - xapian.Query - (xapian.Query.OP_PHRASE, value.split() - ) - ) + xapian.Query.OP_AND_NOT, self._all_query(), self._phrase_query(value.split()) + ) else: - return xapian.Query(xapian.Query.OP_PHRASE, value.split()) + return self._phrase_query(value.split()) else: if is_not: - return xapian.Query(xapian.Query.OP_AND_NOT, '', value) + return xapian.Query(xapian.Query.OP_AND_NOT, self._all_query(), self._term_query(value)) else: - return xapian.Query(value) + return self._term_query(value) def _filter_exact(self, value, field, is_not): if ' ' in value: - phrase_query = xapian.Query( - xapian.Query.OP_PHRASE, [ - '%s%s%s' % ( - DOCUMENT_CUSTOM_TERM_PREFIX, field.upper(), _marshal_value(term) - ) for term in value.split() - ] - ) - if is_not: return xapian.Query( - xapian.Query.OP_AND_NOT, xapian.Query(''), phrase_query + xapian.Query.OP_AND_NOT, self._all_query(), self._phrase_query(value.split(), field) ) else: - return phrase_query + return self._phrase_query(value.split(), field) else: - term = '%s%s%s' % ( - DOCUMENT_CUSTOM_TERM_PREFIX, field.upper(), value - ) - if is_not: - return xapian.Query(xapian.Query.OP_AND_NOT, '', term) + return xapian.Query(xapian.Query.OP_AND_NOT, self._all_query(), self._term_query(value, field)) else: - return xapian.Query(term) - + return self._term_query(value, field) + + def _filter_in(self, value_list, field, is_not): + query_list = [] + for value in value_list: + if ' ' in value: + query_list.append( + xapian.Query( + xapian.Query.OP_OR, self._phrase_query(value.split(), field) + ) + ) + else: + query_list.append( + xapian.Query( + xapian.Query.OP_OR, self._term_query(value, field) + ) + ) + return xapian.Query(xapian.Query.OP_OR, query_list) + + def _all_query(self): + return xapian.Query('') + + def _term_query(self, value, field=None): + if field: + return xapian.Query('%s%s%s' % ( + DOCUMENT_CUSTOM_TERM_PREFIX, field.upper(), _marshal_value(value) + ) + ) + else: + return xapian.Query(value) + + def _phrase_query(self, value_list, field=None): + if field: + return xapian.Query( + xapian.Query.OP_PHRASE, [ + '%s%s%s' % ( + DOCUMENT_CUSTOM_TERM_PREFIX, field.upper(), _marshal_value(value) + ) for value in value_list + ] + ) + else: + return xapian.Query(xapian.Query.OP_PHRASE, value_list) + def _marshal_value(value): """ - Private method that converts Python values to a string for Xapian values. + Private utility method that converts Python values to a string for Xapian values. """ if isinstance(value, datetime.datetime): if value.microsecond: