Полнотекстовый поиск¶
Функции базы данных в модуле django.contrib.postgres.search упрощают использование ``системы полнотекстового поиска PostgreSQL <https://www.postgresql.org/docs/current/textsearch.html>`_.
Для примеров в этом документе мы будем использовать модели, определенные в Выполнение запросов.
См.также
Общий обзор поиска см. в документации по теме.
Поиск поиск¶
A common way to use full text search is to search a single term against a single column in the database. For example:
>>> Entry.objects.filter(body_text__search='Cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
Это создаст в базе данных to_tsvector из поля body_text и Plainto_tsquery из поискового запроса Cheese, оба используют конфигурацию поиска в базе данных по умолчанию. Результаты получаются путем сопоставления запроса и вектора.
Чтобы использовать поиск, ``“django.contrib.postgres“` должен быть в вашей настройке:INSTALLED_APPS.
Support for query expressions was added.
ПоискВектор¶
- class SearchVector(*expressions, config=None, weight=None)¶
Searching against a single field is great but rather limiting. The Entry
instances we’re searching belong to a Blog, which has a tagline field.
To query against both fields, use a 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 objects can be combined together, allowing you to reuse them.
For example:
>>> Entry.objects.annotate(
... search=SearchVector('body_text') + SearchVector('blog__tagline'),
... ).filter(search='Cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza Recipes>]
См. Изменение конфигурации поиска и Взвешивание запросов для объяснения параметров config и weight.
Поисковый запрос¶
- 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 terms can be combined logically to provide more flexibility:
>>> from django.contrib.postgres.search import SearchQuery
>>> SearchQuery('meat') & SearchQuery('cheese') # AND
>>> SearchQuery('meat') | SearchQuery('cheese') # OR
>>> ~SearchQuery('meat') # NOT
См. Изменение конфигурации поиска для объяснения параметра config.
Support for 'websearch' search type and query expressions in
SearchQuery.value were added.
ПоискРанк¶
- class SearchRank(vector, query, weights=None, normalization=None, cover_density=False)¶
So far, we’ve returned the results for which any match between the vector and the query are possible. It’s likely you may wish to order the results by some sort of relevancy. PostgreSQL provides a ranking function which takes into account how often the query terms appear in the document, how close together the terms are in the document, and how important the part of the document is where they occur. The better the match, the higher the value of the rank. To order by relevancy:
>>> 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, чтобы включить ранжирование плотности покрытия, что означает, что близость совпадающих условий запроса принимается во внимание.
Provide an integer to the normalization parameter to control rank
normalization. This integer is a bit mask, so you can combine multiple
behaviors:
>>> from django.db.models import Value
>>> Entry.objects.annotate(
... rank=SearchRank(
... vector,
... query,
... normalization=Value(2).bitor(Value(4)),
... )
... )
В документации PostgreSQL есть более подробная информация о «различных вариантах нормализации ранга».
The normalization and cover_density parameters were added.
ПоискЗаголовок¶
- 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.
Изменение конфигурации поиска¶
You can specify the config attribute to a SearchVector and
SearchQuery to use a different search configuration. This allows using
different language parsers and dictionaries as defined by the database:
>>> 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>]
The value of config could also be stored in another column:
>>> 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>]
Взвешивание запросов¶
Every field may not have the same relevance in a query, so you can set weights of various vectors before you combine them:
>>> 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')
The weight should be one of the following letters: D, C, B, A. By default,
these weights refer to the numbers 0.1, 0.2, 0.4, and 1.0,
respectively. If you wish to weight them differently, pass a list of four
floats to SearchRank as weights in the same order above:
>>> 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')
Производительность¶
Для использования какой-либо из этих функций не требуется специальная настройка базы данных, однако, если вы ищете более нескольких сотен записей, вы, вероятно, столкнетесь с проблемами производительности. Полнотекстовый поиск — более интенсивный процесс, чем, например, сравнение размера целого числа.
In the event that all the fields you’re querying on are contained within one particular model, you can create a functional index which matches the search vector you wish to use. The PostgreSQL documentation has details on creating indexes for full text search.
SearchVectorField¶
- class SearchVectorField¶
If this approach becomes too slow, you can add a SearchVectorField to your
model. You’ll need to keep it populated with triggers, for example, as
described in the PostgreSQL documentation. You can then query the field as
if it were an annotated SearchVector:
>>> Entry.objects.update(search_vector=SearchVector('body_text'))
>>> Entry.objects.filter(search_vector='cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
Сходство триграмм¶
Another approach to searching is trigram similarity. A trigram is a group of
three consecutive characters. In addition to the trigram_similar
lookup, you can use a couple of other expressions.
Чтобы использовать их, вам необходимо активировать расширение pg_trgm в PostgreSQL. Вы можете установить его с помощью операции миграции TrigramExtension.
ТриграммаСходство¶
- 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>]
ТриграммаРасстояние¶
- 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>]