Поиск по полному тексту
Функции базы данных в модуле 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.1/ref/contrib/postgres/search/