Поиск


Распространенной задачей веб-приложений является поиск некоторых данных в базе данных с вводом пользователя. В простом случае это может быть фильтрация списка объектов по категории. Более сложный вариант использования может потребовать поиска с взвешиванием, категоризацией, выделением, несколькими языками и т.д. В этом документе объясняются некоторые возможные варианты использования и инструменты, которые вы можете использовать.

Мы будем ссылаться на те же модели, что и в Создание запросов.

Случаи использования

Стандартные текстовые запросы
В текстовых полях есть набор простых операций сопоставления. Например, вы можете разрешить поиск автора следующим образом:

>>> Author.objects.filter(name__contains='Terry')
[<Author: Terry Gilliam>, <Author: Terry Jones>]
Это очень хрупкое решение, поскольку оно требует, чтобы пользователь знал точную подстроку имени автора. Лучшим подходом могло бы быть соответствие без учета регистра (icontains), но это лишь незначительно лучше.

Расширенные функции сравнения базы данных
Если вы используете PostgreSQL, Django предоставляет набор инструментов для конкретных баз данных, чтобы вы могли использовать более сложные параметры запросов. В других базах данных есть другой набор инструментов, возможно, через плагины или определяемые пользователем функции. Django в настоящее время не поддерживает их. Мы будем использовать несколько примеров из PostgreSQL, чтобы продемонстрировать, какие функциональные возможности могут иметь базы данных.

Поиск в других базах данных

Все инструменты поиска, предоставляемые django.contrib.postgres, полностью построены на общедоступных API, таких как пользовательский поиск и функции базы данных. В зависимости от вашей базы данных вы должны иметь возможность создавать запросы, позволяющие использовать аналогичные API. Если есть определенные вещи, которых нельзя достичь таким образом, откройте заявку.

В приведенном выше примере мы определили, что поиск без учета регистра будет более полезным. При работе с неанглийскими именами дальнейшее улучшение заключается в использовании сравнение без акцента:

>>> Author.objects.filter(name__unaccent__icontains='Helen')
[<Author: Helen Mirren>, <Author: Helena Bonham Carter>, <Author: Hélène Joy>]
Это показывает другую проблему, когда мы сравниваем с другим написанием имени. В этом случае мы имеем дело с асимметрией - при поиске по запросу Helen будет найдено слово Helena или Hélène, но не наоборот. Другой вариант - использовать сравнение trigram_similar, которое сравнивает последовательности букв.

Например:

>>> Author.objects.filter(name__unaccent__lower__trigram_similar='Hélène')
[<Author: Helen Mirren>, <Author: Hélène Joy>]
Теперь у нас другая проблема - более длинное имя «Хелена Бонэм Картер» не появляется, потому что оно намного длиннее. При поиске по триграмме рассматриваются все комбинации из трех букв и сравнивается их количество в строке поиска и в исходной строке. Для более длинного имени есть больше комбинаций, которые не появляются в исходной строке, поэтому оно больше не считается близким совпадением.

Правильный выбор функций сравнения здесь зависит от вашего конкретного набора данных, например, используемого языка (языков) и типа текста, в котором выполняется поиск. Все примеры, которые мы видели, относятся к коротким строкам, где пользователь может ввести что-то близкое (с помощью различных определений) к исходным данным.

Поиск по документам
Стандартные операции с базой данных перестают быть полезным подходом, когда вы начинаете рассматривать большие блоки текста. В то время как приведенные выше примеры можно рассматривать как операции над строкой символов, при полнотекстовом поиске рассматриваются фактические слова. В зависимости от используемой системы, вероятно, будут использоваться некоторые из следующих идей:

Игнорирование «стоп-слов», таких как «a», «the», «and».
Ключевые слова, так что «лошадь» и «лошади» считаются похожими.
Взвешивание слов на основе различных критериев, таких как частота их появления в тексте или важность полей, таких как заголовок или ключевые слова, в которых они появляются.
Существует множество альтернатив для использования программного обеспечения для поиска, наиболее известными из которых являются Elastic и Solr. Это полноценные поисковые решения на основе документов. Чтобы использовать их с данными из моделей Django, вам понадобится слой, который переводит ваши данные в текстовый документ, включая обратные ссылки на идентификаторы базы данных. Когда поиск с использованием механизма возвращает определенный документ, вы можете найти его в базе данных. Существует множество сторонних библиотек, которые призваны помочь в этом процессе.

Поддержка PostgreSQL
PostgreSQL имеет собственную встроенную реализацию полнотекстового поиска. Хотя он не такой мощный, как некоторые другие поисковые системы, он имеет то преимущество, что находится внутри вашей базы данных и поэтому может быть легко объединен с другими реляционными запросами, такими как категоризация.

Модуль django.contrib.postgres предоставляет несколько помощников для выполнения этих запросов. Например, простой запрос может заключаться в выборе всех записей блога, в которых упоминается «сыр»:

>>> Entry.objects.filter(body_text__search='cheese')
[<Entry: Cheese on Toast recipes>, <Entry: Pizza recipes>]
Вы также можете фильтровать комбинацию полей и связанные модели:

>>> Entry.objects.annotate(
... search=SearchVector('blog__tagline', 'body_text'),
... ).filter(search='cheese')
[
<Entry: Cheese on Toast recipes>,
<Entry: Pizza Recipes>,
<Entry: Dairy farming in Argentina>,
]
Смотрите подробные сведения о contrib.postgres в документе Полнотекстовый поиск.