AACSearch
Руководства

Встраиваемый виджет

Полноценный поисковый интерфейс одним тегом script и короткоживущим токеном сессии — контракт сессии, все data-атрибуты, события, темизация и CSP.

aacsearch.js — самодостаточный бандл на InstantSearch. Добавьте на страницу два элемента, и он отрисует строку поиска, счётчик результатов, список попаданий, фасеты-чекбоксы, сортировку, пагинацию и недавние запросы — обращаясь к вашему индексу через прокси встраивания AACSearch и не раскрывая API-ключ платформы.

На странице никогда нет API-ключа. Ваш сервер обменивает ключ на короткоживущий токен сессии только для поиска через POST /api/v1/embed/session и подставляет этот токен в страницу как data-aac-token.

Тег script

Сначала выпустите токен на бэкенде (см. Сессия встраивания):

curl -sS -X POST "$AAC_API/embed/session" \
  -H "Authorization: Bearer $AAC_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "index": "documentation", "expires_in": 900 }'

Затем положите полученный token на страницу:

<script
	src="https://search.example.com/embed/v1/aacsearch.js"
	data-aac-token="aac_sess_…"
	data-aac-target="#aacsearch"
></script>

<div id="aacsearch"></div>

Это вся интеграция со стороны страницы. Скрипт находит цель, отправляет токен на /api/v1/embed/config, добавляет свои стили и монтирует интерфейс — без сборки, фреймворка и npm-пакета.

АтрибутОбязателенПо умолчаниюЗначение
data-aac-tokenда—Токен сессии из POST /api/v1/embed/session. Без него скрипт не монтируется автоматически
data-aac-targetнет#aacsearchCSS-селектор точки монтирования
data-aac-endpointнетвычисляется из script.srcБазовый URL развёртывания, например https://search.example.com
data-aac-placeholderнетиз настроек сессииПереопределяет подсказку в поле ввода
data-aac-client-idнетанонимный UUID в localStorageПостоянный идентификатор посетителя (например, id вошедшего пользователя): поиски и клики складываются в одну историю на разных устройствах

Атрибута data-aac-key нет: API-ключ платформы не должен появляться ни в HTML, ни в data--атрибуте, ни в URL. Токен — только для поиска, привязан к одному индексу и истекает сам.

Если data-aac-endpoint не указан, скрипт вычисляет его из собственного src, отбрасывая всё начиная с /embed/ — поэтому тег, скопированный из панели встраивания консоли, работает на любом домене.

Если тег стоит в <head>, виджет ждёт DOMContentLoaded; если скрипт выполняется после построения DOM, монтирование происходит сразу. В обоих случаях целевой элемент должен существовать к моменту монтирования.

Версионный и безверсионный URL

URLКешПрименение
/embed/v1/aacsearch.jsmax-age=31536000, immutableПродакшен. Версия в пути: v1 не меняется
/embed/aacsearch.jsmax-age=300Стейджинг и предпросмотр; исправления подхватываются за пять минут

Оба отдаются с access-control-allow-origin: * и cross-origin-resource-policy: cross-origin, поэтому работают с любого домена. В продакшене фиксируйте версионный URL: скрипт без версии, который сам себя обновляет, — неожиданная зависимость.

Ручная инициализация

Для одностраничного приложения или когда нужно смонтировать виджет после смены маршрута, вызывайте глобальный объект вместо data-атрибутов:

<script src="https://search.example.com/embed/v1/aacsearch.js"></script>
<script>
	window.AACSearch.init({
		token: "aac_sess_…", // выпущен на сервере, это не API-ключ
		target: "#docs-search",
		endpoint: "https://search.example.com",
		placeholder: "Поиск по документации…",
		clientId: "user_123", // необязательно
	});
</script>
interface InitOptions {
	token: string; // токен сессии встраивания, НЕ API-ключ
	target: string | HTMLElement;
	endpoint?: string;
	placeholder?: string;
	clientId?: string;
}

init возвращает промис, который завершается после настройки виджета. При сбое он не бросает исключение, а рисует сообщение на месте:

СитуацияЧто видит пользователь
Нет tokenSearch is not configured for this page. (и console.error со ссылкой на POST /api/v1/embed/session)
Токен истёк (401)Search session expired — reload the page.
Любой другой сбой /embed/configSearch is unavailable right now (503). (код статуса меняется)

Если вызвать init до появления цели, в консоль попадёт [AACSearch] target not found, и функция вернётся.

window.AACSearch также предоставляет version (1.0.0 в текущей сборке — самый быстрый способ ответить на вопрос «та ли сборка на странице?»), trackConversion() и getRecommendations().

Контракт сессии

Все запросы бандла идут на прокси /api/v1/embed/* вашего развёртывания и аутентифицируются токеном сессии в JSON-теле — никогда в URL, поэтому токен не попадает в историю браузера, заголовки referrer и журналы доступа.

ЗапросЭндпоинтКогда
НастройкиPOST /api/v1/embed/configОдин раз при монтировании: возвращает index, scope, expires_at и settings
ПоискPOST /api/v1/embed/searchКаждый запрос, смена страницы, фасета или сортировки
СобытияPOST /api/v1/embed/eventsview при первом показе результата, click по результату, conversion из trackConversion()
РекомендацииPOST /api/v1/embed/recommendationsКогда включён showRelated, и из getRecommendations()

Поиск не идёт из браузера напрямую в поисковый сервис. Прокси проверяет токен в каждом запросе, применяет фильтр тенанта на сервере и использует тот же конвейер, что и публичный API (журнал запросов, правила, значения по умолчанию, ослабление запроса, did-you-mean), поэтому трафик виджета виден в аналитике. Браузер не видит ни адреса движка, ни ключа, ни имени коллекции. Каждый проксируемый поиск списывается с квоты searches; один запрос к прокси несёт не более 5 поисков, page ≤ 20, а per_page обрезается до 50.

Срок жизни сессии — по умолчанию 900 секунд (максимум 3600). Виджет не обновляет токен сам: после истечения запросы возвращают 401, и виджет показывает The search session expired — reload the page. Для страниц, которые остаются открытыми, выпускайте свежий токен до истечения и снова вызывайте init — или перезагружайте страницу.

События

Виджет сам отправляет события поведения — по возможности и с keepalive, поэтому сбой учёта никогда не ломает страницу:

// Вызывается из вашего кода, например после оформления заказа:
window.AACSearch.trackConversion({ documentId: "sku-123", valueCents: 4900, query: "кроссовки" });

События несут token сессии, идентификатор посетителя session_id (client id виджета), document_id, position в выдаче и query. Они попадают в те же таблицы поведения, что и REST API событий, с атрибуцией по виджету, если токен выпущен для сохранённого виджета.

Рекомендации

const { items, mode } = await window.AACSearch.getRecommendations({
	seedDocumentId: "sku-123",
	limit: 4,
});

getRecommendations возвращает { items: [], mode: "unavailable" }, пока виджет не смонтирован, и { items: [], mode: "error" } при сбое запроса. Встроенный блок «For you» появляется только когда для индекса включён showRelated и включены рекомендации.

Темизация и раскладка

Палитра берётся из настроек встраивания индекса, они редактируются в консоли в разделе Search → Embed с живым предпросмотром — изменения применяются при следующей загрузке страницы (и следующем выпуске токена), без передеплоя.

НастройкаПо умолчаниюДействие
accent#2563ebКнопки, обводка фокуса, активная страница, ссылки, фон mark
darkfalseТёмная палитра (фон #0b1220)
perPage8Результатов на страницу
placeholderSearch the docs…Подсказка в поле ввода
showStatstrueСтрока с числом результатов
showRecenttrueЧипы недавних запросов
showVoicefalseГолосовой ввод браузера в строке поиска
showImagefalseИзображение результата, если оно есть в документе
showRelatedfalseБлок «For you» под результатами
facets[]Поля фасетов; консоль ограничивает выбор шестью
sortOptionsрелевантность + created_at:descСписок сортировки, скрыт при единственном варианте
queryBytitle,contentПоля поиска

CSS-переменные задаются на корневом элементе, поэтому стили сайта могут их переопределить без правки бандла:

.aac-ui {
	--aac-accent: #0f766e;
	--aac-bg: #ffffff;
	--aac-fg: #0f172a;
	--aac-muted: #64748b;
	--aac-border: #e2e8f0;
	--aac-soft: #f8fafc;
	border-radius: 14px;
}

Раскладка — колонка фасетов 200 px плюс результаты, ниже 720 px переходит в одну колонку. Для попадания используются title, content, url и необязательное поле category для чипа; попадание без url рендерится как <div>, а не ссылка.

Что виджет делает во время работы

  • Поиск по мере ввода с явной кнопкой отправки и кнопкой сброса.
  • Фасеты в виде списков с чекбоксами (до 8 значений), построенные на коннекторе refinement-list, чтобы пустые значения не ломали рендер.
  • Сортировка через setQueryParameter("sortBy", …); первая опция должна иметь значение default.
  • Недавние запросы в localStorage под ключом aac:recent:<index>, не более 5 записей, только для запросов от 3 символов, вернувших результаты. Очистка данных сайта их удаляет.
  • Идентификатор посетителя хранится в localStorage (aac_client_id), если вы не передали data-aac-client-id; по нему строятся история поиска и атрибуция кликов. Cookie не устанавливаются.
  • Голос через виджет voiceSearch из InstantSearch, когда включён: используется распознавание речи браузера, аудио не уходит на ваш бэкенд.
  • Скелетоны на время первого запроса и дружелюбное пустое состояние: No results с предложением сократить запрос.
  • Локализация нескольких встроенных строк (поиск, сброс, нет результатов, результаты) для en и ru по navigator.language; остальные языки показывают английский. Всё остальное берётся из ваших документов.

Ограничения

  • Нет состояния в URL. Виджет не синхронизирует запрос и фильтры с адресной строкой, поэтому выдачу нельзя передать ссылкой, а кнопка «назад» не восстанавливает поиск. Нужен свой роутинг — добавьте его.
  • Один виджет на страницу — документированная конфигурация. Стили добавляются один раз, а CSS-переменные глобальны, поэтому второй экземпляр унаследует палитру первого.
  • Нет шаблонов результатов. Разметка попадания фиксирована; для своих карточек либо переопределяйте CSS, либо стройте интерфейс поверх /embed/search.
  • Нет автоматического обновления токена. Сессия живёт по умолчанию 15 минут, максимум час; для долгоживущих страниц выпускайте токен заново и повторно вызывайте init.
  • Заголовки фасетов — имена полей, а не человекочитаемые названия, поэтому называйте поля соответственно.

Content Security Policy

Весь трафик виджета идёт на один origin — ваше развёртывание: сам скрипт и вызовы /api/v1/embed/*. Браузер не обращается к поисковому движку, поэтому origin движка в политике не нужен.

script-src  'self' https://search.example.com;
connect-src 'self' https://search.example.com;
style-src   'self' 'unsafe-inline';

Бандл вставляет стили инлайном, поэтому style-src требует 'unsafe-inline' (или аккуратного переопределения, если вы заменяете стили своими). connect-src — директива, о которой забывают: скрипт загружается, но вызовы /embed/config, /embed/search и /embed/events без неё падают во вкладке Network. Если API отдаётся с другого хоста, чем скрипт, разрешите оба origin.

On this page