ДАРА
Разделы документации

ДокументацияРуководства

Поиск

POST /search — поиск по смыслу и словам с фрагментами и временем в видео: поля запроса, ответ, страницы, метрики, ошибки и примеры кода.

Поиск ищет по смыслу, а не по совпадению букв: запрос превращается в вектор и сравнивается с содержанием объектов — речью, текстом, описанием и сводкой. У видео и аудио ответ содержит фрагменты со временем, поэтому плеер можно открыть сразу на нужной секунде.

Метод Для чего Ответ
POST /search поиск по каталогу проекта recommendation_id, items: [{id, score, matches}], has_more, identity

Запрос идёт с вашего сервера с ключом проекта: Authorization: Bearer ck_XXXXXXXX… — в примерах он в переменной окружения DARA_KEY. Лимит частоты — общий с выдачей: 100 запросов в секунду на ключ. Все поля и схемы — в справочнике API.

Запрос: POST /search

curl -X POST https://core.daratech.ru/api/v1/search \
  -H "Authorization: Bearer $DARA_KEY" \
  -H "Content-Type: application/json" \
  -d '{"query": "как приготовить борщ", "user": {"user_id": "u-501", "anonymous_id": "a-7f3c2e"}, "limit": 20}'
Поле Тип Значение
query string текст запроса, до 500 знаков. Длина считается после схлопывания пробелов и обрезки краёв
user object пользователь, как в событиях: хотя бы один идентификатор. Нужен, чтобы не показывать скрытое пользователем и учитывать его интересы
limit int объектов на странице, 1–50; по умолчанию 20
offset int смещение, 0–500; по умолчанию 0
types array только объекты этих типов: 1–5 разных значений из video, audio, image, text, mixed
filters object условия по фильтруемым свойствам проекта — те же, что в ленте, см. Рекомендации → Фильтры
recommendation_id string id результатов первой страницы (ULID): передавайте его на следующих страницах

Другие поля не принимаются: лишнее поле — 422 с кодом unknown_field.

Ошибки полей

Все ошибки приходят одним ответом 422 validation_failed, до 20 строк details:

Код Когда
required query не передан или null
invalid_type query не строка, limit или offset не целое число, types не массив
invalid_value пустой запрос или управляющие символы в нём, limit или offset вне диапазона, повтор в types, recommendation_id не ULID
too_long запрос длиннее 500 знаков
unknown_field в теле поле, которого нет в таблице выше
invalid_user user — не объект, в нём неизвестное поле или идентификатор неверного формата
unknown_filter свойство не отмечено в проекте фильтруемым или условие не подходит к его типу

Ответ

{"recommendation_id": "01J8Z3K4M5N6P7Q8R9S0T1V2W6",
 "items": [
   {"id": "v-1042", "score": 1.2841,
    "matches": [{"media_position": 0, "start_sec": 585.6, "end_sec": 675.2, "text": "…и теперь добавляем свёклу…"}]},
   {"id": "a-7", "score": 0.9312, "matches": []}
 ],
 "has_more": true, "identity": "user"}
Поле Значение
recommendation_id id результатов, ULID из 26 символов: передавайте его в событиях показа, открытия и цели
items объекты по убыванию оценки; страница бывает короче limit, если объекты успели удалить или скрыть
score оценка соответствия запросу, 4 знака после запятой. Сравнивайте только внутри одного ответа: между запросами шкала не сопоставима
matches до 3 фрагментов объекта, по убыванию близости; ключ есть всегда, пустой список — у объекта нет фрагментов или они сейчас недоступны
has_more есть ли следующая страница
identity как определён пользователь — те же значения, что в выдаче: user, recognized, anonymous, none

Поля фрагмента:

Поле Значение
media_position позиция медиа в объекте, с нуля; ключа нет — фрагмент из текста, описания или сводки
start_sec начало фрагмента в медиа, секунды: с этого места открывайте плеер
end_sec конец фрагмента в медиа, секунды
text текст фрагмента, не длиннее 2000 знаков

В ответе только доступные и неудалённые объекты проекта. Поля mode у поиска нет: доля запросов, на которые ДАРА ответила по словам, видна в кабинете, в «Метриках».

Фрагменты и время в медиа

Время фрагмента — оценка по транскрипту: речь разбита на окна примерно по полторы минуты с перекрытием, и границы окна попадают в start_sec и end_sec. Для плеера этого достаточно: открывайте видео немного раньше начала фрагмента.

{"id": "v-1042", "score": 1.31,
 "matches": [
   {"media_position": 0, "start_sec": 585.6, "end_sec": 675.2, "text": "…свёклу лучше тушить отдельно…"},
   {"media_position": 0, "start_sec": 1209.6, "end_sec": 1288.0, "text": "…заправка из томатной пасты…"}
 ]}

Ссылка на такой результат у вас на сайте выглядит как /video/v-1042?t=585. У текстовых объектов и у фрагментов из сводки времени нет — есть только text.

Что ищется и когда объект находится

ДАРА ищет по тому, что построил анализ:

  • речь медиа — окна транскрипта со временем;
  • текст объекта — куски текста и описания;
  • сводка — отдельный кусок;
  • заголовок, теги и сводка — ещё и по словам.

Что именно проиндексировано у объекта, видно в GET /items/{id}?include=search_chunks — см. Объекты.

Когда объект появляется в поиске. Сразу после приёма объекта считается быстрый вектор по заголовку и описанию — с этого момента объект находится по смыслу заголовка и по словам. Фрагменты появляются, когда шаг векторов завершён: processing.embedding в GET /items/{id} равен done. Смена модели векторов проекта запускает переиндексацию; пока она идёт, поиск работает на прежних векторах, см. Модели.

Куда уходит запрос. Если у проекта выбрана облачная модель векторов, текст запроса уходит поставщику модели — как и тексты объектов при анализе (см. Модели). У проекта с локальной моделью запрос сервер не покидает.

Порядок результатов

Порядок решают, в порядке влияния:

  • близость содержания объекта и его фрагментов к смыслу запроса;
  • интересы пользователя, если он известен и не отказался от персонализации;
  • совпадение всех слов запроса в заголовке, тегах или сводке.

Слова ищутся с учётом окончаний: «рецепты борща» находит «рецепт борща» и «борщ». Служебные слова («как», «для», «что») не обязательны. Порога релевантности нет: если по запросу нашлось мало, ответ будет коротким, а не пустым от строгого порога. Досмотренное и уже купленное из поиска не убирается — пользователь ищет и то, что уже видел.

Фильтры и что не находится

  • filters работают так же, как в ленте: Рекомендации → Фильтры. Свойство должно быть отмечено фильтруемым в настройках проекта.
  • Скрытые пользователем объекты и объекты скрытых авторов не показываются.
  • При включённом фильтре по возрасту объект с отметкой N+ не показывается пользователю младше N лет.
  • Недоступные (available: false) и удалённые объекты не находятся.
  • types ограничивает типы объектов.

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

Страницы

Ответ первой страницы сохраняется снимком, как в выдаче: следующие страницы берутся из него — без повторов и пропусков, даже если объекты за это время изменились. Снимок живёт 30 минут.

  • Передавайте recommendation_id первой страницы на следующих страницах. Без него страница находится по идентификаторам пользователя и параметрам запроса — если они те же, продолжается тот же список.
  • offset — до 500: глубже поиск не листается. has_more: false — страниц больше нет.
  • Объекты, которые удалили или скрыли после первой страницы, из следующих страниц выбрасываются.
  • Повторный анонимный запрос с тем же текстом и параметрами в том же проекте в пределах минуты может вернуть тот же recommendation_id и тот же список: одинаковые запросы обходчиков и автодополнения не пересчитываются. Объект, добавленный только что, попадает в такой ответ не позже чем через минуту.

Метрики поиска

Показы и открытия результатов поиска передавайте событиями с recommendation_id ответа, surface: "search" и position с 0 — как в выдаче, см. Рекомендации → Метрики выдачи. В кабинете поиск виден отдельной поверхностью: запросы, показы, открытия и доля ответов по словам.

Тайм-аут и ошибки

Поиск рассчитан на ответ примерно за полсекунды, но облачный вектор запроса добавляет к этому время поставщика. Ставьте на стороне сайта таймаут около полутора секунд (соединение — около 200 мс) и держите запасной вариант — например, свой поиск по названиям.

Когда вектор запроса получить не удалось (поставщик недоступен, превышен предел частоты, локальная модель ещё загружается) или индекс проекта ещё готовится, ДАРА отвечает 200 — списком по словам запроса, с пустыми matches у всех объектов. Ошибкой клиенту это не оборачивается: часть результатов лучше, чем пустая страница. Доля таких ответов видна в кабинете, в «Метриках».

Ответ Когда Что делать
422 validation_failed поля запроса не прошли проверку исправить запрос, см. таблицу выше
429 rate_limited превышен общий с выдачей лимит 100 запросов в секунду на ключ повторить после Retry-After
503 unavailable, Retry-After: 1 поиск перегружен: одновременных поисков больше, чем сервер готов считать повторить через секунду или показать свой запасной поиск
503 unavailable, Retry-After: 2 база данных недоступна или ответ не уложился во время показать свой запасной поиск

Полный список кодов — Ошибки и лимиты.

Автодополнение. Запрос на каждую нажатую букву даёт поток уникальных текстов: вектор для каждого из них считать дорого, и часть таких запросов получит ответ по словам. Запрашивайте поиск после паузы ввода — примерно через треть секунды после последнего нажатия — и не чаще.

Примеры на curl, PHP и Python

<?php
/** Результаты поиска ДАРА или null: при null покажите свой поиск. */
function dara_search(string $query, array $user, int $limit = 20, int $offset = 0): ?array
{
    $ch = curl_init('https://core.daratech.ru/api/v1/search');
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . getenv('DARA_KEY'), 'Content-Type: application/json'],
        CURLOPT_POSTFIELDS => json_encode(
            ['query' => $query, 'user' => $user, 'limit' => $limit, 'offset' => $offset],
            JSON_UNESCAPED_UNICODE
        ),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_NOSIGNAL => true,
        CURLOPT_CONNECTTIMEOUT_MS => 200,
        CURLOPT_TIMEOUT_MS => 1500,
    ]);
    $response = curl_exec($ch);
    $status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    if ($response === false || $status !== 200) {
        return null;
    }
    $data = json_decode($response, true);
    return is_array($data) ? $data : null;
}

$found = dara_search('рецепт борща', ['user_id' => 'u-501', 'anonymous_id' => 'a-7f3c2e']);
foreach ($found['items'] ?? [] as $item) {
    $start = $item['matches'][0]['start_sec'] ?? null;
    echo $item['id'], $start === null ? '' : ' — с ' . (int) $start . ' с', "\n";
}
import httpx

api = httpx.Client(
    base_url="https://core.daratech.ru/api/v1",
    headers={"Authorization": f"Bearer {DARA_KEY}"},
    timeout=httpx.Timeout(1.5, connect=0.2),
)


def search(query, user, limit=20, offset=0):
    """Результаты поиска или None: при None покажите свой поиск."""
    try:
        response = api.post("/search", json={"query": query, "user": user, "limit": limit, "offset": offset})
        return response.json() if response.status_code == 200 else None
    except (httpx.HTTPError, ValueError):  # сеть, таймаут, неразборчивый ответ
        return None


user = {"user_id": "u-501", "anonymous_id": "a-7f3c2e"}
page1 = search("рецепт борща", user)
page2 = (
    search("рецепт борща", user, offset=20)
    if page1 and page1["has_more"]
    else None
)

Свой поиск и переранжирование

Если у вас уже есть поиск — по названиям, артикулам или в поисковом движке, — его результаты можно упорядочить под пользователя: POST /recommendations/rerank принимает ваш список id и возвращает его в порядке интересов пользователя. См. Рекомендации → Переранжирование.