ДокументацияРуководства
Поиск
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 и возвращает его в порядке интересов пользователя. См. Рекомендации → Переранжирование.