Полнотекстовый поиск
Функции базы данных в модуле django.contrib.postgres.search облегчают использование механизма полнотекстового поиска PostgreSQL.
В примерах этого документа мы будем использовать модели, определенные в создании запросов.
См. также
Для общего обзора поиска см. документацию по теме.
Поиск по виду search
Распространённый способ использования полнотекстового поиска — поиск одного термина в одном столбце базы данных. Например:
>>> Entry.objects.filter(body_text__search="Cheese") [<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
Это создаёт to_tsvector в базе данных из поля body_text и plainto_tsquery из поискового запроса 'Cheese', оба с использованием стандартной конфигурации поиска базы данных. Результаты получаются путём сопоставления запроса и вектора.
Чтобы использовать вид поиска search, 'django.contrib.postgres' должен быть в вашем INSTALLED_APPS.
SearchVector
-
class SearchVector(*expressions, config=None, weight=None)[source]
Поиск по одному полю удобен, но ограничен. Объекты 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.
SearchQuery
-
class SearchQuery(value, config=None, search_type='plain')[source]
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.
SearchRank
-
class SearchRank(vector, query, weights=None, normalization=None, cover_density=False)[source]
До сих пор мы возвращали результаты, для которых любое соответствие между вектором и запросом возможно. Вероятно, вы захотите упорядочить результаты по какой-то степени релевантности. 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 содержатся дополнительные сведения о различных вариантах нормализации ранга.
SearchHeadline
-
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)[source]
Принимает одно текстовое поле или выражение, запрос, конфигурацию и набор опций. Возвращает выделенные результаты поиска.
Установите параметры 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[source]
Если этот подход окажется слишком медленным, вы можете добавить 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.
TrigramSimilarity
-
class TrigramSimilarity(expression, string, **extra)[source]
Принимает имя поля или выражение и строку или выражение. Возвращает сходство триграмм между двумя аргументами.
Пример использования:
>>> 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>]
TrigramWordSimilarity
-
class TrigramWordSimilarity(string, expression, **extra)[source]
Принимает строку или выражение и имя поля или выражение. Возвращает сходство триграмм слов между двумя аргументами.
Пример использования:
>>> 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)[source]
Принимает строку или выражение и имя поля или выражение. Возвращает строгое сходство триграмм слов между двумя аргументами. Аналогично TrigramWordSimilarity(), за исключением того, что он принудительно согласует границы с границами слов.
TrigramDistance
-
class TrigramDistance(expression, string, **extra)[source]
Принимает имя поля или выражение и строку или выражение. Возвращает расстояние триграмм между двумя аргументами.
Пример использования:
>>> 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>]
TrigramWordDistance
-
class TrigramWordDistance(string, expression, **extra)[source]
Принимает строку или выражение и имя поля или выражение. Возвращает расстояние триграмм слов между двумя аргументами.
Пример использования:
>>> 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)[source]
Принимает строку или выражение и имя поля или выражение. Возвращает строгое расстояние триграмм слов между двумя аргументами.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/5.2/ref/contrib/postgres/search/