Система автодополнения поисковых запросов
Пользователь вводит 'найк' в поиске, и система мгновенно предлагает релевантные варианты: 'Nike кроссовки', 'Nike одежда', 'Nike аксессуары'. Идеальная задержка — менее 100 мс от ввода символа до появления подсказки. Любая задержка свыше 200 мс снижает конверсию на 20%. Мы — команда с опытом более 5 лет, реализовавшая автодополнение для 50+ проектов. Разберём, как построить надёжное автодополнение со стеком Elasticsearch, Swift, Kotlin, Flutter, и какие ошибки обойти.
Автодополнение — одна из самых требовательных к скорости функций. Пользователь ожидает подсказки быстрее, чем успевает заметить их появление. Запросы должны быть релевантными, а не просто популярными. Мы гарантируем стабильную работу при 10 000 запросов в секунду и покрытие 80% самых частых префиксов.
Почему простой prefix-поиск не работает
Наивная реализация — хранить частые запросы в словаре и искать по префиксу. Это работает для 'nike' → 'nike кроссовки', но ломается для орфографических ошибок ('найк'), транслитерации ('krossovki' vs 'кроссовки'), семантически близких запросов ('беговая обувь' при вводе 'кросс') и персонализации (одинаковый запрос 'платье' для разных пользователей).
Архитектура production-ready автодополнения
Trie + нечёткий поиск для скорости
Базовый слой — Trie на популярных запросах с нечётким поиском через BK-tree или симметричное удаление (Symmetric Delete). Elasticsearch с маппингом completion field — готовое решение с fuzzy matching из коробки:
{
"mappings": {
"properties": {
"suggest": {
"type": "completion",
"analyzer": "standard",
"contexts": [
{"name": "category", "type": "category"}
]
},
"weight": {"type": "integer"}
}
}
}
# Поиск автодополнений через ES Completion Suggester
async def get_suggestions(prefix: str, category: str, user_id: str) -> list[str]:
response = await es.search(
index="search_suggestions",
body={
"suggest": {
"query_suggest": {
"prefix": prefix,
"completion": {
"field": "suggest",
"size": 8,
"fuzzy": {"fuzziness": "AUTO"},
"contexts": {"category": [category]}
}
}
}
}
)
return [hit["_source"]["query"] for hit in response["suggest"]["query_suggest"][0]["options"]]
Эффективность такого подхода подтверждена документацией Elasticsearch.
Персонализированный ранжировщик подсказок
Базовые подсказки из ES переранжируются с учётом истории пользователя. Признаки ранжировщика:
-
global_frequency— сколько раз этот запрос вводили все пользователи -
user_query_history_match— вводил ли пользователь похожий запрос раньше -
user_category_affinity— насколько категория запроса близка интересам пользователя -
recency_boost— трендовые запросы за последние 24 часа получают буст
On-device кэш для мгновенного отклика
Первые 3–5 символов запроса покрывают около 80% популярных prefix-комбинаций. Кэшируем подсказки для 500 самых частых префиксов на устройстве при старте приложения:
// Android: предзагрузка популярных prefix-подсказок
class AutocompleteCache(context: Context) {
private val db = Room.databaseBuilder(context, AutocompleteDatabase::class.java, "autocomplete").build()
suspend fun preload() {
val popularPrefixes = autocompleteApi.getPopularPrefixes(limit = 500)
db.suggestionDao().insertAll(popularPrefixes)
}
suspend fun getSuggestions(prefix: String): List<String> {
// сначала проверяем локальный кэш
val cached = db.suggestionDao().getSuggestions(prefix)
if (cached.isNotEmpty()) return cached
// если нет в кэше — запрос на сервер
return autocompleteApi.getSuggestions(prefix)
}
}
Debounce и cancellation на клиенте
Каждый символ не должен триггерить новый запрос. Debounce 150–200 мс + отмена предыдущего in-flight запроса:
// iOS: debounced автодополнение с cancellation
class SearchViewModel: ObservableObject {
@Published var suggestions: [String] = []
private var searchTask: Task<Void, Never>?
func onQueryChanged(_ query: String) {
searchTask?.cancel()
guard query.count >= 2 else { suggestions = []; return }
searchTask = Task {
try? await Task.sleep(nanoseconds: 150_000_000) // 150ms debounce
guard !Task.isCancelled else { return }
let results = try? await autocompleteService.getSuggestions(query)
await MainActor.run {
suggestions = results ?? []
}
}
}
}
Логирование выбора подсказки
Отметим: когда пользователь тапает на подсказку, логируем позицию в списке, prefix при котором она была выбрана, итоговый запрос. Эти данные — обучающая выборка для следующей версии ранжировщика.
Сравнение методов автодополнения
| Метод | Латентность | Персонализация | Ошибки/транслитерация | Сложность |
|---|---|---|---|---|
| Префиксный поиск (Trie) | <50 мс | Нет | Не поддерживает | Низкая |
| Elasticsearch Completion | <100 мс | Через контексты | Fuzzy matching AUTO | Средняя |
| On-device Trie + ранжировщик | <20 мс | Да (история, аффинность) | Частично | Высокая |
Как ускорить отклик автодополнения?
Основные методы ускорения: предзагрузка on-device кэша, debounce с отменой предыдущих запросов, использование Trie на сервере. Мы также применяем асинхронное логирование, чтобы не блокировать UI. В результате latency снижается на 40% по сравнению с наивной реализацией.
Что входит в реализацию под ключ?
Реализация включает базовое автодополнение (Elasticsearch Completion с fuzzy matching, без персонализации — 2-3 дня), персонализированный ранжировщик (учёт истории пользователя, категорийная аффинность, тренды — +1 неделя), on-device кэш (Room для Android и Core Data для iOS с предзагрузкой 500 префиксов — +2-3 дня) и клиентскую логику (debounce, cancellation, UI интеграция — +1-2 дня). В результате вы получаете рабочее решение с исходным кодом, документацию по API, доступ к хранилищу подсказок, обучение команды, техническую поддержку в течение месяца после деплоя.
Почему персонализация важна для автодополнения?
Персонализация повышает релевантность подсказок для каждого пользователя. При вводе 'платье' один пользователь ищет вечерние платья, другой — повседневные. Ранжировщик учитывает историю запросов, категорийную аффинность и тренды. Это увеличивает кликабельность подсказок на 25-35% и сокращает время поиска.
Процесс работы
- Анализ поисковых логов — выгружаем топ-1000 запросов, выявляем паттерны опечаток, языки, транслитерацию.
- Настройка Elasticsearch Completion Suggester — с fuzzy matching, контекстными фильтрами.
- Разработка персонализированного ранжировщика — на основе истории пользователя.
- Реализация on-device кэша — для Android (Room) и iOS (Core Data).
- Интеграция на клиенте — debounce, cancellation, UI.
- Тестирование и деплой — нагрузочное тестирование, App Store / Google Play.
Подробнее о стоимости
Стоимость одного запроса автодополнения на инфраструктуре составляет менее $0.001. Экономия на инфраструктуре при использовании on-device кэша достигает 40%.Свяжитесь с нами для предварительной оценки проекта. Закажите реализацию автодополнения под ключ — получите надежное решение с гарантией качества.







