summaryrefslogtreecommitdiff
path: root/tests/postgres_tests/test_trigram.py
blob: 079a32a19b25395222e90a5e21a3e4863d088265 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
from django.test import modify_settings

from . import PostgreSQLTestCase
from .models import CharFieldModel, TextFieldModel

try:
    from django.contrib.postgres.search import (
        TrigramDistance, TrigramSimilarity, TrigramWordDistance,
        TrigramWordSimilarity,
    )
except ImportError:
    pass


@modify_settings(INSTALLED_APPS={'append': 'django.contrib.postgres'})
class TrigramTest(PostgreSQLTestCase):
    Model = CharFieldModel

    @classmethod
    def setUpTestData(cls):
        cls.Model.objects.bulk_create([
            cls.Model(field='Matthew'),
            cls.Model(field='Cat sat on mat.'),
            cls.Model(field='Dog sat on rug.'),
        ])

    def test_trigram_search(self):
        self.assertQuerysetEqual(
            self.Model.objects.filter(field__trigram_similar='Mathew'),
            ['Matthew'],
            transform=lambda instance: instance.field,
        )

    def test_trigram_word_search(self):
        obj = self.Model.objects.create(
            field='Gumby rides on the path of Middlesbrough',
        )
        self.assertSequenceEqual(
            self.Model.objects.filter(field__trigram_word_similar='Middlesborough'),
            [obj],
        )

    def test_trigram_similarity(self):
        search = 'Bat sat on cat.'
        # Round result of similarity because PostgreSQL 12+ uses greater
        # precision.
        self.assertQuerysetEqual(
            self.Model.objects.filter(
                field__trigram_similar=search,
            ).annotate(similarity=TrigramSimilarity('field', search)).order_by('-similarity'),
            [('Cat sat on mat.', 0.625), ('Dog sat on rug.', 0.333333)],
            transform=lambda instance: (instance.field, round(instance.similarity, 6)),
            ordered=True,
        )

    def test_trigram_word_similarity(self):
        search = 'mat'
        self.assertSequenceEqual(
            self.Model.objects.filter(
                field__trigram_word_similar=search,
            ).annotate(
                word_similarity=TrigramWordSimilarity(search, 'field'),
            ).values('field', 'word_similarity').order_by('-word_similarity'),
            [
                {'field': 'Cat sat on mat.', 'word_similarity': 1.0},
                {'field': 'Matthew', 'word_similarity': 0.75},
            ],
        )

    def test_trigram_similarity_alternate(self):
        # Round result of distance because PostgreSQL 12+ uses greater
        # precision.
        self.assertQuerysetEqual(
            self.Model.objects.annotate(
                distance=TrigramDistance('field', 'Bat sat on cat.'),
            ).filter(distance__lte=0.7).order_by('distance'),
            [('Cat sat on mat.', 0.375), ('Dog sat on rug.', 0.666667)],
            transform=lambda instance: (instance.field, round(instance.distance, 6)),
            ordered=True,
        )

    def test_trigram_word_similarity_alternate(self):
        self.assertSequenceEqual(
            self.Model.objects.annotate(
                word_distance=TrigramWordDistance('mat', 'field'),
            ).filter(
                word_distance__lte=0.7,
            ).values('field', 'word_distance').order_by('word_distance'),
            [
                {'field': 'Cat sat on mat.', 'word_distance': 0},
                {'field': 'Matthew', 'word_distance': 0.25},
            ],
        )


class TrigramTextFieldTest(TrigramTest):
    """
    TextField has the same behavior as CharField regarding trigram lookups.
    """
    Model = TextFieldModel