Поиск¶
Обычной задачей веб-приложений является поиск некоторых данных в базе данных с помощью пользовательского ввода. В простом случае это может быть фильтрация списка объектов по категории. Более сложный вариант использования может потребовать поиска с взвешиванием, категоризацией, выделением, несколькими языками и т. д. В этом документе описаны некоторые возможные варианты использования и инструменты, которые вы можете использовать.
Мы будем использовать те же модели, что и в Выполнение запросов.
Варианты использования¶
Стандартные текстовые запросы¶
Текстовые поля имеют набор операций сопоставления. Например, вы можете разрешить поиск автора следующим образом:
>>> Author.objects.filter(name__contains="Terry")
[<Author: Terry Gilliam>, <Author: Terry Jones>]
Это очень хрупкое решение, поскольку оно требует, чтобы пользователь знал точную подстроку имени автора. Лучшим подходом могло бы быть сопоставление без учета регистра (icontains), но это лишь незначительно лучше.
Более продвинутые функции сравнения базы данных¶
Если вы используете PostgreSQL, Django предоставляет набор инструментов для конкретных баз данных, позволяющих использовать более сложные параметры запросов. Другие базы данных имеют другой набор инструментов, возможно, через плагины или пользовательские функции. В настоящее время Django не поддерживает их. Мы воспользуемся некоторыми примерами из PostgreSQL, чтобы продемонстрировать, какие функциональные возможности могут иметь базы данных.
Поиск в других базах данных
Все инструменты поиска, предоставляемые django.contrib.postgres, полностью построены на общедоступных API, таких как настраиваемые поиски и функции базы данных. В зависимости от вашей базы данных вы сможете создавать запросы для использования аналогичных API. Если есть конкретные вещи, которых невозможно достичь таким способом, пожалуйста, откройте заявку.
В приведенном выше примере мы определили, что поиск без учета регистра будет более полезным. При работе с неанглийскими именами дальнейшим улучшением является использование unaccented сравнения:
>>> 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>]
Теперь у нас другая проблема: более длинное имя «Хелена Бонем Картер» не отображается, поскольку оно намного длиннее. При поиске по триграммам учитываются все комбинации из трех букв и сравнивается их количество в строке поиска и в исходной строке. Для более длинного имени существует больше комбинаций, которые не появляются в исходной строке, поэтому оно больше не считается близким совпадением.
Правильный выбор функций сравнения здесь зависит от вашего конкретного набора данных, например, от используемого языка (языков) и типа искомого текста. Все примеры, которые мы видели, представляют собой короткие строки, где пользователь, скорее всего, введет что-то близкое (путем изменения определений) к исходным данным.
Поиск по документам¶
Стандартные операции с базой данных перестают быть полезным подходом, когда вы начинаете обрабатывать большие блоки текста. В то время как приведенные выше примеры можно рассматривать как операции над строкой символов, полнотекстовый поиск рассматривает фактические слова. В зависимости от используемой системы, вероятно, будут использоваться некоторые из следующих идей:
Игнорирование «стоп-слов», таких как «а», «the», «и».
Корректировка слов, чтобы слова «пони» и «пони» считались похожими.
Взвешивание слов на основе различных критериев, таких как частота их появления в тексте или важность полей, таких как заголовок или ключевые слова, в которых они появляются.
Существует множество альтернатив использованию программного обеспечения для поиска, наиболее известными из которых являются 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 Полнотекстовый поиск.