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, в котором будет лежать сериализованный protobufTValue(как будто у поля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));