Полнотекстовый поиск¶
Функции базы данных в модуле django.contrib.postgres.search
облегчают использование модуля PostgreSQL full text search engine.
Для примеров в этом документе мы будем использовать модели, определенные в Создание запросов.
См.также
Высокоуровневый обзор поиска см. в topic documentation.
Поиск 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)[исходный код]¶
Поиск по одному полю - это замечательно, но довольно ограничено. Экземпляры 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')[исходный код]¶
SearchQuery
переводит предоставленные пользователем термины в объект поискового запроса, который база данных сравнивает с поисковым вектором. По умолчанию все слова, предоставленные пользователем, пропускаются через алгоритмы стемминга, а затем ищутся совпадения для всех полученных терминов.
Если search_type
равно 'plain'
, что является значением по умолчанию, термины рассматриваются как отдельные ключевые слова. Если search_type
равно 'phrase'
, то термины рассматриваются как одна фраза. Если search_type
- это 'raw'
, то вы можете предоставить форматированный поисковый запрос с терминами и операторами. Если search_type
равно 'websearch'
, то вы можете предоставить форматированный поисковый запрос, подобный тому, который используется поисковыми системами. Для работы 'websearch'
требуется PostgreSQL ≥ 11. Прочитайте статью Full Text Search docs в 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)[исходный код]¶
До сих пор мы возвращали результаты, для которых возможно любое совпадение вектора и запроса. Вполне вероятно, что вы захотите упорядочить результаты по релевантности. В 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 есть более подробная информация о different rank normalization options.
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)[исходный код]¶
Принимает одно текстовое поле или выражение, запрос, конфигурацию и набор опций. Возвращает выделенные результаты поиска.
Установите параметры 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 есть более подробная информация о highlighting search results.
Пример использования:
>>> 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 есть подробная информация о creating indexes for full text search.
SearchVectorField
¶
-
class
SearchVectorField
[исходный код]¶
Если такой подход становится слишком медленным, то в модель можно добавить SearchVectorField
. Вам нужно будет поддерживать его заполненным, например, триггерами, как описано в PostgreSQL documentation. Затем к полю можно выполнять запросы, как если бы оно было аннотированным 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 extension на PostgreSQL. Вы можете установить его с помощью операции миграции TrigramExtension
.
TrigramSimilarity
¶
-
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>]
TrigramWordSimilarity
¶
-
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()
, за исключением того, что границы экстентов должны совпадать с границами слов.
TrigramDistance
¶
-
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>]
TrigramWordDistance
¶
-
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)[исходный код]¶
Принимает строку или выражение, а также имя поля или выражение. Возвращает триграммное расстояние между двумя аргументами.