1. from django.test import modify_settings
    
  2. 
    
  3. from . import PostgreSQLTestCase
    
  4. from .models import CharFieldModel, TextFieldModel
    
  5. 
    
  6. try:
    
  7.     from django.contrib.postgres.search import (
    
  8.         TrigramDistance,
    
  9.         TrigramSimilarity,
    
  10.         TrigramWordDistance,
    
  11.         TrigramWordSimilarity,
    
  12.     )
    
  13. except ImportError:
    
  14.     pass
    
  15. 
    
  16. 
    
  17. @modify_settings(INSTALLED_APPS={"append": "django.contrib.postgres"})
    
  18. class TrigramTest(PostgreSQLTestCase):
    
  19.     Model = CharFieldModel
    
  20. 
    
  21.     @classmethod
    
  22.     def setUpTestData(cls):
    
  23.         cls.Model.objects.bulk_create(
    
  24.             [
    
  25.                 cls.Model(field="Matthew"),
    
  26.                 cls.Model(field="Cat sat on mat."),
    
  27.                 cls.Model(field="Dog sat on rug."),
    
  28.             ]
    
  29.         )
    
  30. 
    
  31.     def test_trigram_search(self):
    
  32.         self.assertQuerysetEqual(
    
  33.             self.Model.objects.filter(field__trigram_similar="Mathew"),
    
  34.             ["Matthew"],
    
  35.             transform=lambda instance: instance.field,
    
  36.         )
    
  37. 
    
  38.     def test_trigram_word_search(self):
    
  39.         obj = self.Model.objects.create(
    
  40.             field="Gumby rides on the path of Middlesbrough",
    
  41.         )
    
  42.         self.assertSequenceEqual(
    
  43.             self.Model.objects.filter(field__trigram_word_similar="Middlesborough"),
    
  44.             [obj],
    
  45.         )
    
  46. 
    
  47.     def test_trigram_similarity(self):
    
  48.         search = "Bat sat on cat."
    
  49.         # Round result of similarity because PostgreSQL 12+ uses greater
    
  50.         # precision.
    
  51.         self.assertQuerysetEqual(
    
  52.             self.Model.objects.filter(
    
  53.                 field__trigram_similar=search,
    
  54.             )
    
  55.             .annotate(similarity=TrigramSimilarity("field", search))
    
  56.             .order_by("-similarity"),
    
  57.             [("Cat sat on mat.", 0.625), ("Dog sat on rug.", 0.333333)],
    
  58.             transform=lambda instance: (instance.field, round(instance.similarity, 6)),
    
  59.             ordered=True,
    
  60.         )
    
  61. 
    
  62.     def test_trigram_word_similarity(self):
    
  63.         search = "mat"
    
  64.         self.assertSequenceEqual(
    
  65.             self.Model.objects.filter(
    
  66.                 field__trigram_word_similar=search,
    
  67.             )
    
  68.             .annotate(
    
  69.                 word_similarity=TrigramWordSimilarity(search, "field"),
    
  70.             )
    
  71.             .values("field", "word_similarity")
    
  72.             .order_by("-word_similarity"),
    
  73.             [
    
  74.                 {"field": "Cat sat on mat.", "word_similarity": 1.0},
    
  75.                 {"field": "Matthew", "word_similarity": 0.75},
    
  76.             ],
    
  77.         )
    
  78. 
    
  79.     def test_trigram_similarity_alternate(self):
    
  80.         # Round result of distance because PostgreSQL 12+ uses greater
    
  81.         # precision.
    
  82.         self.assertQuerysetEqual(
    
  83.             self.Model.objects.annotate(
    
  84.                 distance=TrigramDistance("field", "Bat sat on cat."),
    
  85.             )
    
  86.             .filter(distance__lte=0.7)
    
  87.             .order_by("distance"),
    
  88.             [("Cat sat on mat.", 0.375), ("Dog sat on rug.", 0.666667)],
    
  89.             transform=lambda instance: (instance.field, round(instance.distance, 6)),
    
  90.             ordered=True,
    
  91.         )
    
  92. 
    
  93.     def test_trigram_word_similarity_alternate(self):
    
  94.         self.assertSequenceEqual(
    
  95.             self.Model.objects.annotate(
    
  96.                 word_distance=TrigramWordDistance("mat", "field"),
    
  97.             )
    
  98.             .filter(
    
  99.                 word_distance__lte=0.7,
    
  100.             )
    
  101.             .values("field", "word_distance")
    
  102.             .order_by("word_distance"),
    
  103.             [
    
  104.                 {"field": "Cat sat on mat.", "word_distance": 0},
    
  105.                 {"field": "Matthew", "word_distance": 0.25},
    
  106.             ],
    
  107.         )
    
  108. 
    
  109. 
    
  110. class TrigramTextFieldTest(TrigramTest):
    
  111.     """
    
  112.     TextField has the same behavior as CharField regarding trigram lookups.
    
  113.     """
    
  114. 
    
  115.     Model = TextFieldModel