AACsearch
SDKРецепты

Аналитика кликов

Отслеживайте, по каким результатам пользователи действительно кликают и какие запросы приводят к конверсиям — данные поступают в тюнер релевантности AACsearch и ваши собственные дашборды воронки.

Релевантность поиска настолько хороша, насколько хорош сигнал, который вы в него подаёте. AACsearch принимает события через POST /api/events/track для кликабельности (CTR), конверсий и запросов с нулевым результатом. Этот рецепт показывает, как подключить их, не замедляя клик.

Что вы отслеживаете

СобытиеСрабатывает, когдаЗачем
search_queryПоиск вернул результатыОбъём запросов, топ запросов
zero_resultsПоиск вернул found: 0Поиск запросов, требующих синонимов или контента
result_clickПользователь кликает по результатуCTR по запросу, по позиции
conversionПользователь достигает цели (добавление в корзину, покупка)Связь дохода с исходным запросом
filter_usedПользователь переключает фасетНаиболее используемые фильтры; приоритизация UI

Тюнер релевантности использует result_click и conversion для понижения результатов, по которым кликают, но не конвертируются, и для повышения результатов, которые конвертируются выше базового уровня CTR.

Отправка события

async function trackEvent(event: string, properties: Record<string, unknown>) {
  await fetch("/api/events/track", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({
      event,
      properties: {
        ...properties,
        timestamp: new Date().toISOString(),
        sessionId: getOrCreateSessionId(),
      },
    }),
    // keepalive позволяет запросу пережить навигацию по странице
    keepalive: true,
  });
}

Эндпоинт принимает запрос и немедленно возвращает 202 — события помещаются в очередь, а не обрабатываются синхронно.

Отслеживание кликов с queryId

Каждый ответ поиска содержит queryId, который вы должны прикреплять к последующим кликам, чтобы AACsearch мог связать клик с исходным поиском.

const result = await client.search({ q: "shoes", queryBy: "title" });
// result.queryId: "qry_abc123..."

// Отображение результатов
result.hits.forEach((hit, position) => {
  renderCard({
    product: hit.document,
    onClick: () => {
      trackEvent("result_click", {
        queryId: result.queryId,
        query: "shoes",
        documentId: hit.document.id,
        position, // 0-индексированная
        indexSlug: "products",
      });
    },
  });
});

Поле position критически важно — без него невозможно вычислить коррекцию смещения позиции клика.

Использование navigator.sendBeacon для SPA-навигации

Если клик вызывает переход на другую страницу (наиболее частый случай), стандартный fetch может быть отменён браузером до отправки. Три надёжных варианта:

function trackClickThenNavigate(url: string, payload: object) {
  if ("sendBeacon" in navigator) {
    navigator.sendBeacon("/api/events/track", JSON.stringify(payload));
  } else {
    // Запасной вариант: fetch с keepalive (отправил и забыл)
    fetch("/api/events/track", {
      method: "POST",
      body: JSON.stringify(payload),
      keepalive: true,
    });
  }
  window.location.href = url;
}

sendBeacon работает по принципу «отправил и забыл» и переживает выгрузку страницы. Используйте его для отслеживания кликов; используйте fetch с keepalive для событий, тело которых слишком велико для sendBeacon (ограничение ~64 КБ).

Отслеживание конверсий

Конверсия отправляется страницей, которая завершает цель пользователя — успешное оформление заказа, добавление в корзину, регистрация и т.д. Передавайте исходный queryId через навигацию:

// На странице результатов поиска
<Link href={`/products/${id}?qid=${result.queryId}`}>...</Link>;

// На странице товара
const queryId = useSearchParams().get("qid");

function onAddToCart() {
  trackEvent("conversion", {
    queryId,
    documentId: id,
    conversionType: "add_to_cart",
    value: product.price,
    currency: "USD",
  });
}

Для многошаговых воронок (поиск → товар → корзина → оформление), сохраняйте queryId в sessionStorage, чтобы он пережил все переходы.

Серверный рендеринг

Если ваша страница поиска рендерится на сервере, вы не можете отправить result_click с сервера (вы не знаете, по какому результату кликнет пользователь). Вместо этого:

  1. Отрисуйте data-qid={result.queryId} и data-pos={i} на каждой ссылке результата.
  2. Подключите обработчик клика в небольшом клиентском скрипте, который читает эти атрибуты и отправляет событие.
<a
  href={`/products/${id}`}
  data-qid={result.queryId}
  data-pos={i}
  className="result-link"
>
  {title}
</a>;

// Один глобальный обработчик, монтируемый один раз
document.addEventListener("click", (e) => {
  const target = (e.target as HTMLElement).closest("a.result-link");
  if (!target) return;
  const qid = target.getAttribute("data-qid");
  const pos = Number(target.getAttribute("data-pos"));
  const docId = target.getAttribute("href")?.split("/").pop();
  navigator.sendBeacon(
    "/api/events/track",
    JSON.stringify({
      event: "result_click",
      properties: { queryId: qid, position: pos, documentId: docId },
    }),
  );
});

Конфиденциальность

События принимают произвольные properties. Не помещайте туда персональные данные (email, телефон, полные имена) — они хранятся для анализа релевантности, а не для профилирования пользователей. В частности:

  • userId: "user_abc" (непрозрачный внутренний ID)
  • country: "DE"
  • email: "..."
  • address: "..."

Если вашей downstream-аналитике нужны персональные данные, храните их отдельно и соединяйте по userId.

Приём событий ограничен на уровне организации до 1000 событий/сек. Для очень высоких объёмов используйте пакетную отправку через эндпоинт events:batch (макс. 1000 событий за вызов).

Просмотр данных

Откройте Поиск → Аналитика → «Топ запросов» и «Топ запросов с нулевым результатом». Тюнер релевантности начинает учитывать сигналы примерно через 24 часа данных; для свежих дашбордов смотрите Поиск → Аналитика → «Последние события» (задержка 5 минут).

Св

язанные страницы

On this page