Protobuf concepts into the structs typedb already defines.
Not a parallel set of types: an application that matches on
%TypeDB.Concept.Attribute{} must keep matching after it switches transports,
so this decodes into exactly those structs. Where the two wire formats
disagree, this module is the side that moves.
They disagree about almost every value
The HTTP API sends values as JSON, which means everything TypeDB has no JSON equivalent for arrives as a string to be parsed — a decimal, a datetime, a duration. Protobuf sends them structured, so the work here is the opposite shape: assembling an Elixir term out of fields rather than parsing text.
- a decimal is
integerplusfractional, wherefractionalcounts 1/10^19ths — TypeDB's fixed scale, not a digit count - a date is days since 0001-01-01, day 1 being that date
- a datetime is seconds since the epoch plus nanoseconds
- a duration is months, days and nanos held apart, exactly as TypeQL's
grammar keeps them, which is why
TypeDB.Durationcan be built from it without going through text at all
That last one is a small win over the HTTP transport: this driver never has to emit or parse an ISO-8601 duration, so the negative-component problem the sibling documents cannot arise here.
Summary
Functions
A protobuf Concept into a TypeDB.Concept struct.
Any protobuf message as a tree of plain maps.
A Typedb.Protocol.ConceptRow and its column names into a TypeDB.ConceptRow.
A protobuf Value into the Elixir term it stands for.
The name TypeDB gives the value type of a Value, matching the strings the
HTTP API uses so that %TypeDB.Concept.Value{value_type: ...} reads the same
on both transports.
Functions
@spec concept(Typedb.Protocol.Concept.t()) :: TypeDB.Concept.t() | nil
A protobuf Concept into a TypeDB.Concept struct.
nil for a Concept whose oneof is unset — which a server of a newer
protocol can send, carrying a kind of concept this build has no schema for.
Decoding the rest of the answer and leaving one column empty is better than
failing the whole read.
Any protobuf message as a tree of plain maps.
For analyze, and only for it. Every other reply in this driver decodes into
a struct typedb already defines, because a caller has something to do with
it; a query analysis is a description of the query, arbitrarily shaped, deep,
and different in every TypeDB release. Hand-mapping it would be a hundred
clauses that fall behind the protocol, so this walks the message's own
reflection instead.
The rendering follows the sibling's JSON rather than protobuf convention:
keys are the schema's json_name, so text_span reads as "textSpan", and a
oneof becomes a "tag" alongside its inlined fields, which is exactly how
the HTTP API renders the same tree. It does not follow proto3's JSON
mapping, on purpose: that one omits fields holding their type's default, and
in an analysis that would silently drop every reference to variable 0.
@spec row(Typedb.Protocol.ConceptRow.t(), [String.t()]) :: TypeDB.ConceptRow.t()
A Typedb.Protocol.ConceptRow and its column names into a TypeDB.ConceptRow.
@spec value(Typedb.Protocol.Value.t()) :: term()
A protobuf Value into the Elixir term it stands for.
@spec value_type(Typedb.Protocol.Value.t()) :: String.t() | nil
The name TypeDB gives the value type of a Value, matching the strings the
HTTP API uses so that %TypeDB.Concept.Value{value_type: ...} reads the same
on both transports.