Skip to content

Commit

Permalink
Accept string based primary keys
Browse files Browse the repository at this point in the history
  • Loading branch information
charles-vdulac committed Nov 24, 2016
1 parent 5ef599f commit 7929436
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 6 deletions.
17 changes: 16 additions & 1 deletion tests/xapian_tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Document(models.Model):
text = models.TextField()


class BlogEntry(models.Model):
class AbstractBlogEntry(models.Model):
"""
Same as tests.core.MockModel with a few extra fields for testing various
sorting and ordering criteria.
Expand All @@ -34,3 +34,18 @@ class BlogEntry(models.Model):
number = models.IntegerField()
float_number = models.FloatField()
decimal_number = models.DecimalField(max_digits=4, decimal_places=2)

class Meta:
abstract = True


class BlogEntry(AbstractBlogEntry):
pass


class UUIDBlogEntry(AbstractBlogEntry):
"""
A blog entry with string based primary key instead of an integer.
Covers #138
"""
uuid = models.CharField(primary_key=True, max_length=20)
29 changes: 29 additions & 0 deletions tests/xapian_tests/search_indexes.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,35 @@ def prepare_empty(self, obj):
return ''


class UUIDBlogSearchIndex(BlogSearchIndex):

def get_model(self):
return models.UUIDBlogEntry

def prepare_sites(self, obj):
return ['%d' % (i * int(obj.pk.split('-')[1])) for i in range(1, 4)]

def prepare_tags(self, obj):
if obj.pk == 'uuid-1':
return ['a', 'b', 'c']
elif obj.pk == 'uuid-2':
return ['ab', 'bc', 'cd']
else:
return ['an', 'to', 'or']

def prepare_keys(self, obj):
return [i * int(obj.pk.split('-')[1]) for i in range(1, 4)]

def prepare_titles(self, obj):
if obj.pk == 'uuid-1':
return ['object one title one', 'object one title two']
elif obj.pk == 'uuid-2':
return ['object two title one', 'object two title two']
else:
return ['object three title one', 'object three title two']



class CompleteBlogEntryIndex(indexes.SearchIndex):
text = indexes.CharField(model_attr='text', document=True)
author = indexes.CharField(model_attr='author')
Expand Down
73 changes: 70 additions & 3 deletions tests/xapian_tests/tests/test_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@
from haystack.utils.loading import UnifiedIndex

from ..search_indexes import XapianNGramIndex, XapianEdgeNGramIndex, \
CompleteBlogEntryIndex, BlogSearchIndex
from ..models import BlogEntry, AnotherMockModel, MockTag

CompleteBlogEntryIndex, BlogSearchIndex, UUIDBlogSearchIndex
from ..models import BlogEntry, AnotherMockModel, MockTag, UUIDBlogEntry

XAPIAN_VERSION = [int(x) for x in xapian.__version__.split('.')]

Expand Down Expand Up @@ -681,6 +680,74 @@ def test_more_like_this_with_unindexed_model(self):
self.assertRaises(InvalidIndexError, self.backend.more_like_this, mock)


class StringBasedPKModelBackendFeaturesTestCase(HaystackBackendTestCase, TestCase):
"""
Covers #138, Must not assume django_id is an int
"""

def get_index(self):
return UUIDBlogSearchIndex()

@staticmethod
def get_entry(i):
entry = UUIDBlogEntry()
entry.uuid = 'uuid-%s' % i
entry.author = 'david%s' % i
entry.url = 'http://example.com/%d/' % i
entry.boolean = bool(i % 2)
entry.number = i*5
entry.float_number = i*5.0
entry.decimal_number = Decimal('22.34')
entry.datetime = (
datetime.datetime(2009, 2, 25, 1, 1, 1) - datetime.timedelta(seconds=i)
)
entry.date = datetime.date(2009, 2, 23) + datetime.timedelta(days=i)
return entry

def setUp(self):
super(StringBasedPKModelBackendFeaturesTestCase, self).setUp()

self.sample_objs = []

for i in range(1, 4):
entry = self.get_entry(i)
self.sample_objs.append(entry)

self.sample_objs[0].float_number = 834.0
self.sample_objs[1].float_number = 35.5
self.sample_objs[2].float_number = 972.0
for obj in self.sample_objs:
obj.save()

self.backend.update(self.index, UUIDBlogEntry.objects.all())

def test_update(self):
self.assertEqual(pks(self.backend.search(xapian.Query(''))['results']),
['uuid-1', 'uuid-2', 'uuid-3'])

def test_order_by_django_id(self):
"""
We need this test because ordering on more than
10 entries was not correct at some point.
"""
self.sample_objs = []
pk_list = []
for i in range(101, 200):
entry = self.get_entry(i)
self.sample_objs.append(entry)
pk_list.append('uuid-%03d' % i)

for obj in self.sample_objs:
obj.save()

self.backend.clear()
self.backend.update(self.index, self.sample_objs)

results = self.backend.search(xapian.Query(''), sort_by=['-django_id'])
self.assertEqual(pks(results['results']), list(reversed(pk_list)))



class IndexationNGramTestCase(HaystackBackendTestCase, TestCase):
def get_index(self):
return XapianNGramIndex()
Expand Down
4 changes: 4 additions & 0 deletions tests/xapian_tests/tests/test_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,10 @@ def test_multiple_filter_types(self):
'zzzzzzzzzzzzzzzzzzzzzzzzz AND'
' (QQ000000000001 OR QQ000000000002 OR QQ000000000003))')

def test_filter_string_based_django_pk(self):
self.sq.add_filter(SQ(django_id='uuid-1'))
self.assertExpectedQuery(self.sq.build_query(), 'QQuuid-1')

def test_log_query(self):
reset_search_queries()
self.assertEqual(len(connections['default'].queries), 0)
Expand Down
13 changes: 11 additions & 2 deletions xapian_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,11 @@ def add_datetime_to_document(termpos, prefix, term, weight):
# `django_id` is an int and `django_ct` is text;
# besides, they are indexed by their (unstemmed) value.
if field['field_name'] == 'django_id':
value = int(value)
try:
value = int(value)
except ValueError:
# Django_id is a string
field['type'] = 'text'
value = _term_to_xapian_value(value, field['type'])

document.add_term(TERM_PREFIXES[field['field_name']] + value, weight)
Expand Down Expand Up @@ -1499,7 +1503,12 @@ def _term_query(self, term, field_name, field_type, stemmed=True):
if field_name in ('id', 'django_id', 'django_ct'):
# to ensure the value is serialized correctly.
if field_name == 'django_id':
term = int(term)
try:
term = int(term)
except ValueError:
# Django_id is a string
field_type = 'text'

term = _term_to_xapian_value(term, field_type)
return xapian.Query('%s%s' % (TERM_PREFIXES[field_name], term))

Expand Down

0 comments on commit 7929436

Please sign in to comment.