КурсХакатон Apache DorisЗадания в своём темпе · начните в любое время · без регистрации

Трек A · Задание A1

Приложение гибридного поиска

Ключевые слова, смысл и фильтры в одном SQL-запросе. Создайте поисковую строку по документации Apache Doris.

  • 60–120 мин
  • Для начинающих
  • Любой язык · CLI или веб
  • ИИ-инструменты приветствуются

Нужна помощь? Спросите в #dev в Slack Apache Doris. Разработчики и другие участники помогут вам.

1

Подготовка

Что вы создаёте, почему Doris упрощает задачу и как запустить кластер.

Что вы создадите

Создайте поиск, который понимает и слова, и смысл.

Точные совпадения ключевых слов с ранжированием BM25, семантические соседи из векторного индекса и структурированные фильтры: одна таблица Doris и один SQL-запрос. Без Elasticsearch и отдельной векторной базы.

Небольшое поисковое приложение (CLI, блокнот или веб-страница на ваш выбор) по 299 разделам документации Apache Doris. Пользователь вводит вопрос, например "how do I store messy JSON?", выбирает категорию и переключается между тремя режимами:

  • Ключевые словаПолнотекстовые совпадения с ранжированием BM25.
  • ВекторБлижайшие соседи по сходству эмбеддингов.
  • ГибридныйПредварительный фильтр по ключевым словам и векторное ранжирование (дополнительно: Reciprocal Rank Fusion).

Каждый результат показывает страницу, раздел, оценку (ключевые слова) или расстояние (векторный и гибридный режимы), а также ссылку на документацию. Переключатель «Показать SQL» раскрывает точный запрос Doris.

ЭскизЧто вы создадите · схема интерфейса, не снимок экрана

Эскиз находится в верхней части панели кода

Почему это интересно на Doris

  • Одна таблица, два индекса. Инвертированный индекс текста и индекс HNSW ANN векторов находятся в одной таблице. Планировщик сначала фильтрует по инвертированному индексу, затем ранжирует оставшиеся записи с помощью ANN.

  • Ранжирование поисковой системы в SQL. score() даёт BM25, ту же функцию релевантности, что использует Lucene, внутри обычного SELECT.

  • Фильтры: обычный SQL. Категория, страница, IN списки, диапазоны записываются рядом с MATCH_ANY в том же WHERE условии.

Панель: строки индексов в определении таблицы

Перед началом

  • docker pull apache/doris:all-in-one-4.1.3
  • docker run -d --name doris -p 9030:9030 -p 8030:8030 -p 8040:8040 apache/doris:all-in-one-4.1.3
  • docker exec -it doris mysql -uroot -h127.0.0.1 -P9030
  • curl -LO https://github.com/morningman/demo-env/releases/download/for-hackathon/doris-hackathon-glasgow-2026.zip && unzip doris-hackathon-glasgow-2026.zip && cd doris-hackathon-glasgow-2026
Хост
127.0.0.1
Порт
9030
Пользователь
rootбез пароля
База данных
hackathon

Данные

hackathon.doc_chunks : 47 страниц основных функций документации Doris 4.x, по одной строке на раздел:

Таблицаhackathon.doc_chunks · sql
CREATE TABLE doc_chunks (  id          INT           NOT NULL,  page        VARCHAR(64)   NOT NULL,   -- doc slug, e.g. 'hybrid-search'  page_title  VARCHAR(128)  NOT NULL,  section     VARCHAR(256)  NOT NULL,  category    VARCHAR(32)   NOT NULL,   -- see the 7 values below  url         VARCHAR(256)  NOT NULL,  body        STRING        NOT NULL,  embedding   ARRAY<FLOAT>  NOT NULL,   -- 8-dim toy vector, unit length  INDEX idx_body (body) USING INVERTED PROPERTIES ("parser" = "english", "support_phrase" = "true"),  INDEX idx_category (category) USING INVERTED,  INDEX idx_emb (embedding) USING ANN PROPERTIES (    "index_type" = "hnsw", "metric_type" = "l2_distance", "dim" = "8"))DUPLICATE KEY(id)DISTRIBUTED BY HASH(id) BUCKETS 1PROPERTIES ("replication_num" = "1");

category значения (для раскрывающегося списка фильтра): search, ai, lakehouse, ingestion, performance, table-design, operations.

О векторах. Для полностью автономной работы в стартовый набор включена небольшая, только для английского, учебная модель эмбеддингов, a1/toy_embed.py и a1/toy_embed.js (вывод совпадает до каждой цифры). Она отображает текст в 8 заданных вручную смысловых измерений: поиск, векторы и ИИ, JSON, lakehouse, загрузка, производительность, модель данных, эксплуатация с помощью списков синонимов и нормализует результат до единичной длины. Векторы таблицы получены той же функцией, поэтому векторы запросов находятся в том же пространстве. Подход намеренно прост, но уже показывает пользу векторного поиска: "store messy nested records whose schema keeps changing" находит страницы VARIANT, не упоминая JSON или VARIANT. В дополнительных заданиях вы замените её настоящей моделью.

Панель → Таблица: полное определение таблицы

Разработка с ИИ

Работайте вместе с ИИ-агентом для программирования, это приветствуется. Поисковые и ИИ-функции Doris 4.x появились позже обучающих данных большинства моделей. Поэтому перед написанием SQL дайте агенту правильный контекст:

  1. Вставьте контекстный промпт ниже в своего агента.

  2. Укажите ему справочные страницы из этого задания и AGENTS.md из стартового набора. Многие агенты читают этот файл самостоятельно.

  3. Перед встраиванием в код пусть агент выполнит каждый SQL-запрос на вашем работающем кластере.

  4. Ещё лучше подключите Doris MCP Server (см. задание A2), чтобы агент сам изучал схему и тестировал запросы.

Контекстный промптвставьте в своего агента
I'm building a hybrid search app on Apache Doris 4.1 for a hackathon.Connection: MySQL protocol, host 127.0.0.1, port 9030, user root, empty password,database `hackathon`. Use a plain MySQL driver and raw SQL (no ORM).Table `doc_chunks` (299 rows): id, page, page_title, section, url,category (search / ai / lakehouse / ingestion / performance / table-design / operations),body (STRING, inverted index, english parser, phrase support),embedding (ARRAY<FLOAT> NOT NULL, 8 dims, unit length, HNSW index, l2_distance).Doris SQL rules — follow them exactly:1. Keyword search uses `body MATCH_ANY 'a b'` (OR), `MATCH_ALL` (AND), `MATCH_PHRASE`. Never LIKE.   The parser keeps stopwords, so pass keywords(text) from toy_embed, not the raw question.   Bind the user's text as a driver parameter.2. BM25 relevance is `score()`. It only works with a MATCH_* predicate in WHERE and   `ORDER BY score() DESC LIMIT n`, selected plain (`score() AS relevance`): no ROUND(),   no window function, no JOIN in that SELECT. Round, rank or join it in an outer query.3. Vector search: `ORDER BY l2_distance_approximate(embedding, <vector>) ASC LIMIT n`.   Pass the vector as a string and cast it: CAST(%s AS ARRAY<FLOAT>) with '[f1, ..., f8]'.   Never pass a list/array as a driver parameter.4. Hybrid = MATCH_* pre-filter + vector ORDER BY; show the distance, do not select score()   in that query. For fusion, rank two lists in subqueries and combine with RRF (k = 60);   a1/rrf.sql in the starter kit shows the pattern.5. Query vectors come from toy_embed(text) in a1/toy_embed.py (toyEmbed in a1/toy_embed.js).   Do not invent embeddings.Build: a CLI (or small web page) with keyword / vector / hybrid modes, a category filter,and a "show SQL" option. Run every SQL statement against the live cluster first.

Типичные ловушки Doris для ИИ-агента

TrapWhat to do instead
Использование LIKE '%term%' для текстового поискаcol MATCH_ANY 'a b' (OR), MATCH_ALL (AND), MATCH_PHRASE 'a b' . Они используют инвертированный индекс
Вызов score() в произвольном запросеscore() требует MATCH_* в качестве предиката в WHERE и ORDER BY score() DESC LIMIT n; иначе Doris отклонит запрос
Округление, соединение или ранжирование score() в том же запросеОставьте score() AS relevance без изменений в запросе к одной таблице. Округляйте, соединяйте или применяйте ROW_NUMBER() во внешнем запросе, используя подзапрос вместо WITH CTE
Сортировка по BM25 и векторному расстоянию в одном ORDER BY, либо выбор score() в запросе с ранжированием по векторамНе поддерживается. Гибридный поиск: предварительный фильтр MATCH_* плюс векторный ORDER BY, с выводом расстояния; либо объединение двух ранжированных списков (RRF)
Передача вектора запроса как параметра драйвераСписок превращается в (…) в pymysql и в 8 отдельных аргументов в mysql2. Передайте строку '[0.1, …]' и напишите CAST(%s AS ARRAY<FLOAT>), либо вставьте литерал напрямую
Поиск косинусной метрики ANNМетрики ANN: l2_distance и inner_product. Нормализуйте векторы до единичной длины: возрастание расстояния L2 даёт тот же порядок, что и убывание косинусного сходства
2

Реализуйте

5 этапов, M0 → M4. Панель следует за текущим шагом.

  1. Этап 05 мин

    Загрузите данные

    Из папки стартового набора:

    M0 · Загрузка данныхbash
    docker exec -i doris mysql -uroot -h127.0.0.1 -P9030 < seed/a1_doc_chunks.sql
    Проверка

    Загрузка завершается выводом количества по категориям: 7 категорий, 299 разделов всего.

  2. Этап 115 мин

    Поиск по ключевым словам с BM25

    Откройте оболочку SQL (./doris.sh sql, либо командой docker exec выше) и выполните:

    M1 · Поиск по ключевым словам с BM25sql
    USE hackathon;SELECT page_title, section, score() AS relevanceFROM doc_chunksWHERE body MATCH_ANY 'vector index recall'ORDER BY relevance DESCLIMIT 10;

    Затем попробуйте MATCH_ALL (должны присутствовать все слова) и MATCH_PHRASE 'inverted index' (рядом и по порядку). Соедините поле ввода → SQL → список результатов. Передавайте текст пользователя как параметр драйвера (MATCH_ANY %s), а не вставляйте в SQL: первая же одинарная кавычка нарушит запрос. a1/starter.py и a1/starter.js делают именно это.

    Ввели целый вопрос? Парсер english сохраняет стоп-слова, поэтому 'how do I …' совпадает с третью таблицы. Передайте вместо этого keywords(text) из учебной модели: она удаляет стоп-слова, а также apache и doris, встречающиеся почти в каждом разделе.

    Проверка

    Лучшие результаты для vector index recall относятся к страницам Vector Index и Hybrid Search; первый: How does the Apache Doris vector index work?

  3. Этап 210 мин

    Добавьте фильтры

    M2 · Добавление фильтровsql
    SELECT page_title, section, category, score() AS relevanceFROM doc_chunksWHERE body MATCH_ANY 'vector index recall'  AND category = 'search'ORDER BY relevance DESCLIMIT 10;

    Добавьте выбор категории через раскрывающийся список или флаг CLI (7 значений указаны в разделе «Данные»). Дополнительные условия: обычный SQL: category IN (…), page = ….

    Проверка

    Разделы Vector Index исчезают, эта страница относится к категории ai , а Hybrid Search выходит на первое место.

  4. Этап 315 мин

    Векторный поиск

    Получите эмбеддинг вопроса учебной моделью из кода или командной строки:

    M3 · Получение эмбеддинга вопросаpython
    from toy_embed import toy_embed, to_sql_arrayvec = to_sql_array(toy_embed("store messy nested records whose schema keeps changing"))# '[0.005740, 0.005740, 0.999885, ...]'  ·  CLI: python a1/toy_embed.py "..."
    M3 · Векторный поискsql
    SELECT page_title, section,       l2_distance_approximate(embedding,         [0.005740, 0.005740, 0.999885, 0.005740, 0.005740, 0.005740, 0.005740, 0.005740]) AS distFROM doc_chunksORDER BY dist ASCLIMIT 10;

    В коде оставьте SQL неизменным и передайте вектор строкой как параметр: l2_distance_approximate(embedding, CAST(%s AS ARRAY<FLOAT>)). Список Python или массив JavaScript в качестве параметра не подойдут.

    Проверка

    Первые шесть результатов относятся к VARIANT, хотя вопрос не содержит ни JSON, ни VARIANT. Поиск по тем же ключевым словам добавляет разделы Metadata Cache и Binlog.

  5. Этап 420 мин

    Гибридный

    Вопрос: "stream kafka changes into doris exactly once". Вектор: учебный эмбеддинг вопроса; фильтр оставляет только разделы, содержащие kafka:

    M4 · Гибридный поискsql
    SELECT page_title, section,       l2_distance_approximate(embedding, [0.007020, 0.007020, 0.007020, 0.007020, 0.999827, 0.007020, 0.007020, 0.007020]) AS distFROM doc_chunksWHERE body MATCH_ANY 'kafka'                     -- keyword pre-filter (inverted index)  AND category IN ('ingestion', 'lakehouse')     -- structured filterORDER BY dist ASC                                -- vector ranking (ANN)LIMIT 10;
    • L4WHERE body MATCH_ANY 'kafka'предварительный фильтр по ключевым словам (инвертированный индекс)
    • L5AND category IN ('ingestion', 'lakehouse')структурированный фильтр
    • L6ORDER BY dist ASCвекторное ранжирование (ANN)

    В приложении один ввод используется для обеих частей: keywords(text) для MATCH_ANY, toy_embed(text) для вектора. MATCH_ANY для нескольких слов даёт мягкий фильтр; для строгого условия предложите поле обязательных слов или MATCH_ALL . Не выбирайте score() здесь: показывайте расстояние.

    Добавьте переключатель режима (ключевые слова / векторы / гибридный) и Показать SQL . Сравните три списка для одного вопроса: где и почему они расходятся?

    Проверка

    Каждый результат упоминает Kafka, первым идёт How does the Apache Doris Kafka and CDC integration work? Только векторный режим ставит Pipeline Execution Engine › Overview на первое место для того же вопроса.

3

Завершите работу

Пройдите проверку, покажите результат и получите значок.

Критерии готовности

Ваш контрольный список

Отправьте работу и получите значок

Участвуйте → отправьте работу → получите значок.

  1. Поместите свою работу в папку с именем вашего GitHub ID: код и README с описанием, инструкцией запуска, одним снимком экрана или GIF и перечнем использованных функций Doris.

  2. Откройте pull request и добавьте её в morningman/demo-env как doris-hackathon-glasgow-2026/<your-github-id>/. Сделайте fork и push или используйте в GitHub Add file → Upload files. Больше ничего заполнять не нужно.

  3. Поделитесь результатом в #dev в Slack Apache Doris: ссылкой на pull request или репозиторий и снимком экрана либо коротким демо. Вопросы, отправка работ и выдача значков происходят там.

Каждый участник задания получает значок участника Apache Doris: принятый pull request в Doris не требуется. Срока нет, работайте столько, сколько нужно. Можно участвовать командой, но это не обязательно. Укажите GitHub ID каждого участника в README.

Дополнительные цели

  • Reciprocal Rank Fusion. Объедините списки BM25 и векторного поиска с помощью 1/(60 + rank) в одном запросе: a1/rrf.sql из стартового набора ранжирует каждый список с помощью ROW_NUMBER() на один уровень выше обычного score() запроса и объединяет их через FULL OUTER JOIN. Страница RRF разбирает тот же шаблон на примере из четырёх строк.

  • Настоящие эмбеддинги с EMBED() (с вашим API-ключом): создайте ресурс ИИ и doc_chunks_v2 с параметром модели dim, заполните через INSERT INTO doc_chunks_v2 SELECT …, EMBED(body) FROM doc_chunks (векторный столбец имеет тип NOT NULL, поэтому вставляйте, а не обновляйте) и получите эмбеддинг запроса тоже в SQL. См. Эмбеддинги.

  • Поиск по мере ввода вместе с MATCH_PHRASE_PREFIX.

  • Подсветка совпавших слов в результатах (Doris возвращает оценки, а не подсветку; отрисуйте её в приложении).

  • Индикатор задержки: покажите время каждого режима в миллисекундах.

Устранение неполадок и справочные материалы

SymptomFix
docker: command not found в macOSЗапустите Docker Desktop. Если он уже работает, создайте ссылку на CLI: sudo ln -s /Applications/Docker.app/Contents/Resources/bin/docker /usr/local/bin/docker
error getting credentials при загрузке образовУдалите поле credsStore из ~/.docker/config.json (только для локальной разработки)
port is already allocatedПорты 9030, 8030 или 8040 заняты, часто другим Doris. Проверьте работающие контейнеры (docker ps), либо опубликуйте другие порты хоста, например -p 19030:9030, и подключайтесь к 19030
The container name "/doris" is already in useВы уже запускали его раньше: docker start doris. Чтобы начать заново, docker rm -f doris (данные будут удалены)
Контейнер завершается или не переходит в состояние (healthy)Прочитайте docker logs doris. Обычно причина в памяти: выделите Docker Desktop не менее 6 GB (Settings → Resources). На Apple Silicon не добавляйте --platform linux/amd64
permission denied: ./doris.shПри распаковке потерялось право на выполнение: chmod +x doris.sh, либо выполните bash doris.sh start
Current database is not setВыполните USE hackathon; сначала или подключитесь с помощью -Dhackathon
score() function requires WHERE clause with MATCH function, ORDER BY and LIMITДобавьте MATCH_* предикат, ORDER BY score() DESC и LIMIT, и оставьте score() без обёртки (см. ловушки выше)
Ann topn query vector cannot be emptyВекторный литерал пуст или остался шаблоном: вставьте вывод python a1/toy_embed.py "…"
Can not found function 'l2_distance_approximate' which has 9 arityДрайвер развернул параметр-массив в 8 значений: передайте вектор строкой через CAST(? AS ARRAY<FLOAT>)
Если не получается больше 10 минутНапишите в #dev в Slack Apache Doris

Справочные материалы

Гибридный поискBM25Полнотекстовый поискВекторный индексReciprocal Rank FusionЭмбеддинги

Хакатон Apache Doris

Все задания

Вернуться к курсу
  1. A160–120 минПриложение гибридного поискаВы здесь
  2. A245–90 минЗадавайте вопросы Doris через MCPОткрыть описание задания
  3. A360–120 минОбозреватель поиска по логамОткрыть описание задания
  4. A460–120 минОбозреватель трассировок агентовОткрыть описание задания
  5. B30–60 минТрек B · От документации к демоОткрыть описание задания