Оформление
Метод rankProducts
rankProducts упорядочивает товары, которые сайт уже выбрал сам, внутри каждой группы. На сервер уходят идентификаторы товаров и поведение посетителя — что он смотрел и что лежит в корзине, — по которому модель ранжирования ставит порядок; ваши значения — объекты, элементы страницы, строки — возвращаются те же, в новом порядке. Что такое ранжирование и как его создать, описано на странице Ранжирование товаров.
Вызов
js
const result = await ninja('rankProducts', {
rankingId: 'RANKING_ID',
items: products,
getId: (product) => product.offerId,
timeout: 300,
})Метод можно вызывать сразу после ninja('init', …): он не ждёт полной загрузки страницы и остальных модулей скрипта.
| Параметр | Обязательный | Что передать |
|---|---|---|
rankingId | Да | Идентификатор ранжирования из редактора в кабинете |
items | Да | Массив товаров или массив групп товаров (массив массивов) |
getId | Нет | Функция, которая возвращает id товара: id предложения из фида или артикул |
timeout | Нет | Сколько миллисекунд ждать ответа с момента вызова. По умолчанию 5000. Не успели — исходный список без изменений |
Без getId идентификатор берётся так:
- строка или число — это и есть идентификатор;
- элемент страницы — значение атрибута
data-product-id; - объект — поле
id.
Результат
js
{
status: 'ranked',
items: [
{ item: <ваше значение>, id: 'sku-102', score: 0.84, index: 1 },
{ item: <ваше значение>, id: 'sku-101', score: 0.31, index: 0 },
{ item: <ваше значение>, id: 'new-item', score: null, index: 2 }
]
}items повторяет форму запроса: на массив приходит массив, на массив групп — массив групп в том же порядке.
| Поле | Смысл |
|---|---|
item | Значение, которое вы передали, без изменений |
id | Идентификатор товара, который ушёл на сервер, или null, если его не удалось получить |
score | Балл ранжирования: чем выше, тем раньше (откуда берётся порядок). Сравним только внутри одной группы одного ответа. null — у товара нет балла |
index | Позиция значения в исходной группе |
Внутри группы сначала идут товары с баллом по убыванию балла, затем товары без балла в исходном порядке.
Показы, клики и A/B-тест
В обновлении статистики результат содержит hitId, а при участии в тесте — experimentId и assignedVariant (original или personalized). Это назначение посетителя; scoreSource сообщает, какой порядок фактически был получен. При таймауте отображается исходный список, и поздний ответ не меняет его. Не сохраняйте готовый порядок для других посетителей или изменившегося набора товаров.
Вызов API сам по себе не считается показом. После фактического отображения списка вызовите result.trackImpression(groupIndex). Повторный вызов для того же результата сообщает тот же показ. Клик передавайте по index из результата, а не по id товара: один товар может встречаться дважды.
js
const result = await ninja('rankProducts', { rankingId: 'RANKING_ID', items: cards })
container.append(...result.items.map(({ item }) => item))
result.trackImpression()
for (const { item, index } of result.items) {
item.addEventListener('click', () => result.trackClick(index))
}Для нескольких групп передайте индекс группы в оба метода. Клики отправляются без ожидания, чтобы переход по ссылке не задерживался. Повторный реальный клик учитывается отдельно. После замены списка используйте методы нового результата и снимите обработчики прежнего списка. Показы и клики можно подтверждать также для исходного списка, возвращённого при ошибке или таймауте.
Тест запускается и останавливается в редакторе ранжирования. Вариант посетителя сохраняется на весь запуск; исходный вариант оставляет переданный порядок без пересчёта. Сравнение использует цель, сохранённую на начало запуска. История остаётся после остановки. Статистика не обещает доставку каждого события: запросы без подтверждённого показа и несопоставленные события видны отдельно.
Статусы
status | Что значит |
|---|---|
ranked | Порядок поставлен |
rankingNotFound | Ранжирование с таким идентификатором не найдено в проекте |
rankingStopped | Ранжирование остановлено в кабинете |
catalogUnavailable | Каталог проекта временно недоступен |
experimentUnavailable | Назначение варианта теста временно недоступно; возвращается исходный порядок |
timeout | Ответ не пришёл за timeout миллисекунд с момента вызова: исходный список без изменений, поздний ответ не применяется |
invalidRequest | Запрос отклонён: товаров во всех группах больше 2000 или идентификатор ранжирования не в формате из редактора |
error | Сетевая или серверная ошибка |
При любом статусе, кроме ranked, промис не отклоняется: он возвращает товары в исходном порядке без баллов, и страница продолжает работать. Отклоняется он только при неверных аргументах — без rankingId; items не массив (например, NodeList без [...]); в items смешаны группы и отдельные товары; timeout не число или меньше 1 — и при вызове до init.
Если getId выбросит ошибку на каком-то товаре, этот товар просто останется без балла: остальные будут упорядочены.
Если ответ не успел
timeout — это обещание странице, а не только сети: отсчёт идёт с момента, когда скрипт Sales Ninja выполнил вызов rankProducts. Если за это время ответа нет, промис сразу возвращает исходный список без изменений — те же значения в том же порядке, без баллов — со статусом timeout. Ответ, пришедший позже, не применяется.
Если вызов сделан до загрузки скрипта, он ждёт в очереди, а если скрипт заблокирован (блокировщик рекламы, недоступная сеть), не выполнится вовсе. Поэтому там, где страница ждёт порядок перед отрисовкой, поставьте свою страховку — так страница никогда не ждёт дольше своего бюджета:
js
const ranked = await Promise.race([
ninja('rankProducts', { rankingId: 'RANKING_ID', items: products, timeout: 300 }),
new Promise((resolve) => setTimeout(() => resolve(null), 300)),
])
renderCatalog(ranked ? ranked.items.map(({ item }) => item) : products)Скорость
- Запрос простой: браузер не делает предварительный CORS-запрос, поэтому до сервера идёт один круг.
- В обычном режиме ответ собирается из памяти сервера: модель, каталог и поведение посетителя из запроса уже под рукой, оценка нескольких сотен товаров занимает единицы миллисекунд, остальное время — сеть до посетителя.
- Чтобы не задерживать первую отрисовку, задайте
timeoutпод бюджет страницы и рисуйте исходный порядок, если ответ опоздал.