Урок курса
Архитектура Weaviate и GraphQL API
Vector DB & RAG DeveloperWeaviate: архитектура и место в экосистеме векторных хранилищ
К этому моменту мы поработали с двумя инструментами, которые принципиально отличаются по подходу. FAISS — это библиотека, живущая в памяти вашего процесса: никакого сервера, никакой персистентности, всю обвязку вы строите сами. Qdrant — уже полноценная база данных с отдельным сервисом, REST/gRPC API и фильтрацией по произвольному payload. Weaviate делает ещё один шаг вперёд: он не просто хранит векторы, а понимает структуру данных, умеет векторизовать их самостоятельно и строить явные связи между объектами — как в графе знаний.
Объектная модель и схема
Главное архитектурное отличие Weaviate — объектная модель данных. В Qdrant у вас есть точки с вектором и произвольным payload без какого-либо контракта. В Weaviate данные организованы в классы — аналог таблиц в реляционных базах. Каждый класс описывается схемой: какие у него свойства, какого они типа, как индексируются. Например, класс Article может иметь свойства title типа text, publishedAt типа date и rating типа number.
Схему нужно описать до загрузки данных. Это добавляет немного работы в начале, но даёт важное преимущество: Weaviate точно знает типы данных и строит нужные индексы. В частности, он поддерживает два типа индексов одновременно — HNSW для приближённого векторного поиска и инвертированный индекс для точных фильтров и полнотекстового поиска по алгоритму BM25. Именно наличие обоих индексов делает возможным гибридный поиск без внешней надстройки.
Модульные векторизаторы
Одна из наиболее удобных особенностей Weaviate — встроенная векторизация. Вместо того чтобы вычислять эмбеддинги отдельно и загружать их в базу, можно подключить модуль-векторизатор на уровне класса. При добавлении объекта Weaviate сам вызовет модуль и сохранит вектор.
Доступны модули под разные задачи: text2vec-openai (OpenAI Embeddings API), text2vec-cohere (Cohere), text2vec-transformers (self-hosted трансформер, например all-MiniLM-L6-v2), multi2vec-clip (мультимодальная CLIP-модель для текста и изображений одновременно), img2vec-neural (только изображения). Если хочется подавать готовые векторы самостоятельно — просто не указываем векторизатор, и Weaviate будет ждать явный вектор при каждом добавлении объекта.
Встроенная векторизация упрощает архитектуру, но имеет цену: база делает сетевые вызовы к API или держит ML-модель в памяти. В продакшене с большими объёмами данных часто предпочитают вычислять эмбеддинги заранее батчами — это даёт больше контроля над скоростью и стоимостью. Для прототипов и учебных проектов встроенные модули — отличный выбор.
Named vectors — один объект, несколько векторных представлений
У одного объекта в Weaviate может быть несколько независимых векторов. Эта возможность называется named vectors: каждому вектору даётся имя, и при поиске вы явно указываете, по какому из них работать.
Практический смысл: заголовок статьи и её полный текст несут разную информацию. Если векторизовать их раздельно — одной или разными моделями — поиск по заголовкам и поиск по содержанию дадут разные результаты. Можно хранить оба вектора в одном объекте и в запросе выбирать нужный. Это особенно полезно в мультимодальных сценариях, где, например, одна часть объекта — текст, другая — изображение.
Граф знаний и кросс-референсы
Weaviate изначально разрабатывался как база знаний с графовой структурой. Объекты разных классов можно связывать через кросс-референсы — ссылки, аналогичные внешним ключам в реляционных базах. Если у нас есть классы Article и Author, то в схеме класса Article можно завести свойство hasAuthor с типом-ссылкой на класс Author.
Это открывает два важных сценария. Во-первых, в GraphQL-запросе можно в одном обращении получить статьи вместе с данными их авторов — без второго запроса к базе. Во-вторых, в фильтрах можно писать условия через ссылку: «статьи, у автора которых более 10 лет опыта» — и Weaviate пройдёт по связи сам. Это то, что отличает Weaviate от просто векторного хранилища: поиск здесь не только по векторам, но и по явным отношениям между объектами.
Мультимодальный поиск
Через модуль img2vec-neural Weaviate умеет хранить изображения и искать похожие по визуальному содержанию. Через модуль multi2vec-clip работает кросс-модальный поиск: модель CLIP обучена в едином векторном пространстве для текста и изображений, поэтому можно искать картинки по текстовому описанию и наоборот.
Механика одинакова для всех модальностей: при добавлении объекта с изображением Weaviate вычисляет вектор через подключённый модуль. В запросе передаётся изображение (в Base64) или текст — база ищет ближайшие по вектору. Подробнее разберём это в практическом уроке.
API и способы взаимодействия
Weaviate предоставляет три способа работы. GraphQL API — основной язык для чтения данных: позволяет формулировать сложные поисковые запросы с фильтрами, агрегациями и кросс-референсами в одном обращении на эндпоинт /v1/graphql. REST API — используется для управления схемой, батч-загрузки объектов и административных операций. Python SDK v4 — современная обёртка поверх gRPC и REST, которую мы будем активно использовать во втором уроке.
В современной разработке GraphQL остаётся полезным для интерактивного исследования данных и понимания модели запросов Weaviate, а Python SDK — основным инструментом в продакшен-коде. В этом уроке мы разберём GraphQL достаточно подробно, чтобы понять, как мыслит Weaviate о запросах, а во втором уроке перейдём к SDK.
Сравнение трёх инструментов
| FAISS | Qdrant | Weaviate | |
|---|---|---|---|
| Тип | Библиотека (in-memory) | Векторная БД | Векторная БД + граф знаний |
| Схема данных | Нет | Гибкий payload без контракта | Явная схема классов и свойств |
| Встроенная векторизация | Нет | Нет | Да, через подключаемые модули |
| Основной API | Python | REST / gRPC | GraphQL / REST / gRPC |
| Гибридный поиск | Нет | Да (sparse + dense векторы) | Да (BM25 + HNSW) |
| Связи между объектами | Нет | Нет | Да, кросс-референсы |
| Мультимодальность | Только если сделаете сами | Только если сделаете сами | Встроена (CLIP, img2vec) |
| Порог входа | Низкий | Средний | Выше среднего |
Weaviate — не «лучший» и не «худший» инструмент, он решает другой класс задач. Если нужно быстро сделать поиск по однородным документам, Qdrant справится проще. Если данные разнородны, между ними есть явные отношения, важна встроенная векторизация или мультимодальный поиск — Weaviate будет органичным выбором. Сложность настройки окупается гибкостью запросов, которую сложно получить другими средствами.
GraphQL в Weaviate: синтаксис и первые запросы
Weaviate использует GraphQL как основной язык для чтения данных. Если вы раньше с ним не работали, поначалу синтаксис может казаться непривычным — но он устроен логично, и через несколько запросов становится понятно его главное преимущество: форму ответа вы описываете сами, и база возвращает ровно то, что вы попросили.
Основы синтаксиса
В GraphQL любой запрос — это документ, описывающий что запросить и что вернуть. Две основные операции: query для чтения и mutation для записи. В Weaviate мутации через GraphQL используются редко — управление схемой и загрузка данных делаются через REST API или Python SDK. GraphQL здесь заточен под чтение.
Запрос строится из трёх частей. Selection set — фигурные скобки, описывающие какие поля вернуть. Arguments — круглые скобки с параметрами: фильтры, лимиты, поисковые операторы. Field — имя поля или вложенного объекта. Самый простой запрос выглядит так:
{
Get {
Article(limit: 5) {
title
publishedAt
}
}
}
Мы обращаемся к корневому полю Get, затем к классу Article с аргументом limit: 5, и просим вернуть только поля title и publishedAt. Weaviate вернёт ровно их — никаких лишних данных.
Три корневых операции
Weaviate предоставляет три корневых поля. Get — основной тип запросов, возвращает объекты конкретного класса с возможностью семантического поиска, фильтрации, сортировки и перехода по кросс-референсам. Большинство запросов, которые мы будем писать, начинаются именно с Get. Aggregate — статистические запросы: количество объектов, среднее, минимум, максимум, группировка по полю. Explore — семантическое исследование без указания конкретного класса: ищет объекты любого класса по вектору или тексту. Используется когда структура данных неизвестна заранее.
Поле _additional
Weaviate добавляет к каждому объекту виртуальное поле _additional, которое содержит служебные метаданные: идентификатор объекта, расстояние до запросного вектора, нормализованный скор, сам вектор объекта, объяснение скора. По умолчанию эти данные не возвращаются — вы явно запрашиваете то, что нужно:
{
Get {
Article(
nearText: { concepts: ["machine learning"] }
limit: 3
) {
title
_additional {
id
distance
score
}
}
}
}
Поле distance — косинусное расстояние от запросного вектора до объекта (меньше = ближе). Поле score — нормализованный скор, особенно полезен при гибридном поиске, где участвуют два сигнала. Поле id — UUID объекта, нужен если вы хотите потом обратиться к нему через REST или SDK. Всё это мы разберём подробнее в следующем шаге.
Запуск Weaviate локально
Для работы с GraphQL нам нужен запущенный инстанс. Самый простой способ — Docker Compose. Создаём файл docker-compose.yml:
services:
weaviate:
image: semitechnologies/weaviate:1.35.9-0c75695
container_name: weaviate
ports:
- "8080:8080" # REST + GraphQL
- "50051:50051" # gRPC (Python SDK v4)
environment:
QUERY_DEFAULTS_LIMIT: 25
AUTHENTICATION_ANONYMOUS_ACCESS_ENABLED: "true"
PERSISTENCE_DATA_PATH: "/var/lib/weaviate"
DEFAULT_VECTORIZER_MODULE: "text2vec-transformers"
ENABLE_MODULES: "text2vec-transformers"
CLUSTER_HOSTNAME: "node1"
TRANSFORMERS_INFERENCE_API: "http://text2vec-transformers:8080"
CLIP_INFERENCE_API: "http://multi2vec-clip:8080"
depends_on:
text2vec-transformers:
condition: service_healthy
volumes:
- weaviate_data:/var/lib/weaviate
healthcheck:
test: ["CMD", "wget", "--spider", "-q", "http://localhost:8080/v1/.well-known/ready"]
interval: 10s
timeout: 5s
retries: 5
text2vec-transformers:
image: semitechnologies/transformers-inference:baai-bge-m3-onnx
container_name: text2vec-transformers
ports:
- "8081:8080"
environment:
ENABLE_CUDA: "0" # CPU-режим, для GPU замените на "1" и убедитесь что установлен nvidia-docker
healthcheck:
test: ["CMD-SHELL", "bash -c '</dev/tcp/localhost/8080' || exit 1"]
interval: 15s
timeout: 10s
retries: 10
start_period: 60s # даём время загрузить модель в память
volumes:
weaviate_data:
Тут мы устанавливаем Weaviate с модулем text2vec-transformers с моделью эмбеддингов baai-bge.
Можно разворачивать сервис с разными модулями. Тут есть список доступных модулей для создания эмбеддингов
docker compose up -d
Через 10–15 секунд база будет доступна на http://localhost:8080. Убедиться, что всё запустилось:
curl http://localhost:8080/v1/meta
В ответе будет список активных модулей и версия Weaviate.
Заметьте, что мы пробросили порт 50051 для gRPC — он нужен Python SDK v4, который мы будем использовать в следующем уроке. Порт 8080 обслуживает и REST, и GraphQL эндпоинт.
Altair GraphQL Client
Для интерактивной работы с GraphQL мы используем Altair — бесплатный клиент, доступный как расширение для Chrome/Firefox и как desktop-приложение. Скачать можно на altairgraphql.dev. После установки укажите адрес эндпоинта: http://localhost:8080/v1/graphql.
UI-консоль была удалена из образа Weaviate начиная с v1.30, поэтому Altair — основной инструмент для интерактивной отладки запросов.
Создаём схему и первые объекты
Управление схемой делается через REST API, а не GraphQL. Создадим класс Article:
curl -X POST http://localhost:8080/v1/schema \
-H "Content-Type: application/json" \
-d '{
"class": "Article",
"vectorizer": "none",
"properties": [
{"name": "title", "dataType": ["text"]},
{"name": "content", "dataType": ["text"]},
{"name": "publishedAt", "dataType": ["date"]},
{"name": "rating", "dataType": ["number"]},
{"name": "published", "dataType": ["boolean"]}
]
}'
Параметр "vectorizer": "none" означает, что мы будем подавать векторы сами, а не через встроенный модуль. Добавим несколько объектов для практики:
curl -X POST http://localhost:8080/v1/objects \
-H "Content-Type: application/json" \
-d '{
"class": "Article",
"properties": {
"title": "Introduction to Vector Databases",
"content": "Vector databases store embeddings and enable semantic search at scale",
"publishedAt": "2025-03-15T00:00:00Z",
"rating": 4.8,
"published": true
}
}'
Первые запросы в Altair
Попробуем несколько запросов, которые иллюстрируют ключевые возможности. Сначала простое чтение с сортировкой:
{
Get {
Article(
sort: [{ path: ["rating"], order: desc }]
limit: 5
) {
title
rating
_additional { id }
}
}
}
Затем агрегация — посмотрим сколько объектов в базе и какой средний рейтинг:
{
Aggregate {
Article {
meta { count }
rating {
mean
maximum
minimum
}
}
}
}
Эти два запроса уже показывают разницу между Get и Aggregate: первый возвращает объекты, второй — только статистику без единого объекта в ответе.
В следующем шаге мы подключим векторизатор и разберём операторы семантического поиска: nearText, nearVector, nearImage и hybrid.
Семантический поиск
На предыдущем шаге мы получали данные через простые запросы с сортировкой. Теперь переходим к главному — поиску по смыслу. В Weaviate для этого есть четыре оператора: nearText, nearVector, nearImage и hybrid. Все они задаются как аргументы внутри Get, и каждый решает свою задачу.
Так как мы создали класс Article с параметром "vectorizer": "none" — это значит, что база принимает векторы, которые вы подаёте сами, но не умеет векторизовать текстовые запросы. Чтобы работали операторы nearText и hybrid, классу нужен подключённый векторизатор. Удалим старый класс
curl -X DELETE http://localhost:8080/v1/schema/Article
И пересоздадим его:
curl -X POST http://localhost:8080/v1/schema \
-H "Content-Type: application/json" \
-d '{
"class": "Article",
"vectorizer": "text2vec-transformers",
"properties": [
{"name": "title", "dataType": ["text"]},
{"name": "content", "dataType": ["text"]},
{"name": "publishedAt", "dataType": ["date"]},
{"name": "rating", "dataType": ["number"]},
{"name": "published", "dataType": ["boolean"]}
]
}'
Теперь при добавлении объекта Weaviate сам вызовет контейнер text2vec-transformers, получит эмбеддинг текстовых полей и сохранит его. Добавим несколько статей для примеров:
curl -X POST http://localhost:8080/v1/batch/objects \
-H "Content-Type: application/json" \
-d '{
"objects": [
{
"class": "Article",
"properties": {
"title": "Introduction to Graph Neural Networks",
"content": "Graph neural networks extend deep learning to graph-structured data",
"publishedAt": "2025-01-10T00:00:00Z",
"rating": 4.7,
"published": true
}
},
{
"class": "Article",
"properties": {
"title": "Attention Mechanisms in Transformers",
"content": "Self-attention allows models to weigh relationships between all tokens",
"publishedAt": "2025-02-20T00:00:00Z",
"rating": 4.9,
"published": true
}
},
{
"class": "Article",
"properties": {
"title": "Vector Databases Overview",
"content": "Vector databases store and retrieve high-dimensional embeddings efficiently",
"publishedAt": "2025-03-15T00:00:00Z",
"rating": 4.5,
"published": false
}
}
]
}'
Теперь у нас есть класс с векторизатором и несколько объектов — можно переходить к операторам поиска.
nearText — поиск по текстовому запросу
Оператор nearText принимает текстовую строку, передаёт её в подключённый text2vec-модуль, получает эмбеддинг и находит ближайшие объекты по косинусному расстоянию. Это самый удобный вариант, когда запрос приходит от пользователя в виде текста:
{
Get {
Article(
nearText: {
concepts: ["graph neural networks"]
certainty: 0.6
}
limit: 5
) {
title
_additional { distance certainty }
}
}
}
Разберём параметры.
concepts— массив строк, Weaviate усредняет их векторы как единый запрос: если передать["vector search", "approximate nearest neighbor"], результат будет в пространстве обеих концепций одновременноcertainty: 0.6— порог: объекты ниже 0.6 не попадут в выдачу вне зависимости отlimitlimit: 5— максимальное число объектов в ответе
В блоке выбора полей мы запрашиваем
title— заголовок статьи_additionalс двумя метриками:distance— косинусное расстояние от запросного вектора до объекта (меньше = ближе), иcertainty— то же самое в шкале от 0 до 1, где 1 означает полное совпадение.
Оба значения Weaviate вычисляет динамически и возвращает только если явно попросить.
nearVector — поиск по готовому вектору
Если эмбеддинг уже вычислен — например, в Python через OpenAI API или Sentence Transformers — используем nearVector. Он работает вне зависимости от того, какой векторизатор настроен у класса: вы просто подаёте массив чисел нужной размерности. Это полезно, когда нужен полный контроль над моделью или когда один вектор переиспользуется в нескольких запросах:
{
Get {
Article(
nearVector: {
vector: [0.12, 0.98, -0.34, 0.07, ...]
distance: 0.25
}
limit: 3
) {
title
_additional { distance id }
}
}
}
Здесь вместо certainty мы используем distance — максимально допустимое косинусное расстояние. Объекты с расстоянием больше 0.25 не попадут в результат независимо от limit.
На практике вектор запроса вычисляется в Python и передаётся через SDK или REST — вручную в Altair его вводят редко. В следующем уроке мы увидим, как это делается через Python-клиент.
distance и certainty: в чём разница
Оба параметра управляют «строгостью» поиска, но выражают её по-разному. distance — численное косинусное расстояние: 0 означает идеальное совпадение, 2 — противоположные направления векторов. certainty — интуитивный показатель уверенности от 0 до 1, где 1 — полное совпадение. Это поле унаследовано из ранних версий Weaviate и оно не является универсальным показателем релевантности — значение зависит от метрики и нормализации конкретной модели, не сопоставимо между разными коллекциями и конфигурациями. Для порогов отсечения и оценки качества используйте distance или score и подбирайте пороги на своих данных. certainty удобен только для быстрой визуальной оценки, не более. Между ними прямая связь:
certainty=1−distance2certainty=1−2distance
Для nearText, когда вы ещё не знаете типичных расстояний для своих данных, удобнее начинать с certainty в диапазоне 0.6–0.8 и подбирать экспериментально. Для nearVector, когда вы работаете с конкретными моделями и понимаете масштаб своих данных, точнее использовать distance.
nearImage — поиск по изображениям
Через модуль img2vec-neural можно хранить изображения в Weaviate и искать похожие по визуальному содержанию. При создании класса указывается соответствующий векторизатор, а в запросе передаётся изображение в Base64:
{
Get {
Photo(
nearImage: {
image: "iVBORw0KGgoAAAANSUhEUgAAAAUA..."
distance: 0.3
}
limit: 5
) {
filename
description
_additional { distance }
}
}
}
Weaviate вычислит вектор переданного изображения тем же модулем, что использовался при индексации, и найдёт ближайшие.
Особняком стоит CLIP-модуль (multi2vec-clip). Модель CLIP обучена в едином векторном пространстве для текста и изображений — это значит, что вектор слова «кот» и вектор фотографии кота будут близки. Поэтому с CLIP-модулем становится возможным кросс-модальный поиск: искать изображения через nearText, а тексты через nearImage. Один класс, одно пространство, оба типа данных.
Гибридный поиск: hybrid
Оператор hybrid комбинирует BM25 и векторный поиск, объединяя результаты через алгоритм Relative Score Fusion.
Параметр alpha управляет балансом:
scorehybrid=α⋅scorevector+(1−α)⋅scoreBM25scorehybrid=α⋅scorevector+(1−α)⋅scoreBM25
При alpha = 0 работает только BM25, при alpha = 1 — только векторный поиск, при alpha = 0.5 — равный вклад обоих:
{
Get {
Article(
hybrid: {
query: "attention mechanism transformer"
alpha: 0.4
}
limit: 10
) {
title
_additional {
score
explainScore
}
}
}
}
Поле explainScore в _additional показывает, какой вклад в итоговый скор внесли BM25 и вектор. Это особенно полезно при подборе alpha: видно, почему конкретный документ попал в топ и насколько каждый из сигналов повлиял на результат.
Если у вас уже есть готовый вектор запроса, его можно передать прямо в hybrid, чтобы Weaviate не вычислял его заново:
hybrid: {
query: "attention mechanism transformer"
vector: [0.12, 0.98, -0.34, ...]
alpha: 0.4
}
Метаданные через _additional
Независимо от оператора поиска, поле _additional позволяет запросить любые служебные данные. Перечислим полный набор полезных полей:
_additional {
id # UUID объекта — нужен для обращения через REST или SDK
distance # косинусное расстояние до запросного вектора
certainty # то же расстояние в шкале 0–1
score # нормализованный скор, особенно полезен в hybrid
explainScore # вклад BM25 и вектора в итоговый скор
vector # полный эмбеддинг объекта
}
Поле vector запрашивайте только когда это действительно нужно — это сотни или тысячи чисел на каждый объект в ответе. Типичные случаи: построение визуализации t-SNE/UMAP, экспорт эмбеддингов для внешней обработки или сравнение с результатами другой модели.
Какой оператор выбрать
Есть простое правило: если запрос — текстовая строка и у класса настроен text2vec-модуль, используем nearText. Если вектор уже вычислен в Python — nearVector, он работает с любым классом независимо от векторизатора. Если важны и точные совпадения слов, и семантическая близость — hybrid с подобранным alpha. Для изображений — nearImage, а для кросс-модального поиска текст↔картинка — multi2vec-clip с nearText на классе с изображениями.
Фильтры, агрегации и дополнительные возможности
Семантический поиск по-настоящему раскрывается в сочетании с точными фильтрами. В этом шаге мы разберём параметр where, инструменты управления результатами — сортировку и пагинацию, агрегирующие запросы через Aggregate и кросс-референсы в запросах. В конце обозначим более продвинутые возможности Weaviate, которые пригодятся в реальных проектах.
Фильтры where
Параметр where позволяет ограничить область поиска объектами, соответствующими условию по полям. Структура фильтра: path — путь к полю, operator — тип сравнения, и значение с явным указанием типа:
{
Get {
Article(
nearText: { concepts: ["vector database"] }
where: {
path: ["rating"]
operator: GreaterThanEqual
valueNumber: 4.7
}
limit: 10
) {
title
rating
_additional { distance }
}
}
}
Суффикс типа у значения — valueNumber, valueText, valueDate, valueBoolean — должен совпадать с типом поля в схеме. Доступные операторы: Equal, NotEqual, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual, Like, ContainsAny и ContainsAll для массивов, WithinGeoRange для геоточек.
Оператор Like работает по строкам и поддерживает символ подстановки *, который заменяет любое количество символов. Например, valueText: "*neural*" найдёт все объекты, где поле содержит слово «neural» в любом месте строки: «graph neural networks», «neural architecture search» и т.д. valueText: "graph*" найдёт всё, что начинается на «graph». Без звёздочки Like ведёт себя как точное совпадение — аналогично Equal.
Важный момент: where-фильтр применяется до векторного поиска. Weaviate сначала отбирает объекты по условию, затем ищет ближайшие векторы среди них. Это снижает объём k-NN поиска и часто ускоряет запрос.
Составные фильтры AND и OR
Несколько условий объединяются через operator: And или operator: Or с массивом operands. Условия можно вкладывать на любую глубину:
where: {
operator: And
operands: [
{
path: ["published"]
operator: Equal
valueBoolean: true
},
{
operator: Or
operands: [
{
path: ["publishedAt"]
operator: GreaterThan
valueDate: "2024-01-01T00:00:00Z"
},
{
path: ["rating"]
operator: GreaterThan
valueNumber: 4.8
}
]
}
]
}
Этот фильтр вернёт только опубликованные статьи, которые либо вышли после 2024 года, либо имеют рейтинг выше 4.8.
Фильтры по кросс-референсам
Если объект ссылается на другой класс, можно писать условия через эту ссылку — просто добавив её в path вместе с именем целевого класса:
where: {
path: ["hasAuthor", "Author", "experienceYears"]
operator: GreaterThan
valueNumber: 5
}
Weaviate пройдёт по ссылке hasAuthor к классу Author и применит условие к его полю. Таким образом можно строить сложные условия через несколько уровней связей, не делая дополнительных запросов к базе.
Кросс-референсы в selection set
Когда между классами установлена связь, её можно пройти прямо в запросе Get и получить данные связанных объектов в одном ответе:
{
Get {
Article(
nearText: { concepts: ["weaviate tutorial"] }
limit: 3
) {
title
hasAuthor {
... on Author {
name
experienceYears
}
}
}
}
}
Конструкция ... on Author — это GraphQL inline fragment. Она нужна потому, что поле-ссылка может теоретически указывать на объекты разных классов. Через фрагмент мы указываем, какой именно класс нас интересует и какие его поля запросить. Вложенный блок поддерживает limit, where, сортировку — то есть связанные объекты можно тоже фильтровать и сортировать.
Сортировка и пагинация
Аргумент sort принимает массив объектов с полями path и order. Можно сортировать по нескольким полям одновременно:
{
Get {
Article(
sort: [
{ path: ["publishedAt"], order: desc },
{ path: ["rating"], order: desc }
]
limit: 20
offset: 20
) {
title
publishedAt
rating
}
}
}
Для пагинации используются limit и offset. Пагинация — это разбивка большого списка результатов на страницы, чтобы не отдавать всё сразу. Представьте поисковую выдачу: первые 20 результатов — первая страница, следующие 20 — вторая, и так далее. limit задаёт размер страницы, offset — сколько объектов пропустить с начала. Чтобы получить вторую страницу из 20 результатов, ставим limit: 20, offset: 20, третью — limit: 20, offset: 40.
Это работает, но на больших коллекциях offset-пагинация становится медленной: база вынуждена внутри себя просматривать все предыдущие страницы, прежде чем вернуть нужную. На сотой странице она просмотрит 2000 объектов вхолостую. Для эффективного перелистывания больших объёмов используется cursor-пагинация: параметр after принимает UUID последнего объекта предыдущей страницы, и Weaviate продолжает выдачу с этой точки без сканирования начала. UUID берётся из поля _additional { id } последнего объекта в текущей странице и передаётся в следующий запрос.
Агрегации
Корневой резолвер Aggregate предназначен для статистических запросов. Он не возвращает объекты — только сводные данные:
{
Aggregate {
Article {
meta { count }
rating {
mean
median
maximum
minimum
}
publishedAt {
maximum
minimum
}
}
}
}
Числовые и дата-поля поддерживают mean, median, sum, maximum, minimum. Текстовые поля — topOccurrences, список наиболее частых значений с их количеством.
Агрегации можно комбинировать с where и даже с семантическим поиском — тогда статистика считается только по найденному подмножеству объектов. Группировка делается через groupBy:
{
Aggregate {
Article(
groupBy: { path: ["category"], groups: 10, objectsPerGroup: 3 }
) {
groupedBy { value }
meta { count }
rating { mean }
}
}
}
Такой запрос покажет количество статей и средний рейтинг по каждой категории — без единой строки кода на стороне клиента.
Сжатие векторов
Weaviate предлагает встроенные метода сжатия, которые существенно снижают потребление памяти с минимальной потерей точности.
Product Quantization (PQ) делит вектор на несколько сегментов и заменяет каждый из них индексом ближайшего центроида из заранее обученного словаря. Вместо массива чисел с плавающей точкой хранится компактный код — обычно в 8–32 раза меньше исходного размера. При поиске Weaviate работает с этими кодами напрямую, не восстанавливая полные векторы.
Включается при создании класса через параметры vectorIndexConfig:
curl -X POST http://localhost:8080/v1/schema \
-H "Content-Type: application/json" \
-d '{
"class": "Article",
"vectorizer": "text2vec-transformers",
"vectorIndexConfig": {
"pq": {
"enabled": true,
"segments": 96,
"centroids": 256
}
},
"properties": [
{"name": "title", "dataType": ["text"]},
{"name": "content", "dataType": ["text"]}
]
}'
Параметры:
segments— на сколько частей делится вектор (должен быть кратен размерности; для 768-мерных векторов подходит 96 или 128).centroids— размер словаря для каждого сегмента: больше центроидов — выше точность, но больше памяти под словарь. Значение 256 — хороший баланс для большинства задач.
Важный нюанс: PQ требует обучения словаря на реальных данных. Weaviate делает это автоматически, как только в коллекции накапливается достаточно объектов (по умолчанию — 100 000). До этого момента векторы хранятся в исходном виде.
Scalar Quantization (SQ) уменьшает размер вектора за счёт понижения точности каждого его компонента. Значения с плавающей точкой преобразуются в компактное целочисленное представление (обычно int8), сохраняя структуру вектора, но снижая требования к памяти. В результате достигается сжатие примерно в 4 раза при небольших потерях точности. В отличие от PQ, SQ не требует предварительного обучения и начинает работать сразу после включения.
curl -X POST http://localhost:8080/v1/schema \
-H "Content-Type: application/json" \
-d '{
"class": "Article",
"vectorizer": "text2vec-transformers",
"vectorIndexConfig": {
"sq": {
"enabled": true
}
},
"properties": [
{"name": "title", "dataType": ["text"]},
{"name": "content", "dataType": ["text"]}
]
}'
Scalar Quantization хорошо подходит для коллекций среднего размера, когда требуется снизить потребление памяти без сложной настройки и без этапа обучения квантайзера.
Binary Quantization (BQ) идёт дальше: каждое измерение вектора превращается в один бит — положительное значение становится 1, отрицательное — 0. Это даёт сжатие в 32 раза по сравнению с float32, а сравнение векторов сводится к быстрым побитовым операциям. Включается аналогично:
curl -X POST http://localhost:8080/v1/schema \
-H "Content-Type: application/json" \
-d '{
"class": "Article",
"vectorizer": "text2vec-transformers",
"vectorIndexConfig": {
"bq": {
"enabled": true,
"rescoreLimit": 200
}
},
"properties": [
{"name": "title", "dataType": ["text"]},
{"name": "content", "dataType": ["text"]}
]
}'
rescoreLimitзадаёт, сколько кандидатов после быстрого BQ-поиска пересчитываются по точным векторам перед финальной выдачей. Это компенсирует потери точности: BQ быстро находит грубый список кандидатов, а перерасчёт по оригинальным векторам уточняет порядок. Чем большеrescoreLimit— тем выше recall, но медленнее поиск.
BQ хорошо работает с моделями, у которых векторы достаточно «поляризованы» — например, с современными OpenAI и Cohere эмбеддингами. На менее подходящих моделях потери точности могут быть заметными, поэтому перед включением стоит проверить recall на своих данных: запустите одинаковые запросы с BQ и без, сравните топ-10 результатов.
Residual Quantization (RQ) кодирует вектор поэтапно, последовательно аппроксимируя его несколькими уровнями квантования. Сначала создаётся грубое приближение вектора, затем кодируется остаточная ошибка, после чего процесс повторяется для нового остатка. Такой подход позволяет достичь сильного сжатия при более высокой точности по сравнению с бинарным квантованием.
curl -X POST http://localhost:8080/v1/schema \
-H "Content-Type: application/json" \
-d '{
"class": "Article",
"vectorizer": "text2vec-transformers",
"vectorIndexConfig": {
"rq": {
"enabled": true,
"segments": 8,
"centroids": 256
}
},
"properties": [
{"name": "title", "dataType": ["text"]},
{"name": "content", "dataType": ["text"]}
]
}'
Параметры:
segments— количество последовательных этапов квантования. Большее значение повышает точность, но увеличивает время поиска.centroids— размер словаря на каждом этапе квантования. Большее число центроидов повышает точность, но требует больше памяти.
RQ требует обучения на реальных данных, аналогично Product Quantization. Обычно используется для крупных коллекций, где важно добиться сильного сжатия без существенного падения качества поиска.
Как выбрать метод
| Метод | Сжатие | Обучение | Точность | Когда брать |
|---|---|---|---|---|
| SQ | ~4× | нет | высокая | безопасное уменьшение памяти |
| PQ | 8–32× | да | высокая | большие коллекции |
| RQ | 8–32× | да | выше BQ | компромисс скорость/точность |
| BQ | ~32× | нет | ниже | экстремальная экономия памяти |
Что ещё есть в Weaviate
Несколько важных возможностей, которые выходят за рамки этого урока, но полезно знать как ориентиры.
- Multi-tenancy — полная логическая изоляция данных нескольких клиентов внутри одного кластера: у каждого тенанта свои объекты и свой HNSW-граф, но инфраструктура общая. Актуально для SaaS-приложений, где нужно держать данные разных пользователей раздельно.
- Резервное копирование — встроенный механизм снимков в S3, GCS, Azure Blob или на локальный диск, с возможностью восстановления в один вызов API.
- Мониторинг — встроенный экспортёр метрик в формате Prometheus с готовыми дашбордами Grafana для отслеживания латентности, throughput и состояния индексов, это мы подробнее рассмотрим в DevOps-модуле.
Итоги урока
Мы прошли путь от теории до первых практических запросов.
- Weaviate отличается от FAISS и Qdrant объектной моделью с явной схемой классов, встроенными модулями векторизации и графовыми связями между объектами.
- Named vectors позволяют держать несколько независимых векторных представлений на один объект, что открывает гибкие мультимодальные сценарии — включая кросс-модальный поиск текст↔изображение через CLIP.
- GraphQL API позволяет формулировать сложные запросы компактно: семантический поиск через
nearText,nearVectorилиnearImage, точные фильтрыwhere, агрегации и переходы по кросс-референсам объединяются в одном обращении к базе. - Гибридный поиск через оператор
hybridсочетает BM25 и векторный поиск с настраиваемым балансом через параметрalpha, что делает результаты устойчивыми и к специальной терминологии, и к запросам по смыслу. - Встроенное сжатие векторов (PQ, BQ, SQ, RQ), позволяет значительно уменьшить потребление памяти и ускорить поиск с контролируемой потерей точности.
В следующем уроке мы переходим от GraphQL к Python SDK v4: создадим коллекцию с явной схемой, загрузим датасет батчем и реализуем RAG-пайплайн прямо из Python — без GraphQL-прослойки и с полным контролем над каждым шагом.
Задача 1
Какое ключевое отличие модели данных Weaviate?
Задача 2
Что обеспечивает гибридный поиск в Weaviate?
Задача 3
Когда nearVector уместнее nearText?
Задача 4
Что делает поле _additional в ответе?
Задача 5
Какие возможности даёт объектная схема?
Задача 6
Что верно про hybrid?
Задача 7
Что верно про named vectors?
Задача 8
Сопоставьте оператор и назначение
Сопоставьте элементы
Задача 9
Сопоставьте метод сжатия и свойство
Сопоставьте элементы
Задача 10
Сопоставьте API и роль
Сопоставьте элементы
Продолжить с сохранением прогресса
Перейдите к уроку в учебном кабинете.
