summaryrefslogtreecommitdiff
path: root/tests/postgres_tests/test_trigram.py
blob: a0502e0b9b13a457f4174cc43bc2ba597730ad46 (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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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