TDG Documentation portal logo
Помощь
Обновлена 22 июня 2026 г. в 15:31

Модель данных

В этой главе подробно описана структура модели данных TDG, а также перечислены доступные способы работы с ней - в файле и в веб-интерфейсе. В главе рассмотрен пример создания модели данных и её загрузки в TDG. Для выполнения примера требуется настроенный кластер TDG.

Язык модели данных

Модель данных включает в себя:

  • структуру объектов для заданной предметной области;
  • описание связей между объектами;
  • ограничения, которые накладываются на объекты.

В TDG для описания модели данных используются язык Avro Schema, а также расширения, разработанные специально для системы TDG.

Схема в стандарте Avro Schema - это JSON-файл с расширением .avsc, содержащий описание типов данных для объектов и для их полей. Приложение использует схему в качестве формата и понимает ее, как массив типов объектов:

[    {"name": "TypeA", "type": "record", ...},    {"name": "TypeB", "type": "record", ...}]

Все типы объектов соответствуют стандарту Avro Schema, за исключением расширений для системы TDG. Расширения обратно совместимы, а модель, описанная с их помощью, успешно преобразуется стандартными парсерами (синтаксическими анализаторами).

Определение модели данных

Для начала зададим модель данных с двумя типами объектов - Country (страна) и City (город). В качестве примера возьмем следующую диаграмму объектов:

image

Теперь представим модель с диаграммы в виде схемы данных Avro:

[    {        "name": "Country",        "type": "record",        "doc": "Страна",        "fields": [            {"name": "title", "type": "string"},            {"name": "phone_code", "type": ["null", "string"]}        ],        "indexes": ["title"],        "relations": [            { "name": "city_relation", "to": "City", "count": "many", "from_fields": "title", "to_fields": "country" }        ]    },    {        "name": "City",        "type": "record",        "doc": "Город",        "fields": [            {"name": "title", "type": "string"},            {"name": "country", "type": "string"},            {"name": "population", "type": "int"},            {"name": "capital", "type": "boolean"},            {"name": "postcodes", "type": {"type":"array", "items":"int"}}        ],        "indexes": [            {"name":"primary", "parts":["title", "country"]},            "title",            "country",            "population",            "postcodes"        ]    }]

где

  • name - название типа объекта;
  • type - вид схемы;
  • doc - текстовое описание для типа объекта;
  • fields - поля для типа объекта вместе с соответствующими им типами данных.
    • name - название поля;

    • type - тип данных для поля. Поддерживаются типы из спецификации Avro Schema, а также логические типы данных.

      Если в модели данных есть опциональное поле, его тип данных описывают с помощью объединяющего массива union. Такой массив содержит основной тип данных для этого поля и тип null. Пример:

      {"name": "phone_code", "type": ["null", "string"]}
  • indexes - индексы, которые используются при выполнении операций. Первый по счету индекс в списке является первичным. Поле indexes - расширение TDG для задания ключей;
  • relations - вид связи между объектами. В этой модели данных типы объектов Country и City имеют связь один ко многим, так как в одной стране обычно расположено много городов. Поле relations - расширение TDG для задания отношений между объектами.

Описания всех доступных расширений TDG приведены в разделе Расширения модели данных.

Загрузка модели данных

Чтобы применить модель данных и делать ссылка, нужно загрузить модель данных в TDG. Основные способы загрузки данных:

  • через веб-интерфейс на вкладке Model;
  • в файле .avsc, который будет запакован в архив вместе с файлом конфигурации.

Загрузка через веб-интерфейс

Чтобы загрузить модель данных через веб-интерфейс TDG, выполните следующие шаги:

  1. Откройте веб-интерфейс на экземпляре, входящем в набор реплик с кластерной ролью runner. Если вы используете уже развернутый TDG-кластер, URL экземпляра будет следующий: http://172.19.0.2:8082.
  2. В веб-интерфейсе выберите вкладку Model.
  3. Вставьте созданную модель данных в поле Request.
  4. Нажмите Submit. Если модель данных загружена успешно, в окне Response появится ответ OK.

Загрузка в составе zip-архива

Загрузить модель данных можно также в составе zip-архива вместе с файлом конфигурации. Для этого:

  1. Упакуйте в zip-архив:
    • модель данных в файле с расширением .avsc, например, model.avsc;
    • файл конфигурации config.yml.
  2. Загрузите архив в TDG согласно инструкции.

Расширения модели данных

Расширения для системы TDG дополняют спецификацию Avro Schema. Эти расширения позволяют:

Задание отношений между объектами

Явно задавать связи между объектами нужно:

  • для валидации внешних ключей при вставке объектов;
  • для запросов связанных объектов через GraphQL.

Чтобы указать такую связь, используется поле relations в теле описания типа объекта. Это поле игнорируется существующими парсерами Avro Schema. Связываемые поля (внешние ключи в терминологии SQL) должны быть объявлены на уровне типов объектов.

Например, чтобы задать связь между страной и ее городами, требуются следующие поля:

  • title (Country) - первичный ключ доступен через обращение к типу объекта Country.title;
  • country (City) - внешний ключ доступен через обращение к типу объекта City.country.

Пример поля relations из заданной ранее модели данных:

{    "relations": [        {            "name": "city_relation",            "to": "City",            "count": "many",            "from_fields": "title",            "to_fields": "country"        },        ...    ]}

где:

  • name - название поля, через которое можно получить связанные объекты в GraphQL-запросах;
  • to - тип объекта, с которым устанавливается связь;
  • count - вид связи. Возможные значения:
    • one - связь один к одному;
    • many - связь один ко многим;
  • from_fields - название поля или индекса, содержащего первичный ключ. Возможные значения:
    • название индекса ("from_fields": "title");
    • название поля ("from_fields": "field_name");
    • список названий полей ("from_fields": ["field_name1", "field_name2"]);
  • to_fields - название поля или индекса, содержащего внешний ключ. Возможные значения:
    • название индекса ("to_fields": "country");
    • название поля ("to_fields": "field_name");
    • список названий полей ("to_fields": ["field_name1", "field_name2"]).

Все параметры поля обязательные.

Поле relations можно задать как с одной стороны отношения, так и с обеих. Поле указывается только в одном типе объекта, когда запрашивать данные в обратном направлении не требуется.

Дополним модель данных еще одним полем relations (City), чтобы можно было запрашивать данные в обоих направлениях. Полный пример может выглядеть так:

[    {        "name": "Country",        "type": "record",        "doc": "Страна",        "fields": [            {"name": "title", "type": "string"},            {"name": "phone_code", "type": ["null", "string"]}        ],        "indexes": ["title"],        "relations": [            {"name": "city_relation", "to": "City", "count": "many", "from_fields": "title", "to_fields": "country"}        ]    },    {        "name": "City",        "type": "record",        "doc": "Город",        "fields": [            {"name": "title", "type": "string"},            {"name": "country", "type": "string"},            {"name": "population", "type": "int"},            {"name": "capital", "type": "boolean"},            {"name": "postcodes", "type": {"type":"array", "items":"int"}}        ],        "indexes": [            {"name":"primary", "parts":["title", "country"]},            "title",            "country",            "population",            "postcodes"        ],        "relations": [            {"name": "country_relation", "to": "Country", "count": "one", "from_fields": "country", "to_fields": "title"}        ]    }]

Задание индексов (ключей)

Для задания индексов используется поле indexes в описании типа объекта. Поле не меняет поведение, регулируемое стандартом Avro Schema, а добавляет дополнительные ограничения на хранение и запросы. Если для типа объекта указаны индексы, TDG создает спейсы под этот тип. Индексы по полю с типом bytes строятся так же, как и по полю со строковым типом.

Синтаксис поля indexes:

{    "indexes": ["<index1>", <"index2">, <"index3">, ...]}

Каждый индекс в поле indexes может быть задан в виде:

  • строки с названием поля, по которому будет построен индекс ("indexes": ["title"]);

  • словаря, на основе которого строятся составной индекс или индекс по полю вложенного объекта. Такой индекс указывается в следующем формате:

    {    "name": "<index_name>",    "parts": [        {"path": "<field_name>", "collation": "<collation_type>"},        {"path": "<field_name>", "collation": "<collation_type>"},        ...    ]}

    где:

    • name - название индекса, не совпадающее с именами существующих полей;
    • parts - части индекса. Включает в себя:
      • path - названия полей, по которым строится индекс;
      • collation (опционально) - способ сравнения строк. Возможные значения:
        • binary (по умолчанию) - бинарное сравнение ('A' < 'B' < 'a');
        • case_sensitivity - сравнение, зависящее от регистра ('a' < 'A' < 'B');
        • case_insensitivity - сравнение, не зависящее от регистра (('a' = 'A') < 'B' и 'a' = 'A' = 'á' = 'Á').

Пример составного индекса

{    "name": "City",    "type": "record",    "fields": [        {"name": "title", "type": "string"},        {"name": "country", "type": "string"},        {"name": "population", "type": "int"},        {"name": "capital", "type": "boolean"},        {"name": "postcodes", "type": {"type":"array", "items":"int"}}    ],    "indexes": [        {"name":"primary", "parts":["title", "country"]},        "title",        "country",        "population",        "postcodes"    ]}

Пример индексации по полю вложенного объекта

Иногда требуется построить индекс по полю не из самого объекта, а из его вложенного объекта. Вложенный объект при этом создается, чтобы сгруппировать логически набор стандартных полей.

Например, представьте, что каждая страна (Country) в примере содержит дополнительный блок информации для туристов. Создадим новый тип объекта Info и сложный индекс в типе объекта Country. Тип объекта Info включается здесь непосредственно в тип объекта Country, поэтому индекс может сослаться на поле из Info:

[    {        "name": "Info",        "type": "record",        "doc": "Информация для туристов",        "fields": [            {"name": "id", "type": "long"},            {"name": "info_text", "type": "string"}        ]    },    {        "name": "Country",        "type": "record",        "doc": "Страна",        "fields": [            {"name": "title", "type": "string"},            {"name": "info", "type": "Info"},            {"name": "phone_code", "type": ["null", "string"]}        ],        "indexes": [            "title",            {"name": "info_id", "parts": ["info.id"]}        ]    }]

Распределение объектов по хранилищам

В случае распределенного хранилища данных объекты распределяются с использованием хеш-функции от первичного ключа объекта. Указать такой индекс можно в поле affinity.

Пример

Перед загрузкой модели с новым полем affinity удалите старую модель данных. Кроме того, проверьте список спейсов во вкладке Settings > Unlinked spaces в веб-интерфейсе TDG. Если в списке есть спейсы с типом объекта City, удалите их, чтобы избежать ошибки при загрузке новой модели.

Теперь дополните модель данных новым полем и загрузите модель в TDG:

{    "name": "City",    "type": "record",    "doc": "Город",    "fields": [        {"name": "title", "type": "string"},        {"name": "country", "type": "string"},        {"name": "population", "type": "int"},        {"name": "capital", "type": "boolean"},        {"name": "postcodes", "type": {"type":"array", "items":"int"}}    ],    "indexes": [        {"name":"primary", "parts":["title", "country"]},        "title",        "country",        "population",        "postcodes"    ],    "affinity": ["country"]}

В примере города одной и той же страны размещены физически на одном хранилище. Поле affinity при этом содержит индекс из составного первичного ключа.

Атрибуты полей

Расширение для TDG дополняет список стандартных атрибутов полей Avro Schema:

  • default_function (function) - задает для поля динамическое значение по умолчанию. Значение атрибута - это функция, файл с которой хранится в директории src в корне проекта. При вставке в спейс новой записи вызывается указанная функция, и результат функции становится значением для данного поля. Атрибут не стоит путать со стандартным атрибутом default, который задает для поля статическое значение;

  • auto_increment (boolean) - значение true делает поле автоинкрементным. По умолчанию: false. Может использоваться в качестве числового идентификатора, уникального для объектов данного типа, даже при шардировании базы данных. Автоинкрементное поле обязательно должно иметь тип long. Атрибут несовместим с атрибутами default и default_function;

  • api-encoding (string) - задает способ кодирования и декодирования бинарных данных в API HTTP и GraphQL. Доступно с версии 2.19.0. Опция поддерживается только для полей типа string или bytes, по умолчанию опция отключена. Поддерживаемые кодировки: base64, hex. Принимает на вход один из вариантов ниже:

    • одну строку кодировки, которая применяется сразу к двум интерфейсам
      • HTTP и GraphQL;
    • таблицу с отдельным значением для каждого интерфейса (http, graphql).
    {  "name": "Payload",  "type": "record",  "fields": [    {"name": "id",   "type": "string"},    {"name": "data", "type": "bytes", "api-encoding": "base64"},    {"name": "hash", "type": "string", "api-encoding": {"http": "hex", "graphql": "base64"}}  ]}

Пример

Расширьте тип объекта City этими атрибутами:

  • задайте по умолчанию для поля population динамическое значение;
  • добавьте поле с числовым идентификатором города.

Обновленные поля объекта могут выглядеть так:

{    "name": "City",    "type": "record",    "fields": [        {"name": "city_id", "type": "long", "auto_increment": true},        {"name": "title", "type": "string"},        {"name": "country", "type": "string"},        {"name": "population", "type": "int", "default" : "count_population.call"}},        ...    ],    ...}

Логические типы данных

Логический тип - это простой или составной тип данных Avro, содержащий дополнительные атрибуты для представления нового типа данных для поля. Чтобы объявить в модели поле с логическим типом, используется атрибут logicalType. Поля этих типов можно использовать при построении любых индексов, кроме мультиключевых. По умолчанию для логических типов задается строковый тип данных.

TDG поддерживает следующие логические типы:

  • Date - дата. Хранится в виде int. Формат записи: YYYY-MM-DDZ;

  • Time - время. Хранится в виде int. Формат записи: HH:MM:SSZ;

  • Datetime - дата и время. Хранится в виде int. Форматы записи:

    • дата и время с миллисекундами: YYYY-MM-DDTHH:MM:SS.sssZ;
    • дата и время с микросекундами: YYYY-MM-DDTHH:MM:SS.ssssssZ;
    • дата и время с наносекундами: YYYY-MM-DDTHH:MM:SS.sssssssssZ.
  • Timestamp - тип для работы с датой и временем на основе Tarantool-модуля datetime;

  • Decimal - вычисления с точными числами. Пример записи: 10.001. Тип использует Tarantool-модуль decimal;

  • UUID - уникальный идентификатор. Формат записи: 00000000-0000-0000-0000-000000000000. Тип использует Tarantool-модуль uuid.

Примеры объявления полей с логическими типами:

[    {        "name": "LogicalTypes",        "type": "record",        "fields": [            {"name": "id", "type": {"type": "string", "logicalType": "Timestamp"}},            {"name": "datetime", "type": ["null", {"type": "string", "logicalType": "DateTime"}]},            {"name": "time", "type": ["null", {"type": "string", "logicalType": "Time"}]},            {"name": "date", "type": ["null", {"type": "string", "logicalType": "Date"}]},            {"name": "decimal", "type": ["null", {"type": "string", "logicalType": "Decimal"}]},            {"name": "uuid", "type": ["null", {"type": "string", "logicalType": "UUID"}]}        ],    "indexes": ["id", "datetime", "time", "date", "decimal", "uuid"]    }]