1# encoding: utf-8
2
3from __future__ import absolute_import, division, print_function, unicode_literals
4
5import datetime
6import time
7from threading import Thread
8
9from django.test import TestCase
10from django.utils.six.moves import queue
11from test_haystack.core.models import (AFifthMockModel, AThirdMockModel, ManyToManyLeftSideModel,
12                                       ManyToManyRightSideModel, MockModel)
13
14from haystack import connection_router, connections, indexes
15from haystack.exceptions import SearchFieldError
16from haystack.utils.loading import UnifiedIndex
17
18
19class BadSearchIndex1(indexes.SearchIndex, indexes.Indexable):
20    author = indexes.CharField(model_attr='author')
21    pub_date = indexes.DateTimeField(model_attr='pub_date')
22
23    def get_model(self):
24        return MockModel
25
26
27class BadSearchIndex2(indexes.SearchIndex, indexes.Indexable):
28    text = indexes.CharField(document=True, use_template=True)
29    content2 = indexes.CharField(document=True, use_template=True)
30    author = indexes.CharField(model_attr='author')
31    pub_date = indexes.DateTimeField(model_attr='pub_date')
32
33    def get_model(self):
34        return MockModel
35
36
37class GoodMockSearchIndex(indexes.SearchIndex, indexes.Indexable):
38    text = indexes.CharField(document=True, use_template=True)
39    author = indexes.CharField(model_attr='author')
40    pub_date = indexes.DateTimeField(model_attr='pub_date')
41    extra = indexes.CharField(indexed=False, use_template=True)
42
43    def get_model(self):
44        return MockModel
45
46
47# For testing inheritance...
48class AltGoodMockSearchIndex(GoodMockSearchIndex, indexes.Indexable):
49    additional = indexes.CharField(model_attr='author')
50
51    def get_model(self):
52        return MockModel
53
54
55class GoodCustomMockSearchIndex(indexes.SearchIndex, indexes.Indexable):
56    text = indexes.CharField(document=True, use_template=True)
57    author = indexes.CharField(model_attr='author', faceted=True)
58    pub_date = indexes.DateTimeField(model_attr='pub_date', faceted=True)
59    extra = indexes.CharField(indexed=False, use_template=True)
60    hello = indexes.CharField(model_attr='hello')
61
62    def prepare(self, obj):
63        super(GoodCustomMockSearchIndex, self).prepare(obj)
64        self.prepared_data['whee'] = 'Custom preparation.'
65        return self.prepared_data
66
67    def prepare_author(self, obj):
68        return "Hi, I'm %s" % self.prepared_data['author']
69
70    def load_all_queryset(self):
71        return self.get_model()._default_manager.filter(id__gt=1)
72
73    def get_model(self):
74        return MockModel
75
76    def index_queryset(self, using=None):
77        return MockModel.objects.all()
78
79    def read_queryset(self, using=None):
80        return MockModel.objects.filter(author__in=['daniel1', 'daniel3'])
81
82    def build_queryset(self, start_date=None, end_date=None):
83        return MockModel.objects.filter(author__in=['daniel1', 'daniel3'])
84
85
86class GoodNullableMockSearchIndex(indexes.SearchIndex, indexes.Indexable):
87    text = indexes.CharField(document=True, use_template=True)
88    author = indexes.CharField(model_attr='author', null=True, faceted=True)
89
90    def get_model(self):
91        return MockModel
92
93
94class GoodOverriddenFieldNameMockSearchIndex(indexes.SearchIndex, indexes.Indexable):
95    text = indexes.CharField(document=True, use_template=True, index_fieldname='more_content')
96    author = indexes.CharField(model_attr='author', index_fieldname='name_s')
97    hello = indexes.CharField(model_attr='hello')
98
99    def get_model(self):
100        return MockModel
101
102
103class GoodFacetedMockSearchIndex(indexes.SearchIndex, indexes.Indexable):
104    text = indexes.CharField(document=True, use_template=True)
105    author = indexes.CharField(model_attr='author')
106    author_foo = indexes.FacetCharField(facet_for='author')
107    pub_date = indexes.DateTimeField(model_attr='pub_date')
108    pub_date_exact = indexes.FacetDateTimeField(facet_for='pub_date')
109
110    def get_model(self):
111        return MockModel
112
113    def prepare_author(self, obj):
114        return "Hi, I'm %s" % self.prepared_data['author']
115
116    def prepare_pub_date_exact(self, obj):
117        return "2010-10-26T01:54:32"
118
119
120class MROFieldsSearchIndexA(indexes.SearchIndex, indexes.Indexable):
121    text = indexes.CharField(document=True, model_attr='test_a')
122
123    def get_model(self):
124        return MockModel
125
126
127class MROFieldsSearchIndexB(indexes.SearchIndex, indexes.Indexable):
128    text = indexes.CharField(document=True, model_attr='test_b')
129
130    def get_model(self):
131        return MockModel
132
133
134class MROFieldsSearchChild(MROFieldsSearchIndexA, MROFieldsSearchIndexB):
135    pass
136
137
138class ModelWithManyToManyFieldAndAttributeLookupSearchIndex(indexes.SearchIndex, indexes.Indexable):
139    text = indexes.CharField(document=True)
140    related_models = indexes.MultiValueField(model_attr='related_models__name')
141
142    def get_model(self):
143        return ManyToManyLeftSideModel
144
145
146class SearchIndexTestCase(TestCase):
147    fixtures = ['base_data']
148
149    def setUp(self):
150        super(SearchIndexTestCase, self).setUp()
151        self.sb = connections['default'].get_backend()
152        self.mi = GoodMockSearchIndex()
153        self.cmi = GoodCustomMockSearchIndex()
154        self.cnmi = GoodNullableMockSearchIndex()
155        self.gfmsi = GoodFacetedMockSearchIndex()
156
157        # Fake the unified index.
158        self.old_unified_index = connections['default']._index
159        self.ui = UnifiedIndex()
160        self.ui.build(indexes=[self.mi])
161        connections['default']._index = self.ui
162
163        self.sample_docs = {
164            u'core.mockmodel.1': {
165                'text': u'Indexed!\n1',
166                'django_id': u'1',
167                'django_ct': u'core.mockmodel',
168                'extra': u'Stored!\n1',
169                'author': u'daniel1',
170                'pub_date': datetime.datetime(2009, 3, 17, 6, 0),
171                'id': u'core.mockmodel.1'
172            },
173            u'core.mockmodel.2': {
174                'text': u'Indexed!\n2',
175                'django_id': u'2',
176                'django_ct': u'core.mockmodel',
177                'extra': u'Stored!\n2',
178                'author': u'daniel2',
179                'pub_date': datetime.datetime(2009, 3, 17, 7, 0),
180                'id': u'core.mockmodel.2'
181            },
182            u'core.mockmodel.3': {
183                'text': u'Indexed!\n3',
184                'django_id': u'3',
185                'django_ct': u'core.mockmodel',
186                'extra': u'Stored!\n3',
187                'author': u'daniel3',
188                'pub_date': datetime.datetime(2009, 3, 17, 8, 0),
189                'id': u'core.mockmodel.3'
190            }
191        }
192
193    def tearDown(self):
194        connections['default']._index = self.old_unified_index
195        super(SearchIndexTestCase, self).tearDown()
196
197    def test_no_contentfield_present(self):
198        self.assertRaises(SearchFieldError, BadSearchIndex1)
199
200    def test_too_many_contentfields_present(self):
201        self.assertRaises(SearchFieldError, BadSearchIndex2)
202
203    def test_contentfield_present(self):
204        try:
205            mi = GoodMockSearchIndex()
206        except:
207            self.fail()
208
209    def test_proper_fields(self):
210        self.assertEqual(len(self.mi.fields), 4)
211        self.assertTrue('text' in self.mi.fields)
212        self.assertTrue(isinstance(self.mi.fields['text'], indexes.CharField))
213        self.assertTrue('author' in self.mi.fields)
214        self.assertTrue(isinstance(self.mi.fields['author'], indexes.CharField))
215        self.assertTrue('pub_date' in self.mi.fields)
216        self.assertTrue(isinstance(self.mi.fields['pub_date'], indexes.DateTimeField))
217        self.assertTrue('extra' in self.mi.fields)
218        self.assertTrue(isinstance(self.mi.fields['extra'], indexes.CharField))
219
220        self.assertEqual(len(self.cmi.fields), 7)
221        self.assertTrue('text' in self.cmi.fields)
222        self.assertTrue(isinstance(self.cmi.fields['text'], indexes.CharField))
223        self.assertTrue('author' in self.cmi.fields)
224        self.assertTrue(isinstance(self.cmi.fields['author'], indexes.CharField))
225        self.assertTrue('author_exact' in self.cmi.fields)
226        self.assertTrue(isinstance(self.cmi.fields['author_exact'], indexes.FacetCharField))
227        self.assertTrue('pub_date' in self.cmi.fields)
228        self.assertTrue(isinstance(self.cmi.fields['pub_date'], indexes.DateTimeField))
229        self.assertTrue('pub_date_exact' in self.cmi.fields)
230        self.assertTrue(isinstance(self.cmi.fields['pub_date_exact'], indexes.FacetDateTimeField))
231        self.assertTrue('extra' in self.cmi.fields)
232        self.assertTrue(isinstance(self.cmi.fields['extra'], indexes.CharField))
233        self.assertTrue('hello' in self.cmi.fields)
234        self.assertTrue(isinstance(self.cmi.fields['extra'], indexes.CharField))
235
236    def test_index_queryset(self):
237        self.assertEqual(len(self.cmi.index_queryset()), 3)
238
239    def test_read_queryset(self):
240        self.assertEqual(len(self.cmi.read_queryset()), 2)
241
242    def test_build_queryset(self):
243        # The custom SearchIndex.build_queryset returns the same records as
244        # the read_queryset
245        self.assertEqual(len(self.cmi.build_queryset()), 2)
246
247        # Store a reference to the original method
248        old_guf = self.mi.__class__.get_updated_field
249
250        self.mi.__class__.get_updated_field = lambda self: 'pub_date'
251
252        # With an updated field, we should get have filtered results
253        sd = datetime.datetime(2009, 3, 17, 7, 0)
254        self.assertEqual(len(self.mi.build_queryset(start_date=sd)), 2)
255
256        ed = datetime.datetime(2009, 3, 17, 7, 59)
257        self.assertEqual(len(self.mi.build_queryset(end_date=ed)), 2)
258
259        sd = datetime.datetime(2009, 3, 17, 6, 0)
260        ed = datetime.datetime(2009, 3, 17, 6, 59)
261        self.assertEqual(len(self.mi.build_queryset(start_date=sd,
262                                                    end_date=ed)), 1)
263
264        # Remove the updated field for the next test
265        del self.mi.__class__.get_updated_field
266
267        # The default should return all 3 even if we specify a start date
268        # because there is no updated field specified
269        self.assertEqual(len(self.mi.build_queryset(start_date=sd)), 3)
270
271        # Restore the original attribute
272        self.mi.__class__.get_updated_field = old_guf
273
274    def test_prepare(self):
275        mock = MockModel()
276        mock.pk = 20
277        mock.author = 'daniel%s' % mock.id
278        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
279
280        self.assertEqual(len(self.mi.prepare(mock)), 7)
281        self.assertEqual(sorted(self.mi.prepare(mock).keys()), ['author', 'django_ct', 'django_id', 'extra', 'id', 'pub_date', 'text'])
282
283    def test_custom_prepare(self):
284        mock = MockModel()
285        mock.pk = 20
286        mock.author = 'daniel%s' % mock.id
287        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
288
289        self.assertEqual(len(self.cmi.prepare(mock)), 11)
290        self.assertEqual(sorted(self.cmi.prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee'])
291
292        self.assertEqual(len(self.cmi.full_prepare(mock)), 11)
293        self.assertEqual(sorted(self.cmi.full_prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee'])
294
295    def test_thread_safety(self):
296        # This is a regression. ``SearchIndex`` used to write to
297        # ``self.prepared_data``, which would leak between threads if things
298        # went too fast.
299        exceptions = []
300
301        def threaded_prepare(index_queue, index, model):
302            try:
303                index.queue = index_queue
304                prepped = index.prepare(model)
305            except Exception as e:
306                exceptions.append(e)
307                raise
308
309        class ThreadedSearchIndex(GoodMockSearchIndex):
310            def prepare_author(self, obj):
311                if obj.pk == 20:
312                    time.sleep(0.1)
313                else:
314                    time.sleep(0.5)
315
316                index_queue.put(self.prepared_data['author'])
317                return self.prepared_data['author']
318
319        tmi = ThreadedSearchIndex()
320        index_queue = queue.Queue()
321        mock_1 = MockModel()
322        mock_1.pk = 20
323        mock_1.author = 'foo'
324        mock_1.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
325        mock_2 = MockModel()
326        mock_2.pk = 21
327        mock_2.author = 'daniel%s' % mock_2.id
328        mock_2.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
329
330        th1 = Thread(target=threaded_prepare, args=(index_queue, tmi, mock_1))
331        th2 = Thread(target=threaded_prepare, args=(index_queue, tmi, mock_2))
332
333        th1.start()
334        th2.start()
335        th1.join()
336        th2.join()
337
338        mock_1_result = index_queue.get()
339        mock_2_result = index_queue.get()
340        self.assertEqual(mock_1_result, u'foo')
341        self.assertEqual(mock_2_result, u'daniel21')
342
343    def test_custom_prepare_author(self):
344        mock = MockModel()
345        mock.pk = 20
346        mock.author = 'daniel%s' % mock.id
347        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
348
349        self.assertEqual(len(self.cmi.prepare(mock)), 11)
350        self.assertEqual(sorted(self.cmi.prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee'])
351
352        self.assertEqual(len(self.cmi.full_prepare(mock)), 11)
353        self.assertEqual(sorted(self.cmi.full_prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee'])
354        self.assertEqual(self.cmi.prepared_data['author'], "Hi, I'm daniel20")
355        self.assertEqual(self.cmi.prepared_data['author_exact'], "Hi, I'm daniel20")
356
357    def test_custom_model_attr(self):
358        mock = MockModel()
359        mock.pk = 20
360        mock.author = 'daniel%s' % mock.id
361        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
362
363        self.assertEqual(len(self.cmi.prepare(mock)), 11)
364        self.assertEqual(sorted(self.cmi.prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee'])
365
366        self.assertEqual(len(self.cmi.full_prepare(mock)), 11)
367        self.assertEqual(sorted(self.cmi.full_prepare(mock).keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'extra', 'hello', 'id', 'pub_date', 'pub_date_exact', 'text', 'whee'])
368        self.assertEqual(self.cmi.prepared_data['hello'], u'World!')
369
370    def test_custom_index_fieldname(self):
371        mock = MockModel()
372        mock.pk = 20
373        mock.author = 'daniel%s' % mock.id
374        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
375
376        cofnmi = GoodOverriddenFieldNameMockSearchIndex()
377        self.assertEqual(len(cofnmi.prepare(mock)), 6)
378        self.assertEqual(sorted(cofnmi.prepare(mock).keys()), ['django_ct', 'django_id', 'hello', 'id', 'more_content', 'name_s'])
379        self.assertEqual(cofnmi.prepared_data['name_s'], u'daniel20')
380        self.assertEqual(cofnmi.get_content_field(), 'more_content')
381
382    def test_get_content_field(self):
383        self.assertEqual(self.mi.get_content_field(), 'text')
384
385    def test_update(self):
386        self.sb.clear()
387        self.assertEqual(self.sb.search('*')['hits'], 0)
388        self.mi.update()
389        self.assertEqual(self.sb.search('*')['hits'], 3)
390        self.sb.clear()
391
392    def test_update_object(self):
393        self.sb.clear()
394        self.assertEqual(self.sb.search('*')['hits'], 0)
395
396        mock = MockModel()
397        mock.pk = 20
398        mock.author = 'daniel%s' % mock.id
399        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
400
401        self.mi.update_object(mock)
402        self.assertEqual([(res.content_type(), res.pk) for res in self.sb.search('*')['results']], [(u'core.mockmodel', u'20')])
403        self.sb.clear()
404
405    def test_remove_object(self):
406        self.mi.update()
407        self.assertEqual(self.sb.search('*')['hits'], 3)
408
409        mock = MockModel()
410        mock.pk = 20
411        mock.author = 'daniel%s' % mock.id
412        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
413
414        self.mi.update_object(mock)
415        self.assertEqual(self.sb.search('*')['hits'], 4)
416
417        self.mi.remove_object(mock)
418        self.assertEqual([(res.content_type(), res.pk) for res in self.sb.search('*')['results']], [(u'core.mockmodel', u'1'), (u'core.mockmodel', u'2'), (u'core.mockmodel', u'3')])
419
420        # Put it back so we can test passing kwargs.
421        mock = MockModel()
422        mock.pk = 20
423        mock.author = 'daniel%s' % mock.id
424        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
425
426        self.mi.update_object(mock)
427        self.assertEqual(self.sb.search('*')['hits'], 4)
428
429        self.mi.remove_object(mock, commit=False)
430        self.assertEqual([(res.content_type(), res.pk) for res in self.sb.search('*')['results']], [(u'core.mockmodel', u'1'), (u'core.mockmodel', u'2'), (u'core.mockmodel', u'3'), (u'core.mockmodel', u'20')])
431
432        self.sb.clear()
433
434    def test_clear(self):
435        self.mi.update()
436        self.assertGreater(self.sb.search('*')['hits'], 0)
437
438        self.mi.clear()
439        self.assertEqual(self.sb.search('*')['hits'], 0)
440
441    def test_reindex(self):
442        self.mi.reindex()
443        self.assertEqual([(res.content_type(), res.pk) for res in self.sb.search('*')['results']], [(u'core.mockmodel', u'1'), (u'core.mockmodel', u'2'), (u'core.mockmodel', u'3')])
444        self.sb.clear()
445
446    def test_inheritance(self):
447        try:
448            agmi = AltGoodMockSearchIndex()
449        except:
450            self.fail()
451
452        self.assertEqual(len(agmi.fields), 5)
453        self.assertTrue('text' in agmi.fields)
454        self.assertTrue(isinstance(agmi.fields['text'], indexes.CharField))
455        self.assertTrue('author' in agmi.fields)
456        self.assertTrue(isinstance(agmi.fields['author'], indexes.CharField))
457        self.assertTrue('pub_date' in agmi.fields)
458        self.assertTrue(isinstance(agmi.fields['pub_date'], indexes.DateTimeField))
459        self.assertTrue('extra' in agmi.fields)
460        self.assertTrue(isinstance(agmi.fields['extra'], indexes.CharField))
461        self.assertTrue('additional' in agmi.fields)
462        self.assertTrue(isinstance(agmi.fields['additional'], indexes.CharField))
463
464    def test_proper_field_resolution(self):
465        mrofsc = MROFieldsSearchChild()
466        mock = MockModel()
467        mock.pk = 20
468        mock.author = 'daniel%s' % mock.id
469        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
470        mock.test_a = 'This is A'
471        mock.test_b = 'This is B'
472
473        self.assertEqual(len(mrofsc.fields), 1)
474        prepped_data = mrofsc.prepare(mock)
475        self.assertEqual(len(prepped_data), 4)
476        self.assertEqual(prepped_data['text'], 'This is A')
477
478    def test_load_all_queryset(self):
479        self.assertEqual([obj.id for obj in self.cmi.load_all_queryset()], [2, 3])
480
481    def test_nullable(self):
482        mock = MockModel()
483        mock.pk = 20
484        mock.author = None
485        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
486
487        prepared_data = self.cnmi.prepare(mock)
488        self.assertEqual(len(prepared_data), 6)
489        self.assertEqual(sorted(prepared_data.keys()), ['author', 'author_exact', 'django_ct', 'django_id', 'id', 'text'])
490
491        prepared_data = self.cnmi.full_prepare(mock)
492        self.assertEqual(len(prepared_data), 4)
493        self.assertEqual(sorted(prepared_data.keys()), ['django_ct', 'django_id', 'id', 'text'])
494
495    def test_custom_facet_fields(self):
496        mock = MockModel()
497        mock.pk = 20
498        mock.author = 'daniel'
499        mock.pub_date = datetime.datetime(2009, 1, 31, 4, 19, 0)
500
501        prepared_data = self.gfmsi.prepare(mock)
502        self.assertEqual(len(prepared_data), 8)
503        self.assertEqual(sorted(prepared_data.keys()), ['author', 'author_foo', 'django_ct', 'django_id', 'id', 'pub_date', 'pub_date_exact', 'text'])
504
505        prepared_data = self.gfmsi.full_prepare(mock)
506        self.assertEqual(len(prepared_data), 8)
507        self.assertEqual(sorted(prepared_data.keys()), ['author', 'author_foo', 'django_ct', 'django_id', 'id', 'pub_date', 'pub_date_exact', 'text'])
508        self.assertEqual(prepared_data['author_foo'], u"Hi, I'm daniel")
509        self.assertEqual(prepared_data['pub_date_exact'], '2010-10-26T01:54:32')
510
511
512class BasicModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable):
513    class Meta:
514        model = MockModel
515
516
517class FieldsModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable):
518    class Meta:
519        model = MockModel
520        fields = ['author', 'pub_date']
521
522
523class ExcludesModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable):
524    class Meta:
525        model = MockModel
526        excludes = ['author', 'foo']
527
528
529class FieldsWithOverrideModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable):
530    foo = indexes.IntegerField(model_attr='foo')
531
532    class Meta:
533        model = MockModel
534        fields = ['author', 'foo']
535
536    def get_index_fieldname(self, f):
537        if f.name == 'author':
538            return 'author_bar'
539        else:
540            return f.name
541
542
543class YetAnotherBasicModelSearchIndex(indexes.ModelSearchIndex, indexes.Indexable):
544    text = indexes.CharField(document=True)
545
546    class Meta:
547        model = AThirdMockModel
548
549
550class GhettoAFifthMockModelSearchIndex(indexes.SearchIndex, indexes.Indexable):
551    text = indexes.CharField(document=True)
552
553    def get_model(self):
554        return AFifthMockModel
555
556    def index_queryset(self, using=None):
557        # Index everything,
558        return self.get_model().objects.complete_set()
559
560    def read_queryset(self, using=None):
561        return self.get_model().objects.all()
562
563
564class ReadQuerySetTestSearchIndex(indexes.SearchIndex, indexes.Indexable):
565    author = indexes.CharField(model_attr='author', document=True)
566
567    def get_model(self):
568        return AFifthMockModel
569
570    def read_queryset(self, using=None):
571        return self.get_model().objects.complete_set()
572
573
574class TextReadQuerySetTestSearchIndex(indexes.SearchIndex, indexes.Indexable):
575    text = indexes.CharField(model_attr='author', document=True)
576
577    def get_model(self):
578        return AFifthMockModel
579
580    def read_queryset(self, using=None):
581        return self.get_model().objects.complete_set()
582
583
584class ModelWithManyToManyFieldModelSearchIndex(indexes.ModelSearchIndex):
585    def get_model(self):
586        return ManyToManyLeftSideModel
587
588
589class ModelSearchIndexTestCase(TestCase):
590    def setUp(self):
591        super(ModelSearchIndexTestCase, self).setUp()
592        self.sb = connections['default'].get_backend()
593        self.bmsi = BasicModelSearchIndex()
594        self.fmsi = FieldsModelSearchIndex()
595        self.emsi = ExcludesModelSearchIndex()
596        self.fwomsi = FieldsWithOverrideModelSearchIndex()
597        self.yabmsi = YetAnotherBasicModelSearchIndex()
598        self.m2mmsi = ModelWithManyToManyFieldModelSearchIndex()
599
600    def test_basic(self):
601        self.assertEqual(len(self.bmsi.fields), 4)
602        self.assertTrue('foo' in self.bmsi.fields)
603        self.assertTrue(isinstance(self.bmsi.fields['foo'], indexes.CharField))
604        self.assertEqual(self.bmsi.fields['foo'].null, False)
605        self.assertEqual(self.bmsi.fields['foo'].index_fieldname, 'foo')
606        self.assertTrue('author' in self.bmsi.fields)
607        self.assertTrue(isinstance(self.bmsi.fields['author'], indexes.CharField))
608        self.assertEqual(self.bmsi.fields['author'].null, False)
609        self.assertTrue('pub_date' in self.bmsi.fields)
610        self.assertTrue(isinstance(self.bmsi.fields['pub_date'], indexes.DateTimeField))
611        self.assertTrue(isinstance(self.bmsi.fields['pub_date'].default, datetime.datetime))
612        self.assertTrue('text' in self.bmsi.fields)
613        self.assertTrue(isinstance(self.bmsi.fields['text'], indexes.CharField))
614        self.assertEqual(self.bmsi.fields['text'].document, True)
615        self.assertEqual(self.bmsi.fields['text'].use_template, True)
616
617    def test_fields(self):
618        self.assertEqual(len(self.fmsi.fields), 3)
619        self.assertTrue('author' in self.fmsi.fields)
620        self.assertTrue(isinstance(self.fmsi.fields['author'], indexes.CharField))
621        self.assertTrue('pub_date' in self.fmsi.fields)
622        self.assertTrue(isinstance(self.fmsi.fields['pub_date'], indexes.DateTimeField))
623        self.assertTrue('text' in self.fmsi.fields)
624        self.assertTrue(isinstance(self.fmsi.fields['text'], indexes.CharField))
625
626    def test_excludes(self):
627        self.assertEqual(len(self.emsi.fields), 2)
628        self.assertTrue('pub_date' in self.emsi.fields)
629        self.assertTrue(isinstance(self.emsi.fields['pub_date'], indexes.DateTimeField))
630        self.assertTrue('text' in self.emsi.fields)
631        self.assertTrue(isinstance(self.emsi.fields['text'], indexes.CharField))
632        self.assertNotIn('related_models', self.m2mmsi.fields)
633
634    def test_fields_with_override(self):
635        self.assertEqual(len(self.fwomsi.fields), 3)
636        self.assertTrue('author' in self.fwomsi.fields)
637        self.assertTrue(isinstance(self.fwomsi.fields['author'], indexes.CharField))
638        self.assertTrue('foo' in self.fwomsi.fields)
639        self.assertTrue(isinstance(self.fwomsi.fields['foo'], indexes.IntegerField))
640        self.assertTrue('text' in self.fwomsi.fields)
641        self.assertTrue(isinstance(self.fwomsi.fields['text'], indexes.CharField))
642
643    def test_overriding_field_name_with_get_index_fieldname(self):
644        self.assertTrue(self.fwomsi.fields['foo'].index_fieldname, 'foo')
645        self.assertTrue(self.fwomsi.fields['author'].index_fieldname, 'author_bar')
646
647    def test_float_integer_fields(self):
648        self.assertEqual(len(self.yabmsi.fields), 5)
649        self.assertEqual(sorted(self.yabmsi.fields.keys()), ['author', 'average_delay', 'pub_date', 'text', 'view_count'])
650        self.assertTrue('author' in self.yabmsi.fields)
651        self.assertTrue(isinstance(self.yabmsi.fields['author'], indexes.CharField))
652        self.assertEqual(self.yabmsi.fields['author'].null, False)
653        self.assertTrue('pub_date' in self.yabmsi.fields)
654        self.assertTrue(isinstance(self.yabmsi.fields['pub_date'], indexes.DateTimeField))
655        self.assertTrue(isinstance(self.yabmsi.fields['pub_date'].default, datetime.datetime))
656        self.assertTrue('text' in self.yabmsi.fields)
657        self.assertTrue(isinstance(self.yabmsi.fields['text'], indexes.CharField))
658        self.assertEqual(self.yabmsi.fields['text'].document, True)
659        self.assertEqual(self.yabmsi.fields['text'].use_template, False)
660        self.assertTrue('view_count' in self.yabmsi.fields)
661        self.assertTrue(isinstance(self.yabmsi.fields['view_count'], indexes.IntegerField))
662        self.assertEqual(self.yabmsi.fields['view_count'].null, False)
663        self.assertEqual(self.yabmsi.fields['view_count'].index_fieldname, 'view_count')
664        self.assertTrue('average_delay' in self.yabmsi.fields)
665        self.assertTrue(isinstance(self.yabmsi.fields['average_delay'], indexes.FloatField))
666        self.assertEqual(self.yabmsi.fields['average_delay'].null, False)
667        self.assertEqual(self.yabmsi.fields['average_delay'].index_fieldname, 'average_delay')
668
669
670class ModelWithManyToManyFieldAndAttributeLookupSearchIndexTestCase(TestCase):
671    def test_full_prepare(self):
672        index = ModelWithManyToManyFieldAndAttributeLookupSearchIndex()
673
674        left_model = ManyToManyLeftSideModel.objects.create()
675        right_model_1 = ManyToManyRightSideModel.objects.create(name='Right side 1')
676        right_model_2 = ManyToManyRightSideModel.objects.create()
677        left_model.related_models.add(right_model_1)
678        left_model.related_models.add(right_model_2)
679
680        result = index.full_prepare(left_model)
681
682        self.assertDictEqual(
683            result,
684            {
685                'django_ct': 'core.manytomanyleftsidemodel',
686                'django_id': '1',
687                'text': None,
688                'id': 'core.manytomanyleftsidemodel.1',
689                'related_models': ['Right side 1', 'Default name'],
690            }
691        )
692