Полнотекстовый поиск
Функции базы данных в модуле 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')
Производительность
Специальная конфигурация базы данных не требуется для использования этих функций, однако, если вы ищете более чем в нескольких сотнях записей, вы можете столкнуться с проблемами производительности. Полнотекстовый поиск — более интенсивный процесс, чем сравнение размера целого числа, например.
В случае, если все поля, по которым вы выполняете поиск, содержатся в одной конкретной модели, вы можете создать функциональный индекс, соответствующий вектору поиска, который вы хотите использовать. Подробная информация о создании индексов для полнотекстового поиска содержится в документации 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.0/ref/contrib/postgres/search/