mirror of
https://github.com/Hopiu/xapian-haystack.git
synced 2026-05-05 05:54:47 +00:00
Added support for faceting when using MultiValueField
This commit is contained in:
parent
d681827d0c
commit
e0c6b3d7b1
2 changed files with 45 additions and 14 deletions
|
|
@ -38,7 +38,11 @@ class XapianMockSearchIndex(indexes.SearchIndex):
|
|||
value = indexes.IntegerField(model_attr='value')
|
||||
flag = indexes.BooleanField(model_attr='flag')
|
||||
slug = indexes.CharField(indexed=False, model_attr='slug')
|
||||
popularity = indexes.FloatField(indexed=True, model_attr='popularity')
|
||||
popularity = indexes.FloatField(model_attr='popularity')
|
||||
mvf = indexes.MultiValueField()
|
||||
|
||||
def prepare_mvf(self, obj):
|
||||
return ['%d' % (i * obj.id) for i in xrange(1, 4)]
|
||||
|
||||
|
||||
class XapianSearchSite(sites.SearchSite):
|
||||
|
|
@ -111,11 +115,11 @@ class XapianSearchBackendTestCase(TestCase):
|
|||
|
||||
self.assertEqual(len(self.xapian_search('')), 3)
|
||||
self.assertEqual([dict(doc) for doc in self.xapian_search('')], [
|
||||
{'flag': u't', 'name': u'david1', 'text': u'Indexed!\n1', 'pub_date': u'20090224000000', 'value': '000000000005', 'id': u'tests.mockmodel.1', 'slug': 'http://example.com/1', 'popularity': '\xca\x84'},
|
||||
{'flag': u'f', 'name': u'david2', 'text': u'Indexed!\n2', 'pub_date': u'20090223000000', 'value': '000000000010', 'id': u'tests.mockmodel.2', 'slug': 'http://example.com/2', 'popularity': '\xb4`'},
|
||||
{'flag': u't', 'name': u'david3', 'text': u'Indexed!\n3', 'pub_date': u'20090222000000', 'value': '000000000015', 'id': u'tests.mockmodel.3', 'slug': 'http://example.com/3', 'popularity': '\xcb\x98'}
|
||||
{'flag': u't', 'name': u'david1', 'text': u'Indexed!\n1', 'mvf': u"['1', '2', '3']", 'pub_date': u'20090224000000', 'value': '000000000005', 'id': u'tests.mockmodel.1', 'slug': 'http://example.com/1', 'popularity': '\xca\x84'},
|
||||
{'flag': u'f', 'name': u'david2', 'text': u'Indexed!\n2', 'mvf': u"['2', '4', '6']", 'pub_date': u'20090223000000', 'value': '000000000010', 'id': u'tests.mockmodel.2', 'slug': 'http://example.com/2', 'popularity': '\xb4`'},
|
||||
{'flag': u't', 'name': u'david3', 'text': u'Indexed!\n3', 'mvf': u"['3', '6', '9']", 'pub_date': u'20090222000000', 'value': '000000000015', 'id': u'tests.mockmodel.3', 'slug': 'http://example.com/3', 'popularity': '\xcb\x98'}
|
||||
])
|
||||
|
||||
|
||||
def test_remove(self):
|
||||
self.sb.update(self.msi, self.sample_objs)
|
||||
self.assertEqual(len(self.xapian_search('')), 3)
|
||||
|
|
@ -123,8 +127,8 @@ class XapianSearchBackendTestCase(TestCase):
|
|||
self.sb.remove(self.sample_objs[0])
|
||||
self.assertEqual(len(self.xapian_search('')), 2)
|
||||
self.assertEqual([dict(doc) for doc in self.xapian_search('')], [
|
||||
{'flag': u'f', 'name': u'david2', 'text': u'Indexed!\n2', 'pub_date': u'20090223000000', 'value': '000000000010', 'id': u'tests.mockmodel.2', 'slug': 'http://example.com/2', 'popularity': '\xb4`'},
|
||||
{'flag': u't', 'name': u'david3', 'text': u'Indexed!\n3', 'pub_date': u'20090222000000', 'value': '000000000015', 'id': u'tests.mockmodel.3', 'slug': 'http://example.com/3', 'popularity': '\xcb\x98'}
|
||||
{'flag': u'f', 'name': u'david2', 'text': u'Indexed!\n2', 'mvf': u"['2', '4', '6']", 'pub_date': u'20090223000000', 'value': '000000000010', 'id': u'tests.mockmodel.2', 'slug': 'http://example.com/2', 'popularity': '\xb4`'},
|
||||
{'flag': u't', 'name': u'david3', 'text': u'Indexed!\n3', 'mvf': u"['3', '6', '9']", 'pub_date': u'20090222000000', 'value': '000000000015', 'id': u'tests.mockmodel.3', 'slug': 'http://example.com/3', 'popularity': '\xcb\x98'}
|
||||
])
|
||||
|
||||
def test_clear(self):
|
||||
|
|
@ -192,6 +196,10 @@ class XapianSearchBackendTestCase(TestCase):
|
|||
results = self.sb.search('index', facets=['flag'])
|
||||
self.assertEqual(results['hits'], 3)
|
||||
self.assertEqual(results['facets']['fields']['flag'], [(False, 1), (True, 2)])
|
||||
|
||||
results = self.sb.search('index', facets=['mvf'])
|
||||
self.assertEqual(results['hits'], 3)
|
||||
self.assertEqual(results['facets']['fields']['mvf'], [('1', 1), ('3', 2), ('2', 2), ('4', 1), ('6', 2), ('9', 1)])
|
||||
|
||||
def test_date_facets(self):
|
||||
self.sb.update(self.msi, self.sample_objs)
|
||||
|
|
@ -343,12 +351,13 @@ class XapianSearchBackendTestCase(TestCase):
|
|||
def test_build_schema(self):
|
||||
(content_field_name, fields) = self.sb.build_schema(self.site.all_searchfields())
|
||||
self.assertEqual(content_field_name, 'text')
|
||||
self.assertEqual(len(fields), 6)
|
||||
self.assertEqual(len(fields), 7)
|
||||
self.assertEqual(fields, [
|
||||
{'column': 0, 'type': 'text', 'field_name': 'name', 'multi_valued': 'false'},
|
||||
{'column': 1, 'type': 'text', 'field_name': 'text', 'multi_valued': 'false'},
|
||||
{'column': 2, 'type': 'float', 'field_name': 'popularity', 'multi_valued': 'false'},
|
||||
{'column': 3, 'type': 'long', 'field_name': 'value', 'multi_valued': 'false'},
|
||||
{'column': 4, 'type': 'boolean', 'field_name': 'flag', 'multi_valued': 'false'},
|
||||
{'column': 5, 'type': 'date', 'field_name': 'pub_date', 'multi_valued': 'false'},
|
||||
{'column': 5, 'type': 'text', 'field_name': 'mvf', 'multi_valued': 'true'},
|
||||
{'column': 6, 'type': 'date', 'field_name': 'pub_date', 'multi_valued': 'false'},
|
||||
])
|
||||
|
|
|
|||
|
|
@ -202,18 +202,18 @@ class SearchBackend(BaseSearchBackend):
|
|||
document = xapian.Document()
|
||||
term_generator = self._term_generator(database, document)
|
||||
document_id = self.get_identifier(obj)
|
||||
model_data = index.prepare(obj)
|
||||
data = index.prepare(obj)
|
||||
|
||||
for field in self.schema:
|
||||
if field['field_name'] in model_data.keys():
|
||||
if field['field_name'] in data.keys():
|
||||
prefix = DOCUMENT_CUSTOM_TERM_PREFIX + field['field_name'].upper()
|
||||
value = model_data[field['field_name']]
|
||||
value = data[field['field_name']]
|
||||
term_generator.index_text(force_unicode(value))
|
||||
term_generator.index_text(force_unicode(value), 1, prefix)
|
||||
document.add_value(field['column'], self._marshal_value(value))
|
||||
|
||||
document.set_data(pickle.dumps(
|
||||
(obj._meta.app_label, obj._meta.module_name, obj.pk, model_data),
|
||||
(obj._meta.app_label, obj._meta.module_name, obj.pk, data),
|
||||
pickle.HIGHEST_PROTOCOL
|
||||
))
|
||||
document.add_term(document_id)
|
||||
|
|
@ -522,6 +522,9 @@ class SearchBackend(BaseSearchBackend):
|
|||
"""
|
||||
Private method that facets a document by field name.
|
||||
|
||||
Fields of type MultiValueField will be faceted on each item in the
|
||||
(containing) list.
|
||||
|
||||
Required arguments:
|
||||
`results` -- A list SearchResults to facet
|
||||
`field_facets` -- A list of fields to facet on
|
||||
|
|
@ -533,7 +536,11 @@ class SearchBackend(BaseSearchBackend):
|
|||
|
||||
for result in results:
|
||||
field_value = getattr(result, field)
|
||||
facet_list[field_value] = facet_list.get(field_value, 0) + 1
|
||||
if self._multi_value_field(field):
|
||||
for item in field_value: # Facet each item in a MultiValueField
|
||||
facet_list[item] = facet_list.get(item, 0) + 1
|
||||
else:
|
||||
facet_list[field_value] = facet_list.get(field_value, 0) + 1
|
||||
|
||||
facet_dict[field] = facet_list.items()
|
||||
|
||||
|
|
@ -859,6 +866,21 @@ class SearchBackend(BaseSearchBackend):
|
|||
return field_dict['column']
|
||||
return 0
|
||||
|
||||
def _multi_value_field(self, field):
|
||||
"""
|
||||
Private method that returns `True` if a field is multi-valued, else
|
||||
`False`.
|
||||
|
||||
Required arguemnts:
|
||||
`field` -- The field to lookup
|
||||
|
||||
Returns a boolean value indicating whether the field is multi-valued.
|
||||
"""
|
||||
for field_dict in self.schema:
|
||||
if field_dict['field_name'] == field:
|
||||
return field_dict['multi_valued'] == 'true'
|
||||
return False
|
||||
|
||||
|
||||
class SearchQuery(BaseSearchQuery):
|
||||
"""
|
||||
|
|
|
|||
Loading…
Reference in a new issue