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.
21 KiB
| layout | title | parent |
|---|---|---|
| default | Хранение графов знаний, ориентированных на сущности, в Cassandra | Russian (Beta) |
Хранение графов знаний, ориентированных на сущности, в Cassandra
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.
Обзор
Этот документ описывает модель хранения графов знаний в стиле RDF в Apache Cassandra. Модель использует подход, ориентированный на сущности, при котором каждая сущность знает о каждой квадрупле, в которой она участвует, и о роли, которую она играет. Это заменяет традиционный подход с использованием нескольких таблиц и различных перестановок SPO (Subject, Predicate, Object) всего двумя таблицами.
Предыстория и мотивация
Традиционный подход
Стандартное хранилище квадруплов RDF в Cassandra требует нескольких денормализованных таблиц для охвата шаблонов запросов — обычно 6 или более таблиц, представляющих различные перестановки Subject, Predicate, Object и Dataset (SPOD). Каждый квадруплет записывается в каждую таблицу, что приводит к значительному увеличению количества операций записи, оперативным издержкам и сложности схемы.
Кроме того, разрешение меток (получение удобочитаемых имен для сущностей) требует отдельных запросов, что особенно дорого в сценариях использования AI и GraphRAG, где метки необходимы для контекста LLM.
Инсайт, ориентированный на сущности
Каждый квадруплет (D, S, P, O) включает до 4 сущностей. Записывая строку для участия каждой сущности в квадруплете, мы гарантируем, что любой запрос, содержащий хотя бы один известный элемент, попадет в первичный ключ. Это охватывает все 16 шаблонов запросов с помощью одной таблицы данных.
Основные преимущества:
2 таблицы вместо 7+ 4 записи на квадруплет вместо 6+ Разрешение меток бесплатно — метки сущности находятся рядом с ее отношениями, что естественным образом подогревает кэш приложения. Все 16 шаблонов запросов обслуживаются с помощью чтения из одной секции. Более простые операции — одна таблица данных для настройки, уплотнения и восстановления.
Схема
Таблица 1: quads_by_entity
Основная таблица данных. Каждая сущность имеет секцию, содержащую все квадруплеты, в которых она участвует. Названа в соответствии с шаблоном запроса (поиск по сущности).
CREATE TABLE quads_by_entity (
collection text, -- Collection/tenant scope (always specified)
entity text, -- The entity this row is about
role text, -- 'S', 'P', 'O', 'G' — how this entity participates
p text, -- Predicate of the quad
otype text, -- 'U' (URI), 'L' (literal), 'T' (triple/reification)
s text, -- Subject of the quad
o text, -- Object of the quad
d text, -- Dataset/graph of the quad
dtype text, -- XSD datatype (when otype = 'L'), e.g. 'xsd:string'
lang text, -- Language tag (when otype = 'L'), e.g. 'en', 'fr'
PRIMARY KEY ((collection, entity), role, p, otype, s, o, d, dtype, lang)
);
Ключ секции (Partition key): (collection, entity) — ограничен областью действия коллекции, одна секция на сущность.
Обоснование порядка столбцов кластеризации:
- role — большинство запросов начинаются с "где эта сущность является субъектом/объектом"
- p — следующий по частоте фильтр, "верните все
knowsотношения" - otype — позволяет фильтровать по отношениям со значениями URI по сравнению с литеральными значениями
- s, o, d — оставшиеся столбцы для обеспечения уникальности
- dtype, lang — различают литералы с одинаковым значением, но с разными метаданными типа (например,
"thing"vs"thing"@envs"thing"^^xsd:string)
Таблица 2: quads_by_collection
Поддерживает запросы и удаление на уровне коллекции. Предоставляет список всех квадруплетов, принадлежащих коллекции. Название отражает шаблон запроса (поиск по коллекции).
CREATE TABLE quads_by_collection (
collection text,
d text, -- Dataset/graph of the quad
s text, -- Subject of the quad
p text, -- Predicate of the quad
o text, -- Object of the quad
otype text, -- 'U' (URI), 'L' (literal), 'T' (triple/reification)
dtype text, -- XSD datatype (when otype = 'L')
lang text, -- Language tag (when otype = 'L')
PRIMARY KEY (collection, d, s, p, o, otype, dtype, lang)
);
Группировка сначала по набору данных, что позволяет удалять данные как на уровне коллекции, так и на уровне набора данных. Столбцы otype, dtype и lang включены в ключ группировки для различения литералов с одинаковым значением, но с разными метаданными типа — в RDF, "thing", "thing"@en и "thing"^^xsd:string являются семантически различными значениями.
Путь записи
Для каждой входящей квадрупли (D, S, P, O) в коллекции C, записывайте 4 строки в quads_by_entity и 1 строку в quads_by_collection.
Пример
Для квадрупли в коллекции tenant1:
Dataset: https://example.org/graph1
Subject: https://example.org/Alice
Predicate: https://example.org/knows
Object: https://example.org/Bob
Запишите 4 строки в quads_by_entity:
Запишите 1 строку в quads_by_collection:
| collection | d | s | p | o | otype | dtype | lang |
|---|---|---|---|---|---|---|---|
| tenant1 | https://example.org/graph1 | https://example.org/Alice | https://example.org/knows | https://example.org/Bob | U |
Пример с буквальным текстом
Для тройки меток:
Dataset: https://example.org/graph1
Subject: https://example.org/Alice
Predicate: http://www.w3.org/2000/01/rdf-schema#label
Object: "Alice Smith" (lang: en)
otype — это 'L', dtype — это 'xsd:string', а lang — это 'en'. Литеральное значение "Alice Smith" хранится в o. В quads_by_entity требуется всего 3 строки — строка для литерала как сущности не записывается, поскольку литералы не являются независимыми сущностями, к которым можно обращаться.
Шаблоны запросов
Все 16 шаблонов DSPO
В таблице ниже "Идеальный префикс" означает, что запрос использует непрерывный префикс столбцов кластеризации. "Сканирование раздела + фильтрация" означает, что Cassandra считывает часть одного раздела и фильтрует в памяти — это эффективно, но не является чистым совпадением префикса.
| # | Известно | Поиск сущности | Префикс кластеризации | Эффективность |
|---|---|---|---|---|
| 1 | D,S,P,O | entity=S, role='S', p=P | Полное совпадение | Идеальный префикс |
| 2 | D,S,P,? | entity=S, role='S', p=P | Фильтрация по D | Сканирование раздела + фильтрация |
| 3 | D,S,?,O | entity=S, role='S' | Фильтрация по D, O | Сканирование раздела + фильтрация |
| 4 | D,?,P,O | entity=O, role='O', p=P | Фильтрация по D | Сканирование раздела + фильтрация |
| 5 | ?,S,P,O | entity=S, role='S', p=P | Фильтрация по O | Сканирование раздела + фильтрация |
| 6 | D,S,?,? | entity=S, role='S' | Фильтрация по D | Сканирование раздела + фильтрация |
| 7 | D,?,P,? | entity=P, role='P' | Фильтрация по D | Сканирование раздела + фильтрация |
| 8 | D,?,?,O | entity=O, role='O' | Фильтрация по D | Сканирование раздела + фильтрация |
| 9 | ?,S,P,? | entity=S, role='S', p=P | — | Идеальный префикс |
| 10 | ?,S,?,O | entity=S, role='S' | Фильтрация по O | Сканирование раздела + фильтрация |
| 11 | ?,?,P,O | entity=O, role='O', p=P | — | Идеальный префикс |
| 12 | D,?,?,? | entity=D, role='G' | — | Идеальный префикс |
| 13 | ?,S,?,? | entity=S, role='S' | — | Идеальный префикс |
| 14 | ?,?,P,? | entity=P, role='P' | — | Идеальный префикс |
| 15 | ?,?,?,O | entity=O, role='O' | — | Идеальный префикс |
| 16 | ?,?,?,? | — | Полное сканирование | Только исследование |
Ключевой результат: 7 из 15 нетривиальных шаблонов являются идеальными совпадениями префикса кластеризации. Оставшиеся 8 — это чтение одного раздела с фильтрацией внутри раздела. Каждый запрос, содержащий хотя бы один известный элемент, соответствует ключу раздела.
Шаблон 16 (?,?,?,?) не встречается на практике, поскольку коллекция всегда указана, что сводит его к шаблону 12.
Примеры распространенных запросов
Вся информация об сущности:
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice';
Все исходящие связи для сущности:
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S';
Конкретный предикат для сущности:
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S' AND p = 'https://example.org/knows';
Метка для сущности (на конкретном языке):
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S' AND p = 'http://www.w3.org/2000/01/rdf-schema#label'
AND otype = 'L';
Затем, при необходимости, выполните фильтрацию на стороне приложения с использованием lang = 'en'.
Только связи, имеющие значение URI (ссылки между сущностями):
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Alice'
AND role = 'S' AND p = 'https://example.org/knows' AND otype = 'U';
Обратный поиск — что указывает на эту сущность:
SELECT * FROM quads_by_entity
WHERE collection = 'tenant1' AND entity = 'https://example.org/Bob'
AND role = 'O';
Разрешение меток и предварительная загрузка кэша
Одним из наиболее значительных преимуществ модели, ориентированной на сущности, является то, что разрешение меток становится побочным эффектом.
В традиционной многотабличной модели получение меток требует отдельных запросов: извлечение троек, определение URI сущностей в результатах, а затем получение rdfs:label для каждого. Этот шаблон N+1 дорог.
В модели, ориентированной на сущности, запрос сущности возвращает все ее квадруплеты, включая ее метки, типы и другие свойства. Когда приложение кэширует результаты запросов, метки предварительно загружаются в кэш до того, как кто-либо запросит их.
Два режима использования подтверждают, что это хорошо работает на практике:
Запросы, предназначенные для пользователей: обычно небольшие наборы результатов, метки необходимы. Чтение сущностей предварительно загружает кэш. Запросы для ИИ/пакетной обработки: большие наборы результатов с жесткими ограничениями. Метки либо не нужны, либо необходимы только для курированного подмножества сущностей, которые уже находятся в кэше.
Теоретическое опасение по поводу разрешения меток для огромных наборов результатов (например, 30 000 сущностей) нивелируется практическим наблюдением, что ни один пользователь или ИИ не обрабатывает такое большое количество меток. Ограничения на запросы на уровне приложения обеспечивают, чтобы нагрузка на кэш оставалась управляемой.
Широкие разделы и реификация
Реификация (утверждения RDF-star об утверждениях) создает центральные сущности, например, документ-источник, который поддерживает тысячи извлеченных фактов. Это может привести к широким разделам.
Факторы, смягчающие ситуацию:
Ограничения на запросы на уровне приложения: все запросы GraphRAG и предназначенные для пользователей применяют жесткие ограничения, поэтому широкие разделы никогда не сканируются полностью на пути горячего чтения. Cassandra эффективно обрабатывает частичные чтения: сканирование кластерной колонки с ранней остановкой происходит быстро даже на больших разделах. Удаление коллекции (единственная операция, которая может просматривать полные разделы) является приемлемым фоновым процессом.
Удаление коллекции
Запускается по API-вызову, выполняется в фоновом режиме (с конечной согласованностью).
- Чтение
quads_by_collectionдля целевой коллекции, чтобы получить все квадруплеты. - Извлечение уникальных сущностей из квадруплетов (значений s, p, o, d).
- Для каждой уникальной сущности удалите раздел из
quads_by_entity. - Удалите строки из
quads_by_collection.
Таблица quads_by_collection предоставляет индекс, необходимый для поиска всех разделов сущностей без полного сканирования таблицы. Удаление на уровне раздела эффективно, поскольку (collection, entity) является ключом раздела.
Путь миграции из многотабличной модели
Модель, ориентированная на сущности, может сосуществовать с существующей многотабличной моделью во время миграции:
- Разверните таблицы
quads_by_entityиquads_by_collectionрядом с существующими таблицами. - Записывайте новые квадруплеты одновременно в старые и новые таблицы.
- Заполните существующие данные в новые таблицы.
- Мигрируйте пути запросов по одному шаблону за раз.
- Отключите старые таблицы после миграции всех запросов.
Краткое описание
| Аспект | Традиционная (6 таблиц) | Ориентированная на сущности (2 таблицы) |
|---|---|---|
| Таблицы | 7+ | 2 |
| Записи на квадруплет | 6+ | 5 (4 данных + 1 манифест) |
| Разрешение меток | Отдельные запросы | Бесплатно благодаря предварительной загрузке кэша |
| Шаблоны запросов | 16 по 6 таблицам | 16 на 1 таблицу |
| Сложность схемы | Высокая | Низкая |
| Операционные накладные расходы | 6 таблиц для настройки/восстановления | 1 таблица данных |
| Поддержка реификации | Дополнительная сложность | Естественная совместимость |
| Фильтрация по типу объекта | Недоступно | Нативно (через кластеризацию по типу объекта) |