Полнотекстовый поиск
Функции базы данных в модуле 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'), name='body_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 запросов, вы можете использовать несколько других выражений.
Для их использования необходимо активировать расширение 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>]
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>]
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/4.1/ref/contrib/postgres/search/