Трек A · Задание A1
Приложение гибридного поиска
Ключевые слова, смысл и фильтры в одном SQL-запросе. Создайте поисковую строку по документации Apache Doris.
- 60–120 мин
- Для начинающих
- Любой язык · CLI или веб
- ИИ-инструменты приветствуются
Нужна помощь? Спросите в #dev в Slack Apache Doris. Разработчики и другие участники помогут вам.
Подготовка
Что вы создаёте, почему 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.3docker run -d --name doris -p 9030:9030 -p 8030:8030 -p 8040:8040 apache/doris:all-in-one-4.1.3docker exec -it doris mysql -uroot -h127.0.0.1 -P9030curl -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, по одной строке на раздел:
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 дайте агенту правильный контекст:
Вставьте контекстный промпт ниже в своего агента.
Укажите ему справочные страницы из этого задания и
AGENTS.mdиз стартового набора. Многие агенты читают этот файл самостоятельно.Перед встраиванием в код пусть агент выполнит каждый SQL-запрос на вашем работающем кластере.
Ещё лучше подключите 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 для ИИ-агента
| Trap | What 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 даёт тот же порядок, что и убывание косинусного сходства |
Реализуйте
5 этапов, M0 → M4. Панель следует за текущим шагом.
Загрузите данные
Из папки стартового набора:
M0 · Загрузка данныхbashdocker exec -i doris mysql -uroot -h127.0.0.1 -P9030 < seed/a1_doc_chunks.sqlПроверкаЗагрузка завершается выводом количества по категориям: 7 категорий, 299 разделов всего.
Поиск по ключевым словам с BM25
Откройте оболочку SQL (
./doris.sh sql, либо командойdocker execвыше) и выполните:M1 · Поиск по ключевым словам с BM25sqlUSE 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?Добавьте фильтры
M2 · Добавление фильтровsqlSELECT 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 выходит на первое место.Векторный поиск
Получите эмбеддинг вопроса учебной моделью из кода или командной строки:
M3 · Получение эмбеддинга вопросаpythonfrom 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 · Векторный поискsqlSELECT 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.
Гибридный
Вопрос: "stream kafka changes into doris exactly once". Вектор: учебный эмбеддинг вопроса; фильтр оставляет только разделы, содержащие kafka:
M4 · Гибридный поискsqlSELECT 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;- L4
WHERE body MATCH_ANY 'kafka'предварительный фильтр по ключевым словам (инвертированный индекс) - L5
AND category IN ('ingestion', 'lakehouse')структурированный фильтр - L6
ORDER 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 на первое место для того же вопроса.
- L4
Завершите работу
Пройдите проверку, покажите результат и получите значок.
Критерии готовности
Ваш контрольный списокОтправьте работу и получите значок
Участвуйте → отправьте работу → получите значок.
Поместите свою работу в папку с именем вашего GitHub ID: код и README с описанием, инструкцией запуска, одним снимком экрана или GIF и перечнем использованных функций Doris.
Откройте pull request и добавьте её в morningman/demo-env как
doris-hackathon-glasgow-2026/<your-github-id>/. Сделайте fork и push или используйте в GitHub Add file → Upload files. Больше ничего заполнять не нужно.Поделитесь результатом в
#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 возвращает оценки, а не подсветку; отрисуйте её в приложении).
Индикатор задержки: покажите время каждого режима в миллисекундах.
Устранение неполадок и справочные материалы
| Symptom | Fix |
|---|---|
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
Все задания
- A160–120 минПриложение гибридного поискаВы здесь
- A245–90 минЗадавайте вопросы Doris через MCPОткрыть описание задания
- A360–120 минОбозреватель поиска по логамОткрыть описание задания
- A460–120 минОбозреватель трассировок агентовОткрыть описание задания
- B30–60 минТрек B · От документации к демоОткрыть описание задания
