mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 08:26:21 +02:00
Native CLI i18n: The TrustGraph CLI has built-in translation support that dynamically loads language strings. You can test and use different languages by simply passing the --lang flag (e.g., --lang es for Spanish, --lang ru for Russian) or by configuring your environment's LANG variable. Automated Docs Translations: This PR introduces autonomously translated Markdown documentation into several target languages, including Spanish, Swahili, Portuguese, Turkish, Hindi, Hebrew, Arabic, Simplified Chinese, and Russian.
621 lines
36 KiB
Markdown
621 lines
36 KiB
Markdown
---
|
||
layout: default
|
||
title: "Техническая спецификация структурированных данных (часть 2)"
|
||
parent: "Russian (Beta)"
|
||
---
|
||
|
||
# Техническая спецификация структурированных данных (часть 2)
|
||
|
||
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
|
||
|
||
## Обзор
|
||
|
||
Эта спецификация рассматривает проблемы и недостатки, выявленные в процессе первоначальной реализации интеграции структурированных данных TrustGraph, как описано в `structured-data.md`.
|
||
|
||
## Описание проблем
|
||
|
||
### 1. Несоответствие в наименованиях: "Объект" против "Строка"
|
||
|
||
В текущей реализации используется терминология "объект" во всем коде (например, `ExtractedObject`, извлечение объектов, векторные представления объектов). Это наименование слишком общее и вызывает путаницу:
|
||
|
||
Термин "объект" является перегруженным в программном обеспечении (объекты Python, объекты JSON и т.д.)
|
||
Данные, которые обрабатываются, по сути являются табличными - строки в таблицах с определенными схемами.
|
||
"Строка" более точно описывает модель данных и соответствует терминологии баз данных.
|
||
|
||
Это несоответствие проявляется в названиях модулей, названиях классов, типах сообщений и документации.
|
||
|
||
### 2. Ограничения запросов к хранилищу строк
|
||
|
||
Текущая реализация хранилища строк имеет значительные ограничения по запросам:
|
||
|
||
**Несоответствие с естественным языком**: Запросы испытывают трудности с вариациями реальных данных. Например:
|
||
Сложно найти базу данных улиц, содержащую `"CHESTNUT ST"`, при запросе информации о `"Chestnut Street"`.
|
||
Аббревиатуры, различия в регистре и вариации форматирования нарушают запросы точного соответствия.
|
||
Пользователи ожидают семантического понимания, но хранилище обеспечивает только буквальное сопоставление.
|
||
|
||
**Проблемы с эволюцией схемы**: Изменение схем вызывает проблемы:
|
||
Существующие данные могут не соответствовать обновленным схемам.
|
||
Изменения структуры таблицы могут нарушить запросы и целостность данных.
|
||
Отсутствует четкий путь миграции для обновлений схемы.
|
||
|
||
### 3. Требуются векторные представления строк
|
||
|
||
В связи с проблемой 2, системе требуются векторные представления данных строк для обеспечения:
|
||
|
||
Семантического поиска по структурированным данным (поиск "Chestnut Street", когда данные содержат "CHESTNUT ST").
|
||
Сопоставления по сходству для нечетких запросов.
|
||
Гибридного поиска, сочетающего структурированные фильтры с семантическим сходством.
|
||
Лучшей поддержки запросов на естественном языке.
|
||
|
||
Сервис создания векторных представлений был определен, но не реализован.
|
||
|
||
### 4. Неполная загрузка данных строк
|
||
|
||
Конвейер загрузки структурированных данных не полностью функционирует:
|
||
|
||
Существуют диагностические подсказки для классификации форматов входных данных (CSV, JSON и т.д.).
|
||
Сервис загрузки, использующий эти подсказки, не подключен к системе.
|
||
Отсутствует сквозной путь для загрузки предварительно структурированных данных в хранилище строк.
|
||
|
||
## Цели
|
||
|
||
**Гибкость схемы**: Обеспечить эволюцию схемы без нарушения существующих данных или необходимости миграций.
|
||
**Согласованное наименование**: Стандартизировать использование термина "строка" во всем коде.
|
||
**Семантическая запросаемость**: Поддержка нечеткого/семантического сопоставления с помощью векторных представлений строк.
|
||
**Полный конвейер загрузки**: Предоставить сквозной путь для загрузки структурированных данных.
|
||
|
||
## Технический дизайн
|
||
|
||
### Унифицированная схема хранения строк
|
||
|
||
В предыдущей реализации для каждой схемы создавалась отдельная таблица Cassandra. Это вызывало проблемы при эволюции схем, поскольку изменения структуры таблицы требовали миграций.
|
||
|
||
В новой разработке используется единая унифицированная таблица для всех данных строк:
|
||
|
||
```sql
|
||
CREATE TABLE rows (
|
||
collection text,
|
||
schema_name text,
|
||
index_name text,
|
||
index_value frozen<list<text>>,
|
||
data map<text, text>,
|
||
source text,
|
||
PRIMARY KEY ((collection, schema_name, index_name), index_value)
|
||
)
|
||
```
|
||
|
||
#### Описание столбцов
|
||
|
||
| Столбец | Тип | Описание |
|
||
|--------|------|-------------|
|
||
| `collection` | `text` | Идентификатор сбора/импорта данных (из метаданных) |
|
||
| `schema_name` | `text` | Название схемы, которой соответствует эта строка |
|
||
| `index_name` | `text` | Название индексируемого поля (полей), объединенное запятыми для составных индексов |
|
||
| `index_value` | `frozen<list<text>>` | Значение(я) индекса в виде списка |
|
||
| `data` | `map<text, text>` | Данные строки в виде пар ключ-значение |
|
||
| `source` | `text` | Необязательный URI, ссылающийся на информацию об источнике в графе знаний. Пустая строка или NULL указывает на отсутствие источника. |
|
||
|
||
#### Обработка индексов
|
||
|
||
Каждая строка хранится несколько раз - один раз для каждого индексируемого поля, определенного в схеме. Первичные ключи рассматриваются как индекс без специального маркера, что обеспечивает гибкость в будущем.
|
||
|
||
**Пример индекса для одного поля:**
|
||
Схема определяет `email` как индексируемое
|
||
`index_name = "email"`
|
||
`index_value = ['foo@bar.com']`
|
||
|
||
**Пример составного индекса:**
|
||
Схема определяет составной индекс для `region` и `status`
|
||
`index_name = "region,status"` (названия полей отсортированы и объединены запятыми)
|
||
`index_value = ['US', 'active']` (значения в том же порядке, что и названия полей)
|
||
|
||
**Пример первичного ключа:**
|
||
Схема определяет `customer_id` как первичный ключ
|
||
`index_name = "customer_id"`
|
||
`index_value = ['CUST001']`
|
||
|
||
#### Шаблоны запросов
|
||
|
||
Все запросы следуют одной и той же схеме, независимо от того, какой индекс используется:
|
||
|
||
```sql
|
||
SELECT * FROM rows
|
||
WHERE collection = 'import_2024'
|
||
AND schema_name = 'customers'
|
||
AND index_name = 'email'
|
||
AND index_value = ['foo@bar.com']
|
||
```
|
||
|
||
#### Компромиссы в проектировании
|
||
|
||
**Преимущества:**
|
||
Изменения схемы не требуют изменений структуры таблицы
|
||
Данные строк не видны Cassandra - добавление/удаление полей прозрачно
|
||
Единый шаблон запросов для всех методов доступа
|
||
Отсутствуют вторичные индексы Cassandra (которые могут быть медленными при больших масштабах)
|
||
Использование нативных типов Cassandra (`map`, `frozen<list>`)
|
||
|
||
**Компромиссы:**
|
||
Увеличение количества операций записи: каждая вставка строки = N вставок (по одной на каждое индексированное поле)
|
||
Дополнительные затраты памяти из-за дублирования данных строк
|
||
Информация о типах хранится в конфигурации схемы, преобразование происходит на уровне приложения
|
||
|
||
#### Модель согласованности
|
||
|
||
В проекте приняты определенные упрощения:
|
||
|
||
1. **Отсутствие обновлений строк**: Система работает только на добавление данных. Это устраняет проблемы согласованности, связанные с обновлением нескольких копий одной и той же строки.
|
||
|
||
2. **Толерантность к изменениям схемы**: При изменении схем (например, при добавлении/удалении индексов) существующие строки сохраняют свою первоначальную индексацию. Старые строки не будут доступны через новые индексы. Пользователи могут удалить и пересоздать схему для обеспечения согласованности, если это необходимо.
|
||
|
||
### Отслеживание и удаление разделов
|
||
|
||
#### Проблема
|
||
|
||
При использовании ключа раздела `(collection, schema_name, index_name)`, для эффективного удаления необходимо знать все ключи разделов, которые нужно удалить. Удаление только по `collection` или `collection + schema_name` требует знания всех значений `index_name`, которые содержат данные.
|
||
|
||
#### Таблица отслеживания разделов
|
||
|
||
Вторичная таблица поиска отслеживает, какие разделы существуют:
|
||
|
||
```sql
|
||
CREATE TABLE row_partitions (
|
||
collection text,
|
||
schema_name text,
|
||
index_name text,
|
||
PRIMARY KEY ((collection), schema_name, index_name)
|
||
)
|
||
```
|
||
|
||
Это обеспечивает эффективное обнаружение разделов для операций удаления.
|
||
|
||
#### Поведение модуля записи строк
|
||
|
||
Модуль записи строк поддерживает кэш в памяти, содержащий зарегистрированные пары `(collection, schema_name)`. При обработке строки:
|
||
|
||
1. Проверьте, есть ли `(collection, schema_name)` в кэше.
|
||
2. Если ⟦CODE_0⟧ не в кэше (первая строка для этой пары):
|
||
Найдите конфигурацию схемы, чтобы получить все имена индексов.
|
||
Вставьте записи в `row_partitions` для каждого `(collection, schema_name, index_name)`.
|
||
Добавьте пару в кэш.
|
||
3. Перейдите к записи данных строки.
|
||
|
||
Модуль записи строк также отслеживает события изменения конфигурации схемы. При изменении схемы соответствующие записи кэша очищаются, чтобы следующая строка вызвала повторную регистрацию с обновленными именами индексов.
|
||
|
||
Этот подход обеспечивает:
|
||
Записи в таблицу поиска происходят только один раз для каждой пары `(collection, schema_name)`, а не для каждой строки.
|
||
Таблица поиска отражает индексы, которые были активны во время записи данных.
|
||
Изменения схемы во время импорта обрабатываются правильно.
|
||
|
||
#### Операции удаления
|
||
|
||
**Удаление коллекции:**
|
||
```sql
|
||
-- 1. Discover all partitions
|
||
SELECT schema_name, index_name FROM row_partitions WHERE collection = 'X';
|
||
|
||
-- 2. Delete each partition from rows table
|
||
DELETE FROM rows WHERE collection = 'X' AND schema_name = '...' AND index_name = '...';
|
||
-- (repeat for each discovered partition)
|
||
|
||
-- 3. Clean up the lookup table
|
||
DELETE FROM row_partitions WHERE collection = 'X';
|
||
```
|
||
|
||
**Удалить коллекцию и схему:**
|
||
```sql
|
||
-- 1. Discover partitions for this schema
|
||
SELECT index_name FROM row_partitions WHERE collection = 'X' AND schema_name = 'Y';
|
||
|
||
-- 2. Delete each partition from rows table
|
||
DELETE FROM rows WHERE collection = 'X' AND schema_name = 'Y' AND index_name = '...';
|
||
-- (repeat for each discovered partition)
|
||
|
||
-- 3. Clean up the lookup table entries
|
||
DELETE FROM row_partitions WHERE collection = 'X' AND schema_name = 'Y';
|
||
```
|
||
|
||
### Встраивания строк
|
||
|
||
Встраивания строк обеспечивают семантическое/приближенное сопоставление проиндексированных значений, решая проблему несовместимости естественного языка (например, поиск "CHESTNUT ST", когда выполняется запрос "Chestnut Street").
|
||
|
||
#### Обзор архитектуры
|
||
|
||
Каждое проиндексированное значение преобразуется во встраивание и хранится в векторной базе данных (Qdrant). Во время запроса запрос также преобразуется во встраивание, находятся похожие векторы, и связанная метаинформация используется для поиска фактических строк в Cassandra.
|
||
|
||
#### Структура коллекции Qdrant
|
||
|
||
Одна коллекция Qdrant для каждой кортежи `(user, collection, schema_name, dimension)`:
|
||
|
||
**Именование коллекции:** `rows_{user}_{collection}_{schema_name}_{dimension}`
|
||
Имена очищаются (небуквенно-цифровые символы заменяются на `_`, приводятся к нижнему регистру, числовые префиксы получают префикс `r_`)
|
||
**Обоснование:** Позволяет чисто удалить экземпляр `(user, collection, schema_name)` путем удаления соответствующих коллекций Qdrant; суффикс измерения позволяет различным моделям встраивания сосуществовать.
|
||
|
||
#### Что подвергается встраиванию
|
||
|
||
Текстовое представление значений индекса:
|
||
|
||
| Тип индекса | Пример `index_value` | Текст для встраивания |
|
||
|------------|----------------------|---------------|
|
||
| Одиночное поле | `['foo@bar.com']` | `"foo@bar.com"` |
|
||
| Композитный | `['US', 'active']` | `"US active"` (объединенные пробелами) |
|
||
|
||
#### Структура точки
|
||
|
||
Каждая точка Qdrant содержит:
|
||
|
||
```json
|
||
{
|
||
"id": "<uuid>",
|
||
"vector": [0.1, 0.2, ...],
|
||
"payload": {
|
||
"index_name": "street_name",
|
||
"index_value": ["CHESTNUT ST"],
|
||
"text": "CHESTNUT ST"
|
||
}
|
||
}
|
||
```
|
||
|
||
| Поле полезной нагрузки | Описание |
|
||
|---------------|-------------|
|
||
| `index_name` | Индексированные поля, которые представляет эта вставка. |
|
||
| `index_value` | Исходный список значений (для поиска в Cassandra). |
|
||
| `text` | Текст, который был вставлен (для отладки/отображения). |
|
||
|
||
Обратите внимание: `user`, `collection` и `schema_name` определяются неявно из имени коллекции Qdrant.
|
||
|
||
#### Поток запросов
|
||
|
||
1. Пользователь запрашивает "Chestnut Street" для пользователя U, коллекции X, схемы Y.
|
||
2. Вставьте текст запроса.
|
||
3. Определите имя(и) коллекции Qdrant, соответствующие префиксу `rows_U_X_Y_`.
|
||
4. Выполните поиск в соответствующих коллекциях Qdrant для поиска ближайших векторов.
|
||
5. Получите соответствующие точки с полезными нагрузками, содержащими `index_name` и `index_value`.
|
||
6. Запрос к Cassandra:
|
||
```sql
|
||
SELECT * FROM rows
|
||
WHERE collection = 'X'
|
||
AND schema_name = 'Y'
|
||
AND index_name = '<from payload>'
|
||
AND index_value = <from payload>
|
||
```
|
||
7. Возврат соответствующих строк.
|
||
|
||
#### Необязательно: Фильтрация по имени индекса.
|
||
|
||
Запросы могут опционально фильтровать данные по `index_name` в Qdrant, чтобы искать только определенные поля:
|
||
|
||
**"Найти любое поле, соответствующее 'Chestnut'"** → поиск по всем векторам в коллекции.
|
||
**"Найти поле street_name, соответствующее 'Chestnut'"** → фильтрация по `payload.index_name = 'street_name'`.
|
||
|
||
#### Архитектура.
|
||
|
||
Векторные представления строк следуют **двухступенчатой схеме**, используемой в GraphRAG (graph-embeddings, document-embeddings):
|
||
|
||
**Этап 1: Вычисление векторных представлений** (`trustgraph-flow/trustgraph/embeddings/row_embeddings/`) - Использует `ExtractedObject`, вычисляет векторные представления через сервис векторных представлений, выдает `RowEmbeddings`.
|
||
**Этап 2: Хранение векторных представлений** (`trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/`) - Использует `RowEmbeddings`, записывает векторы в Qdrant.
|
||
|
||
Модуль записи строк в Cassandra является отдельным параллельным компонентом:
|
||
|
||
**Модуль записи строк в Cassandra** (`trustgraph-flow/trustgraph/storage/rows/cassandra`) - Использует `ExtractedObject`, записывает строки в Cassandra.
|
||
|
||
Все три компонента используют один и тот же поток данных, что обеспечивает их независимость. Это позволяет:
|
||
Независимое масштабирование записи в Cassandra по сравнению с генерацией векторных представлений и хранением векторов.
|
||
Отключение сервисов генерации векторных представлений, если они не требуются.
|
||
Отказы в одном компоненте не влияют на другие.
|
||
Согласованная архитектура с конвейерами GraphRAG.
|
||
|
||
#### Путь записи.
|
||
|
||
**Этап 1 (процессор векторных представлений строк):** При получении `ExtractedObject`:
|
||
|
||
1. Получение схемы для поиска индексированных полей.
|
||
2. Для каждого индексированного поля:
|
||
Создание текстового представления значения индекса.
|
||
Вычисление векторного представления через сервис векторных представлений.
|
||
3. Вывод сообщения `RowEmbeddings`, содержащего все вычисленные векторы.
|
||
|
||
**Этап 2 (запись векторных представлений в Qdrant):** При получении `RowEmbeddings`:
|
||
|
||
1. Для каждого векторного представления в сообщении:
|
||
Определение коллекции Qdrant из `(user, collection, schema_name, dimension)`.
|
||
Создание коллекции, если это необходимо (создание при первой записи).
|
||
Добавление точки с вектором и полезной нагрузкой.
|
||
|
||
#### Типы сообщений.
|
||
|
||
```python
|
||
@dataclass
|
||
class RowIndexEmbedding:
|
||
index_name: str # The indexed field name(s)
|
||
index_value: list[str] # The field value(s)
|
||
text: str # Text that was embedded
|
||
vectors: list[list[float]] # Computed embedding vectors
|
||
|
||
@dataclass
|
||
class RowEmbeddings:
|
||
metadata: Metadata
|
||
schema_name: str
|
||
embeddings: list[RowIndexEmbedding]
|
||
```
|
||
|
||
#### Интеграция удаления
|
||
|
||
Коллекции Qdrant обнаруживаются путем сопоставления префикса с шаблоном имени коллекции:
|
||
|
||
**Удаление `(user, collection)`:**
|
||
1. Перечислить все коллекции Qdrant, соответствующие префиксу `rows_{user}_{collection}_`
|
||
2. Удалить каждую соответствующую коллекцию
|
||
3. Удалить разделы строк Cassandra (как описано выше)
|
||
4. Очистить записи `row_partitions`
|
||
|
||
**Удаление `(user, collection, schema_name)`:**
|
||
1. Перечислить все коллекции Qdrant, соответствующие префиксу `rows_{user}_{collection}_{schema_name}_`
|
||
2. Удалить каждую соответствующую коллекцию (обрабатывает несколько измерений)
|
||
3. Удалить разделы строк Cassandra
|
||
4. Очистить `row_partitions`
|
||
|
||
#### Расположение модулей
|
||
|
||
| Этап | Модуль | Точка входа |
|
||
|-------|--------|-------------|
|
||
| Этап 1 | `trustgraph-flow/trustgraph/embeddings/row_embeddings/` | `row-embeddings` |
|
||
| Этап 2 | `trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/` | `row-embeddings-write-qdrant` |
|
||
|
||
### API запросов для векторных представлений строк
|
||
|
||
API запросов для векторных представлений строк является **отдельным API** от сервиса запросов строк GraphQL:
|
||
|
||
| API | Назначение | Бэкенд |
|
||
|-----|---------|---------|
|
||
| Запрос строк (GraphQL) | Точное сопоставление с индексированными полями | Cassandra |
|
||
| Запрос векторных представлений строк | Нечеткое/семантическое сопоставление | Qdrant |
|
||
|
||
Это разделение позволяет четко разделить функциональность:
|
||
Сервис GraphQL фокусируется на точных, структурированных запросах
|
||
API векторных представлений обрабатывает семантическую схожесть
|
||
Рабочий процесс пользователя: нечеткий поиск с помощью векторных представлений для поиска кандидатов, затем точный запрос для получения полных данных строки
|
||
|
||
#### Схема запроса/ответа
|
||
|
||
```python
|
||
@dataclass
|
||
class RowEmbeddingsRequest:
|
||
vectors: list[list[float]] # Query vectors (pre-computed embeddings)
|
||
user: str = ""
|
||
collection: str = ""
|
||
schema_name: str = ""
|
||
index_name: str = "" # Optional: filter to specific index
|
||
limit: int = 10 # Max results per vector
|
||
|
||
@dataclass
|
||
class RowIndexMatch:
|
||
index_name: str = "" # The matched index field(s)
|
||
index_value: list[str] = [] # The matched value(s)
|
||
text: str = "" # Original text that was embedded
|
||
score: float = 0.0 # Similarity score
|
||
|
||
@dataclass
|
||
class RowEmbeddingsResponse:
|
||
error: Error | None = None
|
||
matches: list[RowIndexMatch] = []
|
||
```
|
||
|
||
#### Обработчик запросов
|
||
|
||
Модуль: `trustgraph-flow/trustgraph/query/row_embeddings/qdrant`
|
||
|
||
Точка входа: `row-embeddings-query-qdrant`
|
||
|
||
Обработчик:
|
||
1. Получает `RowEmbeddingsRequest` с векторами запросов
|
||
2. Находит соответствующую коллекцию Qdrant путем сопоставления префикса
|
||
3. Ищет ближайшие векторы с необязательным фильтром `index_name`
|
||
4. Возвращает `RowEmbeddingsResponse` с информацией об индексе, соответствующем запросу
|
||
|
||
#### Интеграция с API-шлюзом
|
||
|
||
Шлюз предоставляет запросы на получение векторных представлений строк через стандартный шаблон запроса/ответа:
|
||
|
||
| Компонент | Местоположение |
|
||
|-----------|----------|
|
||
| Диспетчер | `trustgraph-flow/trustgraph/gateway/dispatch/row_embeddings_query.py` |
|
||
| Регистрация | Добавьте `"row-embeddings"` в `request_response_dispatchers` в `manager.py` |
|
||
|
||
Имя интерфейса потока: `row-embeddings`
|
||
|
||
Определение интерфейса в шаблоне потока:
|
||
```json
|
||
{
|
||
"interfaces": {
|
||
"row-embeddings": {
|
||
"request": "non-persistent://tg/request/row-embeddings:{id}",
|
||
"response": "non-persistent://tg/response/row-embeddings:{id}"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
#### Поддержка Python SDK
|
||
|
||
SDK предоставляет методы для запросов векторных представлений строк:
|
||
|
||
```python
|
||
# Flow-scoped query (preferred)
|
||
api = Api(url)
|
||
flow = api.flow().id("default")
|
||
|
||
# Query with text (SDK computes embeddings)
|
||
matches = flow.row_embeddings_query(
|
||
text="Chestnut Street",
|
||
collection="my_collection",
|
||
schema_name="addresses",
|
||
index_name="street_name", # Optional filter
|
||
limit=10
|
||
)
|
||
|
||
# Query with pre-computed vectors
|
||
matches = flow.row_embeddings_query(
|
||
vectors=[[0.1, 0.2, ...]],
|
||
collection="my_collection",
|
||
schema_name="addresses"
|
||
)
|
||
|
||
# Each match contains:
|
||
for match in matches:
|
||
print(match.index_name) # e.g., "street_name"
|
||
print(match.index_value) # e.g., ["CHESTNUT ST"]
|
||
print(match.text) # e.g., "CHESTNUT ST"
|
||
print(match.score) # e.g., 0.95
|
||
```
|
||
|
||
#### Утилита командной строки
|
||
|
||
Команда: `tg-invoke-row-embeddings`
|
||
|
||
```bash
|
||
# Query by text (computes embedding automatically)
|
||
tg-invoke-row-embeddings \
|
||
--text "Chestnut Street" \
|
||
--collection my_collection \
|
||
--schema addresses \
|
||
--index street_name \
|
||
--limit 10
|
||
|
||
# Query by vector file
|
||
tg-invoke-row-embeddings \
|
||
--vectors vectors.json \
|
||
--collection my_collection \
|
||
--schema addresses
|
||
|
||
# Output formats
|
||
tg-invoke-row-embeddings --text "..." --format json
|
||
tg-invoke-row-embeddings --text "..." --format table
|
||
```
|
||
|
||
#### Типичная схема использования
|
||
|
||
Запрос векторных представлений строк обычно используется как часть процесса поиска приблизительного соответствия для точного поиска:
|
||
|
||
```python
|
||
# Step 1: Fuzzy search via embeddings
|
||
matches = flow.row_embeddings_query(
|
||
text="chestnut street",
|
||
collection="geo",
|
||
schema_name="streets"
|
||
)
|
||
|
||
# Step 2: Exact lookup via GraphQL for full row data
|
||
for match in matches:
|
||
query = f'''
|
||
query {{
|
||
streets(where: {{ {match.index_name}: {{ eq: "{match.index_value[0]}" }} }}) {{
|
||
street_name
|
||
city
|
||
zip_code
|
||
}}
|
||
}}
|
||
'''
|
||
rows = flow.rows_query(query, collection="geo")
|
||
```
|
||
|
||
Эта двухэтапная схема позволяет:
|
||
Находить "CHESTNUT ST", когда пользователь ищет "Chestnut Street"
|
||
Получать полные данные строки со всеми полями
|
||
Объединять семантическую схожесть с доступом к структурированным данным
|
||
|
||
### Импорт данных строк
|
||
|
||
Отложено до последующей фазы. Будет разработано вместе с другими изменениями импорта.
|
||
|
||
## Влияние на реализацию
|
||
|
||
### Анализ текущего состояния
|
||
|
||
Существующая реализация имеет два основных компонента:
|
||
|
||
| Компонент | Местоположение | Строк | Описание |
|
||
|-----------|----------|-------|-------------|
|
||
| Сервис запросов | `trustgraph-flow/trustgraph/query/objects/cassandra/service.py` | ~740 | Монолит: генерация схемы GraphQL, разбор фильтров, запросы Cassandra, обработка запросов |
|
||
| Записывающий модуль | `trustgraph-flow/trustgraph/storage/objects/cassandra/write.py` | ~540 | Создание таблиц для каждой схемы, вторичные индексы, вставка/удаление |
|
||
|
||
**Текущая схема запросов:**
|
||
```sql
|
||
SELECT * FROM {keyspace}.o_{schema_name}
|
||
WHERE collection = 'X' AND email = 'foo@bar.com'
|
||
ALLOW FILTERING
|
||
```
|
||
|
||
**Новая схема запроса:**
|
||
```sql
|
||
SELECT * FROM {keyspace}.rows
|
||
WHERE collection = 'X' AND schema_name = 'customers'
|
||
AND index_name = 'email' AND index_value = ['foo@bar.com']
|
||
```
|
||
|
||
### Ключевые изменения
|
||
|
||
1. **Упрощение семантики запросов**: Новая схема поддерживает только точное соответствие для `index_value`. Текущие фильтры GraphQL (`gt`, `lt`, `contains` и т.д.) либо:
|
||
Становятся постобработкой возвращаемых данных (если это все еще необходимо)
|
||
Удаляются в пользу использования API для работы с эмбеддингами для нечеткого сопоставления.
|
||
|
||
2. **Код GraphQL тесно связан**: Текущие сборки `service.py` включают генерацию типов Strawberry, разбор фильтров и запросы, специфичные для Cassandra. Добавление еще одного бэкенда для хранения данных потребует дублирования примерно 400 строк кода GraphQL.
|
||
|
||
### Предлагаемая реструктуризация
|
||
|
||
Реструктуризация состоит из двух частей:
|
||
|
||
#### 1. Выделение кода GraphQL
|
||
|
||
Извлеките многократно используемые компоненты GraphQL в общий модуль:
|
||
|
||
```
|
||
trustgraph-flow/trustgraph/query/graphql/
|
||
├── __init__.py
|
||
├── types.py # Filter types (IntFilter, StringFilter, FloatFilter)
|
||
├── schema.py # Dynamic schema generation from RowSchema
|
||
└── filters.py # Filter parsing utilities
|
||
```
|
||
|
||
Это позволяет:
|
||
Повторное использование в различных системах хранения данных.
|
||
Более четкое разделение ответственности.
|
||
Более простое тестирование логики GraphQL независимо.
|
||
|
||
#### 2. Реализация новой схемы таблицы
|
||
|
||
Переработайте код, специфичный для Cassandra, для использования унифицированной таблицы:
|
||
|
||
**Записывающий модуль** (`trustgraph-flow/trustgraph/storage/rows/cassandra/`):
|
||
Одна `rows` таблица вместо таблиц для каждой схемы.
|
||
Запись N копий на строку (по одной для каждого индекса).
|
||
Регистрация в `row_partitions` таблице.
|
||
Более простое создание таблицы (единоразовая настройка).
|
||
|
||
**Сервис запросов** (`trustgraph-flow/trustgraph/query/rows/cassandra/`):
|
||
Запрос унифицированной `rows` таблицы.
|
||
Использование извлеченного модуля GraphQL для генерации схемы.
|
||
Упрощенная обработка фильтров (только точное соответствие на уровне базы данных).
|
||
|
||
### Переименование модулей
|
||
|
||
В рамках очистки наименований "object" → "row":
|
||
|
||
| Текущее | Новое |
|
||
|---------|-----|
|
||
| `storage/objects/cassandra/` | `storage/rows/cassandra/` |
|
||
| `query/objects/cassandra/` | `query/rows/cassandra/` |
|
||
| `embeddings/object_embeddings/` | `embeddings/row_embeddings/` |
|
||
|
||
### Новые модули
|
||
|
||
| Модуль | Назначение |
|
||
|--------|---------|
|
||
| `trustgraph-flow/trustgraph/query/graphql/` | Общие утилиты GraphQL |
|
||
| `trustgraph-flow/trustgraph/query/row_embeddings/qdrant/` | API запросов для векторных представлений строк |
|
||
| `trustgraph-flow/trustgraph/embeddings/row_embeddings/` | Вычисление векторных представлений строк (Этап 1) |
|
||
| `trustgraph-flow/trustgraph/storage/row_embeddings/qdrant/` | Хранение векторных представлений строк (Этап 2) |
|
||
|
||
## Ссылки
|
||
|
||
[Техническая спецификация структурированных данных](structured-data.md)
|