Полнотекстовый поиск¶
Функции базы данных в модуле django.contrib.postgres.search упрощают использование ``системы полнотекстового поиска PostgreSQL <https://www.postgresql.org/docs/current/textsearch.html>`_.
Для примеров в этом документе мы будем использовать модели, определенные в Выполнение запросов.
См.также
Общий обзор поиска см. в документации по теме.
Поиск поиск¶
Распространенный способ использования полнотекстового поиска — поиск одного термина в одном столбце базы данных. Например:
>>> Entry.objects.filter(body_text__search="Cheese")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
Это создаст в базе данных to_tsvector из поля body_text и Plainto_tsquery из поискового запроса Cheese, оба используют конфигурацию поиска в базе данных по умолчанию. Результаты получаются путем сопоставления запроса и вектора.
Чтобы использовать поиск, ``“django.contrib.postgres“` должен быть в вашей настройке:INSTALLED_APPS.
ПоискВектор¶
- class SearchVector(*expressions, config=None, weight=None)¶
Поиск по одному полю — это хорошо, но весьма ограниченно. Экземпляры Entry, которые мы ищем, принадлежат Blog, который имеет поле tagline. Чтобы запросить оба поля, используйте SearchVector:
>>> from django.contrib.postgres.search import SearchVector
>>> Entry.objects.annotate(
... search=SearchVector("body_text", "blog__tagline"),
... ).filter(search="Cheese")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
Аргументами SearchVector могут быть любые Expression или имя поля. Несколько аргументов будут объединены через пробел, чтобы документ поиска включал их все.
Объекты SearchVector можно комбинировать вместе, что позволяет повторно использовать их. Например:
>>> Entry.objects.annotate(
... search=SearchVector("body_text") + SearchVector("blog__tagline"),
... ).filter(search="Cheese")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
См. Изменение конфигурации поиска и Взвешивание запросов для объяснения параметров config и weight.
Поисковый запрос¶
- class SearchQuery(value, config=None, search_type='plain')¶
SearchQuery преобразует термины, предоставленные пользователем, в объект поискового запроса, который база данных сравнивает с вектором поиска. По умолчанию все слова, предоставленные пользователем, проходят через алгоритмы стемминга, а затем он ищет совпадения для всех полученных терминов.
Если search_type имеет значение 'plain', что является значением по умолчанию, термины обрабатываются как отдельные ключевые слова. Если search_type равен 'phrase', термины рассматриваются как одна фраза. Если search_type является 'raw', вы можете предоставить форматированный поисковый запрос с терминами и операторами. Если search_type равен 'websearch', вы можете предоставить отформатированный поисковый запрос, аналогичный тому, который используется поисковыми системами в Интернете. Для 'websearch' требуется PostgreSQL ≥ 11. Прочтите документацию ``Полнотекстовый поиск`_ PostgreSQL, чтобы узнать о различиях и синтаксисе. Примеры:
>>> from django.contrib.postgres.search import SearchQuery
>>> SearchQuery("red tomato") # two keywords
>>> SearchQuery("tomato red") # same results as above
>>> SearchQuery("red tomato", search_type="phrase") # a phrase
>>> SearchQuery("tomato red", search_type="phrase") # a different phrase
>>> SearchQuery("'tomato' & ('red' | 'green')", search_type="raw") # boolean operators
>>> SearchQuery(
... "'tomato' ('red' OR 'green')", search_type="websearch"
... ) # websearch operators
Термины SearchQuery можно логически комбинировать для обеспечения большей гибкости:
>>> from django.contrib.postgres.search import SearchQuery
>>> SearchQuery("meat") & SearchQuery("cheese") # AND
>>> SearchQuery("meat") | SearchQuery("cheese") # OR
>>> ~SearchQuery("meat") # NOT
См. Изменение конфигурации поиска для объяснения параметра config.
ПоискРанк¶
- class SearchRank(vector, query, weights=None, normalization=None, cover_density=False)¶
До сих пор мы возвращали результаты, для которых возможно любое совпадение вектора и запроса. Вероятно, вы захотите упорядочить результаты по какой-либо релевантности. PostgreSQL предоставляет функцию ранжирования, которая учитывает, как часто термины запроса появляются в документе, насколько близко друг к другу находятся термины в документе и насколько важна та часть документа, где они встречаются. Чем лучше совпадение, тем выше значение ранга. Чтобы упорядочить по релевантности:
>>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
>>> vector = SearchVector("body_text")
>>> query = SearchQuery("cheese")
>>> Entry.objects.annotate(rank=SearchRank(vector, query)).order_by("-rank")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
См. Взвешивание запросов для объяснения параметра weights.
Установите для параметра Cover_density значение True, чтобы включить ранжирование плотности покрытия, что означает, что близость совпадающих условий запроса принимается во внимание.
Укажите целое число в параметреnormalization для управления нормализацией ранга. Это целое число является битовой маской, поэтому вы можете комбинировать несколько вариантов поведения:
>>> from django.db.models import Value
>>> Entry.objects.annotate(
... rank=SearchRank(
... vector,
... query,
... normalization=Value(2).bitor(Value(4)),
... )
... )
В документации PostgreSQL есть более подробная информация о «различных вариантах нормализации ранга».
ПоискЗаголовок¶
- class SearchHeadline(expression, query, config=None, start_sel=None, stop_sel=None, max_words=None, min_words=None, short_word=None, highlight_all=None, max_fragments=None, fragment_delimiter=None)¶
Принимает одно текстовое поле или выражение, запрос, конфигурацию и набор параметров. Возвращает выделенные результаты поиска.
Установите для параметров start_sel и stop_sel строковые значения, которые будут использоваться для переноса выделенных терминов запроса в документ. По умолчанию в PostgreSQL используются <b> и </b>.
Укажите целочисленные значения для параметров max_words и min_words, чтобы определить самые длинные и самые короткие заголовки. По умолчанию PostgreSQL — 35 и 15.
Укажите целочисленное значение параметра short_word, чтобы отбросить слова этой или меньшей длины в каждом заголовке. Значение по умолчанию в PostgreSQL — 3.
Установите для параметра «highlight_all» значение «True», чтобы использовать весь документ вместо фрагмента и игнорировать параметры «max_words», «min_words» и «short_word». В PostgreSQL это отключено по умолчанию.
Укажите ненулевое целое значение для параметра max_fragments, чтобы установить максимальное количество отображаемых фрагментов. В PostgreSQL это отключено по умолчанию.
Установите строковый параметр fragment_delimiter, чтобы настроить разделитель между фрагментами. По умолчанию в PostgreSQL используется " ... ".
В документации PostgreSQL есть более подробная информация о «выделении результатов поиска».
Пример использования:
>>> from django.contrib.postgres.search import SearchHeadline, SearchQuery
>>> query = SearchQuery("red tomato")
>>> entry = Entry.objects.annotate(
... headline=SearchHeadline(
... "body_text",
... query,
... start_sel="<span>",
... stop_sel="</span>",
... ),
... ).get()
>>> print(entry.headline)
Sandwich with <span>tomato</span> and <span>red</span> cheese.
См. Изменение конфигурации поиска для объяснения параметра config.
Изменение конфигурации поиска¶
Вы можете указать атрибут config для SearchVector и SearchQuery, чтобы использовать другую конфигурацию поиска. Это позволяет использовать различные языковые парсеры и словари, определенные в базе данных:
>>> from django.contrib.postgres.search import SearchQuery, SearchVector
>>> Entry.objects.annotate(
... search=SearchVector("body_text", config="french"),
... ).filter(search=SearchQuery("œuf", config="french"))
[<Entry: Pain perdu>]
Значение config также может быть сохранено в другом столбце:
>>> from django.db.models import F
>>> Entry.objects.annotate(
... search=SearchVector("body_text", config=F("blog__language")),
... ).filter(search=SearchQuery("œuf", config=F("blog__language")))
[<Entry: Pain perdu>]
Взвешивание запросов¶
Каждое поле может иметь разную релевантность в запросе, поэтому вы можете установить веса различных векторов, прежде чем объединять их:
>>> from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
>>> vector = SearchVector("body_text", weight="A") + SearchVector(
... "blog__tagline", weight="B"
... )
>>> query = SearchQuery("cheese")
>>> Entry.objects.annotate(rank=SearchRank(vector, query)).filter(rank__gte=0.3).order_by(
... "rank"
... )
Вес должен быть одной из следующих букв: D, C, B, A. По умолчанию эти веса относятся к числам 0,1, 0,2, 0,4 и 1,0 соответственно. Если вы хотите присвоить им разные веса, передайте список из четырех чисел с плавающей запятой в SearchRank как weights в том же порядке, что и выше:
>>> rank = SearchRank(vector, query, weights=[0.2, 0.4, 0.6, 0.8])
>>> Entry.objects.annotate(rank=rank).filter(rank__gte=0.3).order_by("-rank")
Производительность¶
Для использования какой-либо из этих функций не требуется специальная настройка базы данных, однако, если вы ищете более нескольких сотен записей, вы, вероятно, столкнетесь с проблемами производительности. Полнотекстовый поиск — более интенсивный процесс, чем, например, сравнение размера целого числа.
В случае, если все поля, к которым вы запрашиваете, содержатся в одной конкретной модели, вы можете создать функциональный индекс GIN или GiST, который соответствует вектору поиска, который вы хотите использовать. Например:
GinIndex(
SearchVector("body_text", "headline", config="english"),
name="search_vector_idx",
)
В документации PostgreSQL есть подробности о создании индексов для полнотекстового поиска.
SearchVectorField¶
- class SearchVectorField¶
Если этот подход становится слишком медленным, вы можете добавить в свою модель SearchVectorField. Вам потребуется, чтобы он был заполнен триггерами, например, как описано в документации PostgreSQL. Затем вы можете запросить поле, как если бы оно было аннотированным SearchVector:
>>> Entry.objects.update(search_vector=SearchVector("body_text"))
>>> Entry.objects.filter(search_vector="cheese")
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
Сходство триграмм¶
Другой подход к поиску — сходство триграмм. Триграмма — это группа из трех последовательных символов. В дополнение к поискам trigram_similar, trigram_word_similar и trigram_strict_word_similar вы можете использовать несколько других выражений.
Чтобы использовать их, вам необходимо активировать расширение pg_trgm в PostgreSQL. Вы можете установить его с помощью операции миграции TrigramExtension.
ТриграммаСходство¶
- class TrigramSimilarity(expression, string, **extra)¶
Принимает имя поля или выражение, а также строку или выражение. Возвращает сходство триграмм между двумя аргументами.
Пример использования:
>>> from django.contrib.postgres.search import TrigramSimilarity
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Katie Stephens"
>>> Author.objects.annotate(
... similarity=TrigramSimilarity("name", test),
... ).filter(
... similarity__gt=0.3
... ).order_by("-similarity")
[<Author: Katy Stevens>, <Author: Stephen Keats>]
ТриграммаСловоСходство¶
- class TrigramWordSimilarity(string, expression, **extra)¶
Принимает строку или выражение, а также имя или выражение поля. Возвращает сходство слов триграммы между двумя аргументами.
Пример использования:
>>> from django.contrib.postgres.search import TrigramWordSimilarity
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Kat"
>>> Author.objects.annotate(
... similarity=TrigramWordSimilarity(test, "name"),
... ).filter(
... similarity__gt=0.3
... ).order_by("-similarity")
[<Author: Katy Stevens>]
TrigramStrictWordSimilarity¶
- class TrigramStrictWordSimilarity(string, expression, **extra)¶
Принимает строку или выражение, а также имя или выражение поля. Возвращает строгое сходство слов триграммы между двумя аргументами. Аналогично TrigramWordSimilarity(), за исключением того, что он заставляет границы экстента совпадать с границами слов.
ТриграммаРасстояние¶
- class TrigramDistance(expression, string, **extra)¶
Принимает имя поля или выражение, а также строку или выражение. Возвращает триграммное расстояние между двумя аргументами.
Пример использования:
>>> from django.contrib.postgres.search import TrigramDistance
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Katie Stephens"
>>> Author.objects.annotate(
... distance=TrigramDistance("name", test),
... ).filter(
... distance__lte=0.7
... ).order_by("distance")
[<Author: Katy Stevens>, <Author: Stephen Keats>]
ТриграммаСловаДистанция¶
- class TrigramWordDistance(string, expression, **extra)¶
Принимает строку или выражение, а также имя или выражение поля. Возвращает расстояние в слове триграммы между двумя аргументами.
Пример использования:
>>> from django.contrib.postgres.search import TrigramWordDistance
>>> Author.objects.create(name="Katy Stevens")
>>> Author.objects.create(name="Stephen Keats")
>>> test = "Kat"
>>> Author.objects.annotate(
... distance=TrigramWordDistance(test, "name"),
... ).filter(
... distance__lte=0.7
... ).order_by("distance")
[<Author: Katy Stevens>]
TrigramStrictWordDistance¶
- class TrigramStrictWordDistance(string, expression, **extra)¶
Принимает строку или выражение, а также имя или выражение поля. Возвращает строгое расстояние по словам триграммы между двумя аргументами.