Поиск по полному тексту
Функции базы данных в модуле 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)
Поиск по одному полю — хорошо, но довольно ограниченно. Примеры 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. Обратитесь к документации 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 содержится более подробная информация о различных вариантах нормализации ранга.
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.
Пример использования:
>>> 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.
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)
Принимает строку или выражение и имя поля или выражение. Возвращает строгое триграмное расстояние слов между двумя аргументами.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.2/ref/contrib/postgres/search/