Извлечение фильтров: сопоставление с границами слов через регулярные выражения

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

Проблема: от естественного языка к структурированным фильтрам

Когда пользователь ищет «мини пк с 16 гб оперативной памяти», нам нужно извлечь:

{
  "Form Factor": "Mini PC",
  "Main Memory": "16"
}

Эти структурированные фильтры позволяют:

  • Фильтрацию товаров: показывать только подходящие товары

  • Фасетную навигацию: отображать доступные варианты фильтров

  • Генерацию страниц запросов: создавать SEO-оптимизированные страницы

  • Похожие запросы: находить похожие запросы

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

Алгоритм: сопоставление с границами слов через регулярные выражения

Шаг 1: Загрузка соответствий фраз

Мы загружаем соответствия фраз и фильтров, сгенерированные SEO-пайплайном. Эти соответствия связывают фразы со значениями фильтров, позволяя выполнять поиск от фразы к соответствующему ключу и значению фильтра.

Мы инвертируем эту структуру для быстрого поиска, чтобы можно было искать по фразе, а не по фильтру:

phrase_to_filter = {
    "16gb ram": ("Main Memory", "16"),
    "16 gb ram": ("Main Memory", "16"),
    "mini pc": ("Form Factor", "Mini PC"),
    "mini computer": ("Form Factor", "Mini PC")
}

Шаг 2: Нормализация запроса

Преобразуем запрос в нижний регистр для регистронезависимого сопоставления.

Шаг 3: Сопоставление фраз с границами слов

Для каждой фразы в соответствиях мы проверяем, встречается ли она в запросе, используя якоря границ слов в регулярных выражениях.

Якоря \b гарантируют, что мы сопоставляем целые слова, а не подстроки: «mini pc» соответствует «mini pc with ram», но НЕ соответствует «minipc» (без пробела), а «16gb» соответствует «16gb ram», но НЕ соответствует «216gb» (частичное совпадение).

Шаг 4: Сбор всех совпадений

Мы перебираем все фразы и собираем совпадающие фильтры. Для «mini pc with 16gb ram» это дает фильтры, такие как {"Form Factor": "Mini PC", "Main Memory": "16"}.

Почему границы слов?

Границы слов предотвращают ложные совпадения:

Без границ слов:

  • «i5» соответствовало бы «i5000» (неверно)

  • «ram» соответствовало бы «program» (неверно)

  • «pc» соответствовало бы «pcie» (неверно)

С границами слов: «i5» соответствует «i5 processor» ✅, но НЕ соответствует «i5000» ❌; «ram» соответствует «16gb ram» ✅, но НЕ соответствует «program» ❌. Якорь \b гарантирует, что мы сопоставляем только по границам слов.

Обработка множественных совпадений

Если несколько фраз соответствуют одному и тому же фильтру, побеждает последнее совпадение:

# Запрос: "mini pc small computer"
# И "mini pc", и "small computer" соответствуют "Form Factor:Mini PC"
# Результат: {"Form Factor": "Mini PC"} (дубликаты удалены)

Если несколько фраз соответствуют разным значениям одного и того же фильтра, побеждает последнее совпадение:

# Запрос: "8gb 16gb ram"
# "8gb" → Main Memory:8
# "16gb" → Main Memory:16
# Результат: {"Main Memory": "16"} (побеждает последнее совпадение)

На практике пользователи редко указывают конфликтующие значения, поэтому это не является проблемой.

Приоритет фраз

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

Оптимизация производительности

Кэширование

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

Резервный вариант с Valkey

Сначала мы пытаемся загрузить соответствия из Valkey (форк Redis), а в случае промаха возвращаемся к JSON-файлам:

  1. Проверяем Valkey для быстрого поиска в памяти
  2. Загружаем из JSON, если промах в Valkey
  3. Сохраняем в Valkey с истечением срока действия кэша

Это снижает задержку для последующих запросов.

Сопоставление с регулярными выражениями

Шаблоны используют re.search() с якорями границ слов для эффективного сопоставления целых слов без ложных частичных совпадений.

Интеграция с поисковым сервисом

Извлечение фильтров реализовано как конечная точка API-сервиса, которая принимает поисковые запросы и возвращает извлеченные фильтры.

Сервис:

  1. Принимает поисковый запрос на вход
  2. Загружает соответствия фраз и фильтров в память
  3. Нормализует и сопоставляет фразы с границами слов
  4. Возвращает структурированные пары ключ-значение фильтров

Основной веб-сервер вызывает этот сервис для извлечения фильтров из пользовательских запросов:

filters = extract_filters_from_query("mini pc 16gb ram")
# Возвращает: {"Form Factor": "Mini PC", "Main Memory": "16"}

Такое разделение ответственности позволяет:

  • Независимое масштабирование: извлечение фильтров может работать на отдельном сервере

  • Изоляцию кэширования: сервис управляет своим собственным кэшем соответствий

  • Перезапуск сервиса: сервис можно перезапускать независимо, не затрагивая основной веб-сервер

Подробности см. в разделе Архитектура поискового сервиса.

Логирование запросов

Каждое извлечение фильтров логируется для использования SEO-пайплайном. Эти логи передаются обратно в SEO-пайплайн для обнаружения новых шаблонов запросов и улучшения соответствий фраз со временем.

Варианты использования

Страницы запросов (/q/)

Страницы запросов извлекают фильтры из слага URL, чтобы определить, какие товары отображать.

Поисковый API (/api/search)

Поисковый API извлекает фильтры из поискового запроса, чтобы найти и вернуть подходящие товары.

Автодополнение

Подсказки автодополнения извлекают фильтры, чтобы предоставить предварительный просмотр фильтров вместе с поисковыми подсказками.

Похожие запросы

Генерация похожих запросов использует извлеченные фильтры для поиска похожих запросов:

Запрос: "mini pc 16gb ram"
→ Фильтры: {"Form Factor": "Mini PC", "Main Memory": "16"}
→ Найти запросы с похожими фильтрами
→ Предложить: "mini pc 32gb ram", "mini pc 16gb ssd"

Подробности см. в разделе Генерация похожих запросов.

Обработка ошибок

Отсутствующие соответствия

Если соответствия фраз не загружены, мы возвращаем пустые фильтры. Это предотвращает сбои, когда SEO-пайплайн еще не запущен.

Некорректные запросы

Пустые запросы или запросы, состоящие только из пробелов, возвращают пустые фильтры.

Ошибки регулярных выражений

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

Характеристики производительности

Извлечение фильтров работает эффективно благодаря сопоставлению с границами слов через регулярные выражения и агрессивному кэшированию:

  • Загрузка соответствий происходит один раз при запуске

  • Извлечение на один запрос ограничено производительностью ЦП (сопоставление с регулярными выражениями)

  • Использование памяти для кэшей остается управляемым

  • В продакшене высокий процент попаданий в кэш

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

Ограничения

Зависимость от порядка фраз

Мы сопоставляем фразы в порядке итерации, который не гарантирован. Если две фразы перекрываются, побеждает последнее совпадение:

# Запрос: "mini pc"
# Фразы: ["mini", "mini pc"]
# Если "mini" проверяется последней, она перезаписывает "mini pc"

На практике этого не происходит, потому что:

  • Более длинные фразы более специфичны и появляются первыми в отсортированных соответствиях

  • Перекрывающиеся фразы обычно соответствуют одному и тому же значению фильтра

Отсутствие комбинирования фраз

Мы не комбинируем несколько фраз в одно значение фильтра:

# Запрос: "dual core quad core"
# Результат: {"Cores": "4"} (побеждает последнее совпадение)
# НЕ: {"Cores": ["2", "4"]} (множественные значения)

Это сделано намеренно — пользователи редко указывают несколько значений для одного и того же фильтра.

Отсутствие отрицания

Мы не обрабатываем отрицание (например, «мини пк без Windows»). Отрицание редко встречается в поисковых запросах, поэтому в настоящее время не поддерживается.

Ссылки

Технические концепции

Документация Python

Связанные статьи

Резюме

Мы извлекаем фильтры из поисковых запросов с помощью сопоставления с границами слов через регулярные выражения:

  • Загружаем соответствия: соответствия фраз и фильтров из SEO-пайплайна

  • Нормализуем запрос: преобразуем в нижний регистр

  • Сопоставляем фразы: используем границы слов \b для сопоставления целых слов

  • Собираем фильтры: строим словарь пар ключ-значение фильтров

  • Агрессивно кэшируем: кэш в памяти + резервный вариант с Valkey

  • Логируем запросы: передаем обратно в SEO-пайплайн

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


← Назад к указателю документации