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 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158
|
from . import PostgreSQLTestCase
from .models import CharFieldModel, TextFieldModel
try:
from django.contrib.postgres.search import (
TrigramDistance,
TrigramSimilarity,
TrigramStrictWordDistance,
TrigramStrictWordSimilarity,
TrigramWordDistance,
TrigramWordSimilarity,
)
except ImportError:
pass
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],
)
self.assertSequenceEqual(
self.Model.objects.filter(field__trigram_word_similar="Middle"),
[obj],
)
def test_trigram_strict_word_search_matched(self):
obj = self.Model.objects.create(
field="Gumby rides on the path of Middlesbrough",
)
self.assertSequenceEqual(
self.Model.objects.filter(
field__trigram_strict_word_similar="Middlesborough"
),
[obj],
)
self.assertSequenceEqual(
self.Model.objects.filter(field__trigram_strict_word_similar="Middle"),
[],
)
def test_trigram_similarity(self):
search = "Bat sat on cat."
# Round result of similarity because PostgreSQL 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_strict_word_similarity(self):
search = "matt"
self.assertSequenceEqual(
self.Model.objects.filter(field__trigram_word_similar=search)
.annotate(word_similarity=TrigramStrictWordSimilarity(search, "field"))
.values("field", "word_similarity")
.order_by("-word_similarity"),
[
{"field": "Cat sat on mat.", "word_similarity": 0.5},
{"field": "Matthew", "word_similarity": 0.44444445},
],
)
def test_trigram_similarity_alternate(self):
# Round result of distance because PostgreSQL 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},
],
)
def test_trigram_strict_word_distance(self):
self.assertSequenceEqual(
self.Model.objects.annotate(
word_distance=TrigramStrictWordDistance("matt", "field"),
)
.filter(word_distance__lte=0.7)
.values("field", "word_distance")
.order_by("word_distance"),
[
{"field": "Cat sat on mat.", "word_distance": 0.5},
{"field": "Matthew", "word_distance": 0.5555556},
],
)
class TrigramTextFieldTest(TrigramTest):
"""
TextField has the same behavior as CharField regarding trigram lookups.
"""
Model = TextFieldModel
|