Фильтры рубрики, метки и видимости
Полнотекстовый запрос отвечает на вопрос, какие документы похожи на введённые слова. Теперь добавим другой вопрос: какие документы вообще разрешено рассматривать в выбранной рубрике. Результат урока — точные фильтры по разделу и метке, которые сужают кандидатов и сохраняют обязательную границу публичного поиска.
Продолжаем query.py проекта «Индекс». Фильтры уже присутствуют в полном итоговом файле; листинги ниже показывают отдельные части этого файла. Программа и поисковые запросы не запускались. Ожидаемые изменения описывают договор функции и подготовленного корпуса, а не наблюдавшуюся выдачу рабочего сайта.
Совпадение слов и условие допуска
Возьмём строку «HTTP» без ограничения раздела. В библиотеке есть материал о браузерном Fetch, статья о серверном кешировании и заметка о публикации приложения. Все они могут содержать термин. Когда читатель выбирает backend, он ожидает серверные материалы, а не просто дополнительный балл для этой рубрики. Поэтому выбор раздела должен исключать другие разделы.
В bool этому соответствует filter. Условия там определяют допуск документа, не добавляя собственный вклад в оценку релевантности. Смысл такого разделения подробно определён в документации Boolean query. Для нашего примера это позволяет независимо обсуждать правильность рубрики и качество порядка оставшихся карточек.
Следующий фрагмент находится в начале build_query, после проверки внешних параметров:
filters = [
{'term': {'published': True}},
{'term': {'audience': 'public'}}
]
if section:
filters.append({'term': {'section': section}})
if tag:
filters.append({'term': {'tags': tag}})
Условия объединяются логическим «и»: документ опубликован, предназначен для публичной аудитории и соответствует всем добавленным ограничениям. Выбор метки cache внутри backend не означает «backend или cache». Для такого альтернативного режима понадобился бы отдельный явно описанный договор, иначе интерфейс и код начнут понимать одни элементы управления по-разному.
term сравнивает индексированное точное значение. Он не выполняет полнотекстовый анализ пользовательской строки так, как match. Поэтому рубрики и метки получили тип keyword. Применить term к русскому тексту и ожидать автоматическое совпадение словоформ было бы ошибкой: движок сравнивал бы переданное значение с уже подготовленными термами поля.
Предсказуемые значения параметров
Разрешённые разделы перечислены в ALLOWED_SECTIONS: backend, frontend и deployment. Это разделы нашего небольшого корпуса. Полный сайт имеет более широкую структуру, но учебный API не обязан принимать любой идентификатор из внешнего каталога без согласования. Неизвестный раздел возвращает ошибку входного запроса, а не пустую страницу с загадочным отсутствием результатов.
Метка ограничена длиной и простым форматом. Это техническая проверка параметра, а не заявление, что такая метка существует. Запрос по корректной, но отсутствующей метке закономерно вернёт ноль совпадений. Не следует автоматически исправлять её на похожую и незаметно менять фильтр: читатель может искать точно определённую технологию.
В исходниках нет нормализации рубрики на стороне запроса. Значения уже фиксированы договором корпуса и проверяются по списку. Для меток также используем согласованную запись в нижнем регистре. Если в исходнике случайно появились HTTP и http как разные метки, это ошибка подготовки данных, которую желательно исправить перед индексированием, а не скрывать неоднозначным интерфейсом.
Сама строка поиска получает иной режим: пробелы по краям убираются, далее используется анализатор текстовых полей. Так рядом существуют две политики обработки ввода. Они различаются по назначению значений: естественная поисковая фраза допускает языковые формы, а идентификатор фильтра должен совпадать с известным точным значением.
Закрытый документ не участвует в публичном запросе
Добавим в корпус служебную заметку article-111 с audience: staff. Она содержит слово HTTP и метку cache. Публичный запрос по этим словам и метке всё равно не должен включить её. Обязательное условие видимости записано в функции построения запроса, а браузер не получает параметра, который может его убрать.
Ошибочная архитектура выглядела бы так: запрос возвращает всё, gateway удаляет закрытые карточки, но оставляет общее количество и группы меток. Тогда пользователь узнаёт о скрытом совпадении через косвенные данные. В нашем варианте фильтр действует раньше, поэтому поисковые карточки и следующие агрегации рассматривают уже допустимое множество документов.
Однако это ещё не система входа сотрудников. Публичный gateway всегда ищет только публичные материалы. Чтобы создать служебный режим, нужна доверенная проверка личности и отдельное построение условий на сервере. Поле audience само по себе не устанавливает права пользователя, а текст staff в адресной строке не подтверждает его принадлежность редакции.
Ожидаемые отношения множеств можно записать так:
HTTP → все допустимые совпадения по слову
HTTP + backend → допустимые совпадения только серверного раздела
HTTP + backend + cache → пересечение раздела и метки
staff-документ → не участвует ни в одной публичной выборке
Это описание условий, поэтому здесь нет выдуманного числа результатов. При будущем выполнении сравнивайте идентификаторы конкретных документов. Если количество уменьшилось неожиданно, причина может быть в исходной метке, а не в ранжировании. Правильное множество важнее красивого порядка неправильных карточек.
Изменение условий и оценки
Фильтр не добавляет балл релевантности, но изменение набора документов и распределённая обработка запроса не дают универсального обещания одинаковых числовых _score во всех контекстах. Мы не используем оценки как стабильный предметный показатель качества материала. Их назначение — упорядочить кандидатов данного поискового запроса.
Если продукту требуется сортировка по дате вместо релевантности, это отдельный выбор. Не нужно помещать дату в must только ради изменения порядка. Условие «изменён после даты» является фильтром диапазона, а порядок «сначала свежие» задаётся сортировкой. Разделение помогает объяснить, почему документ исчез или переместился.
Нулевой результат при слишком узких условиях также является нормальным состоянием. Интерфейс может предложить снять метку, но не должен молча отменять выбранные пользователем ограничения. Иначе запрос начнёт возвращать документы из другой рубрики и создаст впечатление, что фильтр не работает. Позже gateway передаст понятную форму ответа даже без карточек.
Несколько выбранных меток потребовали бы нового явного правила. Режим «хотя бы одна метка» строится иначе, чем «все метки одновременно». В текущем API разрешена одна tag, поэтому такая неоднозначность не возникает. Если расширите интерфейс множественным выбором, сначала запишите ожидаемые документы для каждой комбинации. Только затем меняйте DSL, сохраняя обязательную видимость независимо от способа объединения пользовательских меток.
Для самостоятельного разбора назначьте одному публичному документу две метки и предскажите результат двух отдельных запросов. Затем сделайте его служебным и объясните, какие данные должны исчезнуть одновременно. В следующем уроке соберём фасеты — счётчики групп внутри этого же допустимого множества.