Protobuf-представление таблиц

В данном разделе описано использование протокола передачи структурированных данных protobuf для работы с таблицами в С++ API.

Введение

С++ API позволяет использовать классы (сообщения) protobuf для чтения и записи таблиц как клиентом, так и внутри джоба.

Рекомендуется использовать версию proto2. В случае использования proto3 могут возникнуть ошибки: например, при попытке записать 0 в поле required=%true.

Схема работы

Пользователь описывает proto-структуру в файле .proto. Proto-структура может быть размечена различными флагами. Флаги влияют на то, как YTsaurus будет формировать или интерпретировать поля, указанные внутри сообщений.

Примитивные типы

Для работы с типом данных YTsaurus из первой колонки можно использовать соответствующий protobuf-тип из второй колонки в таблице ниже.

YTsaurus Protobuf
string, utf8 string, bytes
int{8,16,32,64} int{32,64}, sint{32,64}, sfixed{32,64}
uint{8,16,32,64} uint{32,64}, fixed{32,64}
double double, float
bool bool
date, datetime, timestamp uint{32,64}, fixed{32,64}
interval int{32,64}, sint{32,64}, sfixed{32,64}

Если диапазон целочисленного YTsaurus-типа не соответствует диапазону protobuf-типа, выполняется проверка в момент кодирования или декодирования.

Свойства optional / required

Свойства optional / required в protobuf не обязаны соответствовать опциональности колонок в YT. Система всегда делает проверку в момент кодирования или декодирования protobuf-сообщений.

Например, если колонка foo в YTsaurus имеет тип int64 (required=%true), для её представления можно использовать поле:

  optional int64 foo = 42;

Ошибок не будет, пока все protobuf-сообщения, которые записываются в таблицу, имеют заполненное поле foo.

Вложенные сообщения

Исторически все вложенные protobuf-структуры записываются в YTsaurus в виде байтовой строки, хранящей сериализованное представление вложенной структуры.

Вложенные сообщения достаточно эффективны, но не позволяют удобно представлять значения в веб-интерфейсе или работать с ними другими способами (без помощи protobuf).

Этот способ также можно указать явно, выставив специальный флаг в поле:

optional TEmbeddedMessage UrlRow_1 = 1 [(NYT.flags) = SERIALIZATION_PROTOBUF];

Можно указать альтернативный флаг, тогда YTsaurus будет относить поле к типу struct:

optional TEmbeddedMessage ColumnName = 1 [(NYT.flags) = SERIALIZATION_YT];

Для использования вложенных сообщений требуется, чтобы:

  • у таблицы была задана схема;
  • соответствующая колонка (в примере: ColumnName) имела YTsaurus-тип struct;
  • поля типа struct соответствовали полям вложенного сообщения (в примере: TEmbeddedMessage).

Важно

Флаг во вложенных сообщениях не наследуется по умолчанию. Если для поля с типом T выставлен флаг SERIALIZATION_YT, то для структур, вложенных в T, поведение по умолчанию все равно будет соответствовать флагу SERIALIZATION_PROTOBUF.

Повторяющиеся поля

Чтобы работать с повторяющимися (repeated) полями, нужно явно указать флаг SERIALIZATION_YT:

repeated TType ListColumn = 1 [(NYT.flags) = SERIALIZATION_YT];

В YT такое поле будет иметь тип list. Для использования повторяющихся полей требуется, чтобы:

  • у таблицы была задана схема;
  • соответствующая колонка (в примере: ListColumn) имела YTsaurus-тип list;
  • элемент YTsaurus-типа list соответствовал типу колонки protobuf (в примере: TType). Это может быть примитивный тип или вложенное сообщение.

Важно

Флаг SERIALIZATION_PROTOBUF для повторяющихся полей не поддерживается.

Поля oneof

По умолчанию поля внутри oneof-группы соответствуют YTsaurus-типу variant. Например, сообщение ниже будет соответствовать структуре с полями x типа int64 и my_oneof типа variant<y: string, z: bool>:

message TMessage {
    optional int64 x = 1;
    oneof my_oneof {
        string y = 2;
        bool z = 3;
    }
}

При выводе схемы с помощью CreateTableSchema<T>() будет выведен аналогичный тип.

Чтобы группы oneof соответствовали полям структуры, в которой они описаны, используйте флаг (NYT.oneof_flags) = SEPARATE_FIELDS:

message TMessage {
    optional int64 x = 1;
    oneof my_oneof {
        option (NYT.oneof_flags) = SEPARATE_FIELDS;
        string y = 2;
        bool z = 3;
    }
}

Этому сообщению будет соответствовать структура с опциональными полями x, y и z.

Поля map

Существует 4 варианта отображения такого поля в столбец в таблице. См. пример ниже:

message TValue {
    optional int64 x = 1;
}

message TMessage {
    map<string, TValue> map_field = 1 [(NYT.flags) = SERIALIZATION_YT];
}

Поле map_field в зависимости от своих флагов может соответствовать:

  • списку структур с полями key типа string и value типа string, в котором будет лежать сериализованный protobuf TValue (как будто у поля value выставлен флаг SERIALIZATION_PROTOBUF). По умолчанию в этом случае установлен флаг MAP_AS_LIST_OF_STRUCTS_LEGACY.
  • списку структур с полями key типа string и value типа Struct<x: Int64> (как будто у поля value выставлен флаг SERIALIZATION_PROTOBUF). По умолчанию в этом случае установлен флаг MAP_AS_LIST_OF_STRUCTS.
  • словарю Dict<String, Struct<x: Int64>>>: флаг MAP_AS_DICT.
  • опциональному словарю Optional<Dict<String, Struct<x: Int64>>>>: флаг MAP_AS_OPTIONAL_DICT.

Флаги

С помощью флагов можно настроить поведение protobuf. Для этого необходимо подключить библиотечный .proto файл.

import "yt/yt_proto/yt/formats/extension.proto";

Флаги могут соответствовать сообщениям, oneof-группам и полям сообщений.

Указывать флаги можно на уровне .proto файла, сообщения, oneof-группы и поля сообщения.

SERIALIZATION_YT, SERIALIZATION_PROTOBUF

Поведение этих флагов описано выше.

По умолчанию там, где это актуально, подразумевается SERIALIZATION_PROTOBUF. Есть возможность поменять флаг для одного сообщения:

message TMyMessage
{
    option (NYT.default_field_flags) = SERIALIZATION_YT;
    ...
}

OTHER_COLUMNS

Флагом OTHER_COLUMNS можно пометить поле типа bytes. В это поле помещается YSON-мап, содержащий представления всех полей, которые не описываются другими полями этой protobuf-структуры.

message TMyMessage
{
    ...
    optional bytes OtherColumns = 1 [(NYT.flags) = OTHER_COLUMNS];
    ...
}

ENUM_STRING / ENUM_INT

Флагами ENUM_STRING / ENUM_INT можно помечать поля типа enum:

  • Если поле помечено ENUM_INT, то оно будет сохраняться в колонку в виде целого числа.
  • Если поле помечено ENUM_STRING, то оно будет сохраняться в колонку в виде строки.

По умолчанию подразумевается ENUM_STRING.

enum Color
{
    WHITE = 0;
    BLUE = 1;
    RED = -1;
}

...
optional Color ColorField = 1 [(NYT.flags) = ENUM_INT];
...

ANY

Флагом ANY можно помечать поля типа bytes. Такие поля содержат YSON-представление колонки любого простого типа.
Например, для колонки типа string можно написать следующий код:

// message.proto
message TMyMessage
{
    ...
    optional bytes AnyColumn = 1 [(NYT.flags) = ANY];
    ...
}
// main.cpp
TNode node = "Linnaeus";
TMyMessage m;
m.SetAnyColumn(NodeToYsonString(node));
Предыдущая