Protobuf sources
Below you will find the Protocol Buffers definitions for the Jelly-RDF, Jelly-Patch, and Jelly-SPARQL formats, along with the Jelly gRPC streaming protocol. The original files are hosted on GitHub and all releases can be found here.
Human-readable reference for these definitions can be found here.
The following code is licensed under the Apache License, Version 2.0.
rdf.proto
syntax = "proto3";
package eu.ostrzyciel.jelly.core.proto.v1;
// Jelly RDF serialization with Protocol Buffers.
// Specification document:
// https://w3id.org/jelly/1.1.1/specification/serialization
// Protocol version: 1.1.1
// RDF IRIs
// The IRIs are reconstructed by the consumer using the prefix and name
// lookup tables.
message RdfIri {
// 1-based, refers to an entry in the prefix lookup.
//
// 0 signifies "use the same prefix_id as in the previous IRI".
// For this to work, IRIs must be processed strictly in order: firstly by
// stream row, then by term (subject, predicate, object, graph). This also
// applies recursively to RDF-star quoted triples.
//
// If 0 appears in the first IRI of the stream (and in any subsequent IRI),
// this should be interpreted as an empty ("") prefix. This is for example
// used when the prefix lookup table is disabled.
uint32 prefix_id = 1;
// 1-based, refers to an entry in the name lookup.
//
// 0 signifies "use the previous name_id + 1". This requires the same order
// guarantees as prefixes.
//
// If 0 appears in the first IRI of the stream, it should be interpreted as
// name_id = 1.
uint32 name_id = 2;
}
// RDF literals
message RdfLiteral {
// The lexical form of the literal (required).
string lex = 1;
// Literal kind – at most one of these field may be set.
// If none is set, then it's a simple literal.
oneof literalKind {
// Language-tagged string.
string langtag = 2;
// Typed literal. The datatype is a reference to an entry in the
// datatype lookup. This value is 1-based and the value of 0
// is invalid (in contrast to prefix_id and name_id in RdfIri).
uint32 datatype = 3;
}
}
// Empty message indicating the default RDF graph.
message RdfDefaultGraph {}
// RDF triple
//
// For each term (subject, predicate, object), the fields are repeated for
// performance reasons. This is to avoid the need for boxing each term in a
// separate message.
//
// Note: this message allows for representing generalized RDF triples (for
// example, with literals as predicates). Whether this is used in the stream
// is determined by the stream options (see RdfStreamOptions).
//
// If no field in a given oneof is set, the term is interpreted as a repeated
// term – the same as the term in the same position in the previous triple.
// In the first triple of the stream, all terms must be set.
// All terms must also be set in quoted triples (RDF-star).
message RdfTriple {
// Triple subject
oneof subject {
// IRI
RdfIri s_iri = 1;
// Blank node
string s_bnode = 2;
// Literal
// Only valid in a generalized RDF stream.
RdfLiteral s_literal = 3;
// RDF-star quoted triple
RdfTriple s_triple_term = 4;
}
// Triple predicate
oneof predicate {
// IRI
RdfIri p_iri = 5;
// Blank node
// Only valid in a generalized RDF stream.
string p_bnode = 6;
// Literal
// Only valid in a generalized RDF stream.
RdfLiteral p_literal = 7;
// RDF-star quoted triple
RdfTriple p_triple_term = 8;
}
// Triple object
oneof object {
// IRI
RdfIri o_iri = 9;
// Blank node
string o_bnode = 10;
// Literal
RdfLiteral o_literal = 11;
// RDF-star quoted triple
RdfTriple o_triple_term = 12;
}
}
// RDF quad
//
// Fields 1–12 are repeated from RdfTriple for performance reasons.
//
// Similarly to RdfTriple, this message allows for representing generalized
// RDF quads (for example, with literals as predicates). Whether this is used
// in the stream is determined by the stream options (see RdfStreamOptions).
//
// If no field in a given oneof is set, the term is interpreted as a repeated
// term – the same as the term in the same position in the previous quad.
// In the first quad of the stream, all terms must be set.
message RdfQuad {
// Quad subject
oneof subject {
// IRI
RdfIri s_iri = 1;
// Blank node
string s_bnode = 2;
// Literal
// Only valid in a generalized RDF stream.
RdfLiteral s_literal = 3;
// RDF-star quoted triple
RdfTriple s_triple_term = 4;
}
// Quad predicate
oneof predicate {
// IRI
RdfIri p_iri = 5;
// Blank node
// Only valid in a generalized RDF stream.
string p_bnode = 6;
// Literal
// Only valid in a generalized RDF stream.
RdfLiteral p_literal = 7;
// RDF-star quoted triple
RdfTriple p_triple_term = 8;
}
// Quad object
oneof object {
// IRI
RdfIri o_iri = 9;
// Blank node
string o_bnode = 10;
// Literal
RdfLiteral o_literal = 11;
// RDF-star quoted triple
RdfTriple o_triple_term = 12;
}
// Quad graph
oneof graph {
// IRI
RdfIri g_iri = 13;
// Blank node
string g_bnode = 14;
// Default graph
RdfDefaultGraph g_default_graph = 15;
// Literal – only valid for generalized RDF streams
RdfLiteral g_literal = 16;
}
}
// Start of a graph in a GRAPHS stream
//
// In contrast to RdfQuad, setting the graph oneof to some value
// is always required. No repeated terms are allowed.
message RdfGraphStart {
oneof graph {
// IRI
RdfIri g_iri = 1;
// Blank node
string g_bnode = 2;
// Default graph
RdfDefaultGraph g_default_graph = 3;
// Literal – only valid for generalized RDF streams
RdfLiteral g_literal = 4;
}
}
// End of a graph in a GRAPHS stream
message RdfGraphEnd {}
// Explicit namespace declaration
//
// This does not correspond to any construct in the RDF Abstract Syntax.
// Rather, it is a hint to the consumer that the given IRI prefix (namespace)
// may be associated with a shorter name, like in Turtle syntax:
// PREFIX ex: <http://example.org/>
//
// These short names (here "ex:") are NOT used in the RDF statement encoding.
// This is a purely cosmetic feature useful in cases where you want to
// preserve the namespace declarations from the original RDF document.
// These declarations have nothing in common with the prefix lookup table.
message RdfNamespaceDeclaration {
// Short name of the namespace (e.g., "ex")
// Do NOT include the colon.
string name = 1;
// IRI of the namespace (e.g., "http://example.org/")
RdfIri value = 2;
}
// Entry in the name lookup table
message RdfNameEntry {
// 1-based identifier
// If id=0, it should be interpreted as previous_id + 1.
// If id=0 appears in the first RdfNameEntry of the stream, it should be
// interpreted as 1.
uint32 id = 1;
// Value of the name (UTF-8 encoded)
string value = 2;
}
// Entry in the prefix lookup table
//
// Note: the prefixes in the lookup table can be arbitrary strings, and are
// NOT meant to be user-facing. They are only used for IRI compression.
// To transmit user-facing namespace declarations for cosmetic purposes, use
// RdfNamespaceDeclaration.
message RdfPrefixEntry {
// 1-based identifier
// If id=0, it should be interpreted as previous_id + 1.
// If id=0 appears in the first RdfPrefixEntry of the stream, it should be
// interpreted as 1.
uint32 id = 1;
// Value of the prefix (UTF-8 encoded)
string value = 2;
}
// Entry in the datatype lookup table
message RdfDatatypeEntry {
// 1-based identifier
// If id=0, it should be interpreted as previous_id + 1.
// If id=0 appears in the first RdfDatatypeEntry of the stream, it should be
// interpreted as 1.
uint32 id = 1;
// Value of the datatype (UTF-8 encoded)
string value = 2;
}
// RDF stream options
message RdfStreamOptions {
// Name of the stream (completely optional).
// This may be used for, e.g., topic names in a pub/sub system.
string stream_name = 1;
// Type of the stream (required)
PhysicalStreamType physical_type = 2;
// Whether the stream may contain generalized triples, quads, or datasets
bool generalized_statements = 3;
// Whether the stream may contain RDF-star statements
bool rdf_star = 4;
// Maximum size of the name lookup table
// (required, must be >= 8)
uint32 max_name_table_size = 9;
// Maximum size of the prefix lookup table
// (required if the prefix lookup is used)
uint32 max_prefix_table_size = 10;
// Maximum size of the datatype lookup table
// (required if datatype literals are used)
uint32 max_datatype_table_size = 11;
// Logical (RDF-STaX-based) stream type
// In contrast to the physical type, this field is entirely optional.
LogicalStreamType logical_type = 14;
// Protocol version (required)
// For Jelly 1.0.x value must be 1.
// For Jelly 1.1.x value must be 2.
// For custom extensions, the value must be 10000 or higher.
uint32 version = 15;
}
// Physical stream type
// This determines how the data is encoded in the stream, not the logical
// structure of the data. See LogicalStreamType for the latter.
enum PhysicalStreamType {
// Unspecified stream type – invalid
PHYSICAL_STREAM_TYPE_UNSPECIFIED = 0;
// RDF triples
PHYSICAL_STREAM_TYPE_TRIPLES = 1;
// RDF quads
PHYSICAL_STREAM_TYPE_QUADS = 2;
// RDF triples grouped in graphs
PHYSICAL_STREAM_TYPE_GRAPHS = 3;
}
// Logical stream type, according to the RDF Stream Taxonomy (RDF-STaX).
// Type 0 is reserved for the unspecified stream type.
// The rest of the type numbers follow the taxonomical structure of RDF-STaX.
// For example: 1 is a subtype of 0, 13 and 23 are subtypes of 3,
// 114 is a subtype of 14, etc.
//
// Types 1–4 correspond to the four base concrete stream types. Their
// subtypes can be in most cases simply processed in the same way as
// the base types.
// Therefore, implementations can take the modulo 10 of the stream
// type to determine the base type of the stream and use this information
// to select the appropriate processing logic.
//
// RDF-STaX version: 1.1.2
// https://w3id.org/stax/1.1.2
//
// ^ The above URL is used to automatically determine the version of RDF-STaX
// in the Jelly protocol specification. Please keep it up-to-date and in the
// same format.
enum LogicalStreamType {
// Unspecified stream type – invalid
LOGICAL_STREAM_TYPE_UNSPECIFIED = 0;
// Flat RDF triple stream
// https://w3id.org/stax/ontology#flatTripleStream
LOGICAL_STREAM_TYPE_FLAT_TRIPLES = 1;
// Flat RDF quad stream
// https://w3id.org/stax/ontology#flatQuadStream
LOGICAL_STREAM_TYPE_FLAT_QUADS = 2;
// RDF graph stream
// https://w3id.org/stax/ontology#graphStream
LOGICAL_STREAM_TYPE_GRAPHS = 3;
// RDF dataset stream
// https://w3id.org/stax/ontology#datasetStream
LOGICAL_STREAM_TYPE_DATASETS = 4;
// RDF subject graph stream (subtype of RDF graph stream)
// https://w3id.org/stax/ontology#subjectGraphStream
LOGICAL_STREAM_TYPE_SUBJECT_GRAPHS = 13;
// RDF named graph stream (subtype of RDF dataset stream)
// https://w3id.org/stax/ontology#namedGraphStream
LOGICAL_STREAM_TYPE_NAMED_GRAPHS = 14;
// RDF timestamped named graph stream (subtype of RDF dataset stream)
// https://w3id.org/stax/ontology#timestampedNamedGraphStream
LOGICAL_STREAM_TYPE_TIMESTAMPED_NAMED_GRAPHS = 114;
}
// RDF stream row
message RdfStreamRow {
// Exactly one of these fields must be set.
oneof row {
// Stream options. Must occur at the start of the stream.
RdfStreamOptions options = 1;
// RDF triple statement.
// Valid in streams of physical type TRIPLES or GRAPHS.
RdfTriple triple = 2;
// RDF quad statement.
// Only valid in streams of physical type QUADS.
RdfQuad quad = 3;
// Graph boundary: ends the currently transmitted graph and starts a new one
// Only valid in streams of physical type GRAPHS.
RdfGraphStart graph_start = 4;
// Explicit end of a graph.
// Signals the consumer that the transmitted graph is complete.
// Only valid in streams of physical type GRAPHS.
RdfGraphEnd graph_end = 5;
// Explicit namespace declaration.
RdfNamespaceDeclaration namespace = 6;
// Entry in the name lookup table.
RdfNameEntry name = 9;
// Entry in the prefix lookup table.
RdfPrefixEntry prefix = 10;
// Entry in the datatype lookup table.
RdfDatatypeEntry datatype = 11;
}
}
// RDF stream frame – base message for RDF streams.
message RdfStreamFrame {
// Stream rows
repeated RdfStreamRow rows = 1;
// Arbitrary metadata
// The keys are UTF-8 encoded strings, the values are byte arrays.
// This may be used by implementations in any way they see fit.
// The metadata does not affect the RDF data in any way, treat it
// as comments in a text file.
map<string, bytes> metadata = 15;
}
patch.proto
syntax = "proto3";
package eu.ostrzyciel.jelly.core.proto.v1.patch;
// EXPERIMENTAL FEATURE -- the API and format may change.
// RDF Patch implementation based on Jelly
// RDF Patch spec: https://afs.github.io/rdf-delta/rdf-patch.html
// Jelly-Patch spec: https://w3id.org/jelly/1.1.0/specification/patch
import "rdf.proto";
message RdfPatchTransactionStart {}
message RdfPatchTransactionCommit {}
message RdfPatchTransactionAbort {}
message RdfPatchNamespace {
// Short name of the namespace (e.g., "ex")
// Do NOT include the colon.
string name = 1;
// IRI of the namespace (e.g., "http://example.org/").
// Optional in the "prefix delete" (PD) operation,
// but required in the "prefix add" (PA) operation.
RdfIri value = 2;
// Graph name for the namespace declaration.
//
// This is required for the statement type QUADS, and disallowed for
// the statement type TRIPLES.
//
// This is used to achieve full 1:1 replication in systems that associate
// namespaces with specific graphs (e.g., Apache Jena).
//
// Repeated term compression IS supported for this field.
// The repeated terms are shared between the namespace and statement rows.
oneof graph {
// IRI
RdfIri g_iri = 3;
// Blank node
string g_bnode = 4;
// Default graph
RdfDefaultGraph g_default_graph = 5;
// Literal – only valid for generalized RDF streams
RdfLiteral g_literal = 6;
}
}
// Metadata header for RDF Patch.
// Header rows must occur at the start of the patch frame.
message RdfPatchHeader {
// Header key (required)
string key = 1;
// Header value (required)
// Repeated term compression is not supported for header values.
// This oneof must always be set to a valid RDF term.
oneof value {
// IRI
RdfIri h_iri = 2;
// Blank node
string h_bnode = 3;
// Literal
RdfLiteral h_literal = 4;
// RDF-star quoted triple
RdfTriple h_triple_term = 5;
}
}
message RdfPatchPunctuation {}
// RDF Patch stream options
message RdfPatchOptions {
// Type of statements in the stream (required)
PatchStatementType statement_type = 1;
// Type of the stream (required)
PatchStreamType stream_type = 2;
// Whether the stream may contain generalized triples, quads, or datasets
bool generalized_statements = 3;
// Whether the stream may contain RDF-star statements
bool rdf_star = 4;
// Maximum size of the name lookup table
// (required, must be >= 8)
uint32 max_name_table_size = 9;
// Maximum size of the prefix lookup table
// (required if the prefix lookup is used)
uint32 max_prefix_table_size = 10;
// Maximum size of the datatype lookup table
// (required if datatype literals are used)
uint32 max_datatype_table_size = 11;
// Protocol version (required)
// For Jelly-Patch 1.0.x (based on Jelly-RDF 1.1.x), the value must be 1.
// For custom extensions, the value must be 10000 or higher.
uint32 version = 15;
}
// Type of statements in the stream.
// This determines whether the statements are interpreted as triples or quads.
enum PatchStatementType {
// Unspecified stream type – invalid
PATCH_STATEMENT_TYPE_UNSPECIFIED = 0;
// RDF triples
PATCH_STATEMENT_TYPE_TRIPLES = 1;
// RDF quads
PATCH_STATEMENT_TYPE_QUADS = 2;
}
// Type of the stream.
// This determines whether each RdfPatchFrame is a complete patch by itself,
// or part of a larger patch.
enum PatchStreamType {
// Unspecified stream type – invalid
PATCH_STREAM_TYPE_UNSPECIFIED = 0;
// Each RdfPatchFrame is a complete patch.
// In this case, transactions cannot span multiple frames.
PATCH_STREAM_TYPE_FRAME = 1;
// Entire stream is a single patch.
// Transactions may span multiple frames.
PATCH_STREAM_TYPE_FLAT = 2;
// Stream contains multiple patches separated by punctuation marks.
// Patches and transactions may span multiple frames.
// Each frame must contain at most one patch.
PATCH_STREAM_TYPE_PUNCTUATED = 3;
}
// A row in an RDF Patch stream
message RdfPatchRow {
oneof row {
// Stream options. Must be the first row in the stream.
RdfPatchOptions options = 1;
// Add a triple/quad statement (A)
RdfQuad statement_add = 2;
// Delete a triple/quad statement (D)
RdfQuad statement_delete = 3;
// Add a namespace/prefix declaration (PA)
RdfPatchNamespace namespace_add = 4;
// Delete a namespace/prefix declaration (PD)
RdfPatchNamespace namespace_delete = 5;
// Transaction start (TX)
RdfPatchTransactionStart transaction_start = 6;
// Transaction commit (TC)
RdfPatchTransactionCommit transaction_commit = 7;
// Transaction abort (TA)
RdfPatchTransactionAbort transaction_abort = 8;
// Entry in the name lookup table.
RdfNameEntry name = 11;
// Entry in the prefix lookup table.
RdfPrefixEntry prefix = 12;
// Entry in the datatype lookup table.
RdfDatatypeEntry datatype = 13;
// Header entry (H)
RdfPatchHeader header = 14;
// Punctuation mark, only used in stream type PUNCTUATED
// Indicates the end of a patch.
RdfPatchPunctuation punctuation = 15;
}
}
// A batch of RDF patch rows.
message RdfPatchFrame {
// Patch rows
repeated RdfPatchRow rows = 1;
}
rdf2.proto
syntax = "proto3";
package eu.ostrzyciel.jelly.core.proto.v1;
// EXPERIMENTAL FEATURE -- the API and format may change.
// Messages shared by the newer Jelly formats, in preparation for the RDF 1.2
// version of Jelly.
import "rdf.proto";
// Packed entries of a lookup table (name, prefix, or datatype).
//
// Lookup entries are almost always assigned consecutive identifiers, because
// that is what the "id = 0 means previous_id + 1" rule of the unpacked entries
// optimizes for. A packed entry states the identifier once and then lists the
// values of a whole run of consecutive entries, saving the framing of every
// entry after the first.
//
// The identifier of the first value follows the same rule as the unpacked
// entries: 0 means previous_id + 1, or 1 at the start of the stream. Every
// following value takes the identifier of the previous one plus 1.
//
// A packed entry with no values at all is a no-op and must not be written.
message RdfLookupEntryPacked {
// 1-based identifier of the first value.
// If id=0, it should be interpreted as previous_id + 1.
// If id=0 appears in the first entry of a given lookup table in the
// stream, it should be interpreted as 1.
uint32 id = 1;
// Values of the lookup entries (UTF-8 encoded), for consecutive
// identifiers starting at id.
repeated string values = 2;
}
// Version of RDF that a stream conforms to, following the version labels of
// RDF 1.2 Concepts:
// https://www.w3.org/TR/rdf12-concepts/#defined-version-labels
//
// Each version includes the ones listed before it: RDF 1.1 data is valid
// RDF 1.2 Basic data, which is valid RDF 1.2 data.
enum RdfVersion {
// Unspecified. Each format that uses this enum defines how to interpret it.
RDF_VERSION_UNSPECIFIED = 0;
// RDF 1.1 (version label "1.1"): no triple terms, no base directions.
RDF_VERSION_1_1 = 1;
// RDF 1.2 Basic (version label "1.2-basic"): base directions are allowed,
// triple terms are not.
RDF_VERSION_1_2_BASIC = 2;
// RDF 1.2 (version label "1.2"): base directions and triple terms are
// allowed.
RDF_VERSION_1_2 = 3;
}
// Initial text direction of a language-tagged string (RDF 1.2).
enum RdfBaseDirection {
// No base direction: an ordinary language-tagged string (rdf:langString).
RDF_BASE_DIRECTION_UNSPECIFIED = 0;
// Left-to-right ("ltr"): a directional language-tagged string
// (rdf:dirLangString).
RDF_BASE_DIRECTION_LTR = 1;
// Right-to-left ("rtl"): a directional language-tagged string
// (rdf:dirLangString).
RDF_BASE_DIRECTION_RTL = 2;
}
// RDF literal, with support for the RDF 1.2 base direction.
//
// Fields 1–3 are the same as in RdfLiteral (rdf.proto), so every valid
// RdfLiteral message is also a valid RdfLiteral2 message with no base
// direction, and the other way round.
// TODO (RDF 1.2): unify this!
message RdfLiteral2 {
// The lexical form of the literal (required).
string lex = 1;
// Literal kind – at most one of these field may be set.
// If none is set, then it's a simple literal.
oneof literalKind {
// Language-tagged string.
string langtag = 2;
// Typed literal. The datatype is a reference to an entry in the
// datatype lookup. This value is 1-based and the value of 0
// is invalid (in contrast to prefix_id and name_id in RdfIri).
//
// Must not refer to rdf:langString or rdf:dirLangString – use langtag
// and direction instead.
uint32 datatype = 3;
}
// Base direction of a language-tagged string.
// Must not be set unless langtag is set.
RdfBaseDirection direction = 4;
}
// RDF 1.2 triple term.
//
// This message can only represent triples that are
// valid in RDF 1.2: the subject is an IRI or a blank node, the predicate is
// an IRI, and only the object may be a nested triple term. There are no
// repeated terms: every position must be set.
//
// The field numbers are the same as in RdfTriple.
message RdfTripleTerm {
// Subject (required).
oneof subject {
// IRI
RdfIri s_iri = 1;
// Blank node
string s_bnode = 2;
}
// Predicate (required).
RdfIri p_iri = 5;
// Object (required).
oneof object {
// IRI
RdfIri o_iri = 9;
// Blank node
string o_bnode = 10;
// Literal
RdfLiteral2 o_literal = 11;
// Nested triple term
RdfTripleTerm o_triple_term = 12;
}
}
sparql.proto
syntax = "proto3";
package eu.ostrzyciel.jelly.core.proto.v1.sparql;
// Jelly-SPARQL: binary serialization for SPARQL query results
// (solution sequences and boolean ASK results), based on the Jelly-RDF term
// encoding (rdf.proto).
//
// The results are transmitted as a stream of frames. Each frame contains a
// batch of solutions (rows) in a columnar layout: one column per variable,
// with columns grouped by term type. In practice most columns hold only one
// kind of RDF term (e.g., only IRIs), so keeping them in per-type
// (monomorphic) lists avoids boxing every value in a message with a oneof.
// Variables whose values mix term types use polymorphic columns.
//
// The first frame has the header: stream options and the mapping from
// variable names to columns. The header (except the options) may be restated
// in a later frame to change the column layout mid-stream – for example,
// when a variable that so far held only IRIs encounters a literal and needs
// to move to a polymorphic column. The stream ends with a trailer, which says
// whether the result set is complete.
//
// By default, a stream contains one result set (SPARQL_STREAM_TYPE_FLAT). A
// stream of type SPARQL_STREAM_TYPE_PUNCTUATED contains a sequence of result
// sets instead, each ended by a trailer.
import "rdf2.proto";
// Stream options for SPARQL result streams.
message SparqlResultsOptions {
// Name of the stream (completely optional).
// This may be used for, e.g., topic names in a pub/sub system.
string stream_name = 1;
// Type of the stream: one result set, or a sequence of result sets.
// Optional. Defaults to SPARQL_STREAM_TYPE_FLAT.
// Must be the same in every options message of the stream.
SparqlStreamType stream_type = 2;
// Version of RDF whose terms may occur in the stream.
// RDF_VERSION_1_1 – no triple terms, no base directions
// RDF_VERSION_1_2_BASIC – base directions, no triple terms
// RDF_VERSION_1_2 – base directions and triple terms
// Unspecified (the default) means that no version is announced: the stream
// may contain any RDF 1.2 term. Values not listed here are invalid.
RdfVersion rdf_version = 5;
// Maximum size of the name lookup table
// (required, must be >= 128)
uint32 max_name_table_size = 9;
// Maximum size of the prefix lookup table
// (required if the prefix lookup is used)
uint32 max_prefix_table_size = 10;
// Maximum size of the datatype lookup table
// (required if datatype literals are used)
uint32 max_datatype_table_size = 11;
// Protocol version (required)
// For this proposal, the value must be 1.
// For custom extensions, the value must be 10000 or higher.
uint32 version = 15;
}
// Type of a SPARQL result stream.
enum SparqlStreamType {
// The entire stream is a single result set. This is the default.
// buf:lint:ignore ENUM_ZERO_VALUE_SUFFIX
SPARQL_STREAM_TYPE_FLAT = 0;
// The stream is a sequence of result sets, each ended by a frame with a
// trailer. A result set may span multiple frames, and each frame belongs to
// exactly one result set. The result sets are independent of each other
// (they may have different variables, and mix solution sequences with
// boolean results), but the lookup tables are kept from one to the next.
// Blank node labels are scoped to one result set.
SPARQL_STREAM_TYPE_PUNCTUATED = 1;
}
// Header entry mapping one result variable to one column.
message SparqlVariable {
// Name of the variable, without the leading "?" or "$". Must not be empty.
string name = 1;
// 0-based index of the column that holds this variable's values.
//
// The index points into the virtual concatenation of all column lists of
// a frame, in field number order: iri_columns first, then bnode_columns,
// literal_columns, and finally poly_columns.
//
// For example, if a frame has 2 IRI columns, 1 blank node column, and 2
// literal columns, the column indices are as follows:
// iri_columns[0] | column_index = 0
// iri_columns[1] | column_index = 1
// bnode_columns[0] | column_index = 2
// literal_columns[0] | column_index = 3
// literal_columns[1] | column_index = 4
uint32 column_index = 2;
}
// Boolean result of a SPARQL ASK query.
message SparqlAskResult {
// The boolean value of the result.
bool value = 1;
}
// ---------------------------------------------------------------------------
// Columns
//
// A column stores the cells of one variable across all row_count rows of the
// frame, in row order. A cell is either a bound RDF term or unbound.
//
// Columns use a compact sequence layout encoding: the logical sequence of
// row_count cells is stored as a list of run values (each run of consecutive
// equal values stored exactly once, unbound cells not stored at all) plus a
// `layouts` list describing where the cell sequence deviates from "each value
// appears exactly once". When every cell is bound and no value is repeated
// consecutively, `layouts` is empty.
//
// `layouts` is a flat list of run-length exceptions for values that need to
// be repeated, or need to have a run of unbound values after them. Each
// exception is one varint token, optionally followed by one extension varint:
//
// token = (skip << 5) | (kind << 4) | len_code
//
// skip – number of values emitted exactly once since the previous
// exception (or since the start of the column)
// kind – 0 = repeat run, 1 = unbound run
// len_code – 0–14: len is this literal value;
// 15: len = 15 + the next varint in the layouts list
//
// A token stays a single byte while skip <= 3. Runs of up to 16 repeats or
// 15 unbound cells are inlined in the token, without an extension varint.
//
// Run lengths (a repeat run of length < 2 makes no sense, hence the offsets):
// repeat run – the value at the current position occurs (len + 2) times
// unbound run – (len + 1) consecutive unbound cells
//
// An unbound run sits BEFORE the value at the current position (or after the
// last value, once all values have been consumed), which makes the order of
// an unbound run and a repeat run at the same position unambiguous.
//
// Decoding (m = number of values):
//
// i = 0
// for each exception (skip, kind, len):
// emit values[i .. i+skip) once each; i += skip
// if kind == 0: emit values[i] (len + 2) times; i += 1
// else: emit (len + 1) unbound cells
// emit values[i .. m) once each // implicit tail
// pad with unbound cells up to row_count
//
// Encoders SHOULD omit trailing unbound runs and rely on the padding rule.
// A column with no bound cells at all is an empty message. Encoders must
// merge adjacent runs (e.g., never emit two unbound runs separated by
// skip == 0).
//
// A frame is corrupt if: i + skip > m; a repeat run starts when i == m;
// len_code == 15 is not followed by an extension varint, or the column
// decodes to more than row_count cells.
// ---------------------------------------------------------------------------
// Column of IRI values.
//
// The run values are two parallel packed lists of prefix and name identifiers.
message SparqlIriColumn {
// Name identifiers of the run values, in row order. This list defines the
// number of run values in the column.
//
// The name_id inference rule of RdfIri applies along this list: 0 means
// "previous name_id + 1", and the state resets at the start of every column
// in every frame, where 0 means name_id = 1. Columns can therefore be
// decoded independently of each other (given the lookup tables).
repeated uint32 name_ids = 1;
// Sequence layout, see above. Empty = every cell is bound, no repeats.
repeated uint32 layouts = 2;
// Prefix identifiers of the run values, in row order.
//
// The list must have one of three lengths:
// 0 – every value has prefix_id = 0 (no prefix), which is
// the case when the prefix lookup is disabled
// 1 – every value uses this one prefix, which is the case
// for a column of IRIs sharing a namespace
// len(name_ids) – one entry per value
//
// In the third form the prefix_id inference rule of RdfIri applies along the
// list: 0 means "same prefix as the previous IRI", starting from 0.
//
// This is the last field of the message because it is usually tiny.
repeated uint32 prefix_ids = 3;
}
// Column of blank node values.
message SparqlBnodeColumn {
// Run values (blank node labels), in row order.
repeated string values = 1;
// Sequence layout, see above.
repeated uint32 layouts = 2;
}
// Column of literal values.
//
// Each run value is a lexical form plus a literal kind, which says whether it
// is a simple literal, a literal with a datatype from the datatype lookup, or a
// language-tagged string with one of the column's language tags. A column in
// which every value has the same kind states the kind once, which lets readers
// keep the column in a string array instead of one object per value.
message SparqlLiteralColumn {
// Lexical forms of the run values, in row order. This list defines the
// number of run values in the column.
repeated string lex_values = 1;
// Sequence layout, see above.
repeated uint32 layouts = 2;
// Literal kind of each run value, parallel to lex_values:
// 0 – simple literal (xsd:string)
// odd v – literal with the datatype of datatype lookup id (v + 1) / 2
// even v > 0 – language-tagged string with language tag v / 2 - 1, an
// index into langtags
//
// The list must have one of three lengths:
// 0 – every value is a simple literal
// 1 – every value has this kind
// len(lex_values) – one entry per value
//
// A datatype must not be rdf:langString or rdf:dirLangString – use a
// language tag instead. Simple literals do not need an xsd:string entry in
// the datatype lookup.
repeated uint32 literal_kinds = 3;
// The language tags referred to by literal_kinds, in the order of first use.
// Each should be a valid BCP 47 language tag. The same tag may appear more
// than once, with different base directions.
repeated string langtags = 4;
// Base directions of the language tags (RdfBaseDirection numbers), parallel
// to langtags. Empty means no base direction for any tag. A direction other
// than RDF_BASE_DIRECTION_UNSPECIFIED is not valid if the stream options
// declare RDF_VERSION_1_1.
repeated uint32 langtag_directions = 5;
}
// Polymorphic column: values may be RDF terms of any type.
// To be used only for variables whose values mix term types – prefer the
// monomorphic columns whenever possible.
//
// The run values are split by term type into typed sub-columns. The `kinds`
// field says which sub-column contains the next run value. The sub-columns
// have no layouts of their own: producers must not set `layouts` in them.
// Consumers should ignore it, and may throw an error if it is set.
message SparqlPolyColumn {
// Term type of each run value, in row order, 2 bits per value, four values
// per byte starting from the least significant bits:
// 0 – the next value of iris
// 1 – the next value of literals
// 2 – the next value of bnodes
// 3 – the next value of triple_terms
// The number of run values is the total number of values in the
// sub-columns, and kinds must have exactly ceil(values / 4) bytes. Unused
// bits of the last byte must be 0.
bytes kinds = 1;
// Sequence layout of the whole column, see above.
repeated uint32 layouts = 2;
// The IRIs of the column.
SparqlIriColumn iris = 3;
// The literals of the column.
SparqlLiteralColumn literals = 4;
// The blank nodes of the column.
SparqlBnodeColumn bnodes = 5;
// The triple terms of the column.
// Not valid if the stream options declare RDF_VERSION_1_1 or
// RDF_VERSION_1_2_BASIC.
//
// The IRIs inside the triple terms share one RdfIri inference state, which
// advances through them in order – subject, predicate, object, recursively
// into nested triple terms. It is separate from the state of the `iris`
// sub-column, and resets at the start of every column in every frame.
repeated RdfTripleTerm triple_terms = 6;
}
// End-of-result-set marker, saying whether the result set is complete.
//
// Producers must write a trailer in the last frame of every result set,
// unless they cannot write anything more at all (for example, because the
// process died).
//
// In a FLAT stream, the trailer ends the stream: a frame containing a trailer
// must not be followed by any frame that does not include the stream options.
// In a PUNCTUATED stream, the frame after a trailer starts a new result set.
message SparqlResultsTrailer {
// Empty (the default) means the result set is complete.
// A non-empty value is a human-readable explanation of why the producer
// could not complete the result set (e.g., query failure mid-evaluation).
string error = 1;
}
// SPARQL results frame – base message of a SPARQL result stream.
//
// A frame contains a batch of rows (solutions). A frame must contain either
// exactly the columns established by the most recent header (the same number
// of columns of each type), or no columns at all. The no-columns form is only
// valid when row_count is 0 (or the result set has no variables), and is the
// recommended encoding for a frame with no rows. A column whose cells are all
// unbound in a frame is encoded as an empty message. Columns must decode to
// at most row_count cells each (shorter columns are padded with unbound
// cells – see the layout encoding).
//
// Frames are self-contained except for the lookup tables and the header:
// a frame can be decoded using only the lookup table state left by the
// preceding frames and the most recently transmitted header.
message SparqlResultsFrame {
// Stream options.
// Must be set in the first frame of the stream.
//
// May be set again in a later frame. Doing so empties the lookup tables
// (identifier numbering restarts at 1).
//
// In a FLAT stream, it also makes the header cease to be in effect, so the
// same frame must restate it, with the same variables as the first frame.
// Blank node labels are not reset. This is intended only for concatenating
// result streams of the same query.
//
// In a PUNCTUATED stream, the options may be set again only in the first
// frame of a result set.
SparqlResultsOptions options = 1;
// Result set header: the variables of the result set, in projection
// (SELECT clause) order, each mapped to one column.
//
// Must be set in the first frame of every result set with a solution
// sequence. A later frame of the same result set may restate the header to
// change the column layout (e.g., to move a variable from a monomorphic
// column to a polymorphic one). A restated header must list exactly the
// same variables in the same order – only the column assignments (and thus
// the number of columns of each type) may change. It takes effect for the
// frame it appears in and for all subsequent frames, until restated again.
// An empty variables list in the first frame of a result set, or in a frame
// that contains the options, declares a zero-variable result set (row_count
// then conveys the number of empty solutions).
repeated SparqlVariable variables = 2;
// Number of rows (solutions) in this frame (required).
// Must not be greater than 2^27 - 1, the largest number of cells the
// layout encoding can address.
uint32 row_count = 3;
// Entries in the name lookup table.
// All lookup entries of a frame are applied, in order, before any column
// of the frame is decoded. Consequently, every lookup id referenced by the
// frame's columns must still hold the intended value after all entries are
// applied: the working set of a single frame must fit in the lookup
// tables.
repeated RdfLookupEntryPacked names = 4;
// Entries in the prefix lookup table. See `names` for ordering rules.
//
// Note: the prefixes in the lookup table can be arbitrary strings, and are
// NOT meant to be user-facing. They are only used for IRI compression.
// To transmit user-facing namespace declarations for cosmetic purposes, use
// RdfNamespaceDeclaration.
repeated RdfLookupEntryPacked prefixes = 5;
// Entries in the datatype lookup table. See `names` for ordering rules.
repeated RdfLookupEntryPacked datatypes = 6;
// IRI columns.
repeated SparqlIriColumn iri_columns = 7;
// Blank node columns.
repeated SparqlBnodeColumn bnode_columns = 8;
// Literal columns.
repeated SparqlLiteralColumn literal_columns = 9;
// Polymorphic columns.
repeated SparqlPolyColumn poly_columns = 10;
// Boolean (ASK) result.
// If set, the result set is a boolean result instead of a solution
// sequence. It may be set only in the first frame of a result set, which
// must include no variables, no columns, and row_count = 0. A boolean
// result is always the only frame of its result set.
SparqlAskResult ask_result = 11;
// End-of-result-set marker. See SparqlResultsTrailer.
SparqlResultsTrailer trailer = 12;
// Arbitrary metadata
// The keys are UTF-8 encoded strings, the values are byte arrays.
// The metadata does not affect the result data in any way, treat it
// as comments in a text file.
//
// Well-known keys (their values must be valid UTF-8):
// link – zero or more IRIs separated by LF (U+000A), describing the
// result set as a whole (not the frame). Corresponds to head.link
// in the SPARQL Query Results JSON format.
//
// All other keys may be used by implementations in any way they see fit,
// and their values are arbitrary bytes.
map<string, bytes> metadata = 15;
}
grpc.proto
syntax = "proto3";
package eu.ostrzyciel.jelly.core.proto.v1;
// Jelly gRPC streaming protocol.
// Specification document: https://w3id.org/jelly/1.0.0/specification/streaming
// Protocol version: 1.0.0
import "rdf.proto";
// Subscribe command sent by the client to the server.
message RdfStreamSubscribe {
// The topic to which the client wants to subscribe (UTF-8 encoded).
string topic = 1;
// Optional: the stream options requested by the client.
// The server should respond with a stream that matches these options.
// In case that is not possible, the server must respond with the
// INVALID_ARGUMENT error.
RdfStreamOptions requested_options = 2;
}
// Acknowledgement of receiving a stream sent by the server to the client.
message RdfStreamReceived {}
// Pub/Sub service for RDF streams, to be implemented by the server.
service RdfStreamService {
// Subscribe to an RDF stream.
rpc SubscribeRdf(RdfStreamSubscribe) returns (stream RdfStreamFrame);
// Publish an RDF stream.
// In case the server cannot process the stream, it must respond with
// the INVALID_ARGUMENT error.
rpc PublishRdf(stream RdfStreamFrame) returns (RdfStreamReceived);
}