Tina4   ×   Ultipa

tina4-ultipa

The Ultipa graph driver for Ruby

A full-fidelity gRPC client for the Ultipa graph database — every GQL type decoded, no Tina4 lock-in.

RubyGems Tests MIT Ultipa Tina4

Quick StartType SupportUltipa DocsProtocoltina4.com


A thin, standalone gRPC driver for the Ultipa graph database (ultipa-gqldb). It is the Ultipa driver behind Tina4's graph data layer — but has no Tina4 dependency and is perfectly usable on its own. Tina4's core stays zero-dependency; this driver is an optional gem loaded only for ultipa:// connections, speaking gRPC directly (grpc + google-protobuf).

Ultipa is a high-performance graph database with a GQL (ISO/IEC 39075) query surface. Learn more at ultipa.com · docs at ultipa.com/docs.

Why this driver

  • Complete value decoding. Every gqldb PropertyType is decoded to a natural Ruby value — including the composites and temporals most thin clients skip (see Type support).
  • Real gRPC, vendored stubs. Generated protobuf stubs ship inside the gem (lib/tina4_ultipa/gen/), so there is no reflection round-trip and no codegen step at install time.
  • Fails loud. A bad statement raises; it never returns a falsy value you might miss. An unreachable host raises within your connect_timeout.
  • Cross-language parity. The same driver exists for Python, Node.js, Ruby and PHP, decoding byte-for-byte identically.

Install

gem install tina4-ultipa

or in a Gemfile:

gem "tina4-ultipa"

Quick start

require "tina4_ultipa"

# from a URL...
db = Tina4Ultipa::Client.from_url("ultipa://admin:password@localhost:60061/mygraph").connect
# ...or explicitly
db = Tina4Ultipa::Client.new(
  host: "localhost", port: 60061,
  username: "admin", password: "password", graph: "mygraph"
).connect

# read — GQL text + optional named params
db.query("MATCH (n:Person) WHERE n.age > $min RETURN n.name AS name",
         params: { min: 21 }).each do |row|
  puts row["name"]
end

# write — dml stats on success, raises on a bad statement
r = db.execute("INSERT (:Person {name: 'Alice', age: 30})")
puts r.dml_stats[:inserted_nodes]   # 1

db.close

query runs a read, execute a write — both take GQL and optional params and return a Tina4Ultipa::Result:

Member Meaning
#columns column names
#rows decoded rows (arrays of Ruby values)
#to_h_rows / #dicts rows as hashes keyed by column
#scalar first cell of the first row
#rows_affected, #dml_stats, #warnings write metadata

Result is Enumerable and yields each row as a hash, so each, map and select work directly on a result.

Errors: a bad statement raises Tina4Ultipa::Error; an unreachable host raises Tina4Ultipa::ConnectError within connect_timeout, naming host, port and elapsed time.

Type support

Every gqldb PropertyType decodes to a natural Ruby value:

Category Types Decodes to
Numeric int32/uint32/int64/uint64, float, double Integer / Float
Text string, text String
Boolean / null bool, null, unset true/false / nil
Binary blob binary String (e.g. an image, round-trips intact)
Decimal decimal String (precision-preserving)
Temporal date, local/zoned time, local/zoned datetime, timestamp ISO-8601 String
Interval year-to-month, day-to-second {"months" => …} / {"seconds" => …, "nanoseconds" => …}
Spatial point, point3d {"x", "y"[, "z"], "srid"} Hash
Vector vector Array of Float
Graph node, edge, path Hash (_kind node/edge/path; nodes/edges carry id, labels/type, properties, internal uuid)
Tabular list, set, map, record, table Array / Hash
Error error {"code", "message"}

Node/edge hashes include the 8-byte internal-id (uuid) trailer emitted by gqldb 6.1.147+.

Protocol

ultipa-gqldb speaks gRPC (default port 60061, protobuf package gqldb). The driver authenticates with SessionService.Login, passes the returned session_id on every call as the session-id metadata header (as an unsigned decimal), then runs QueryService.Gql. Each value is a TypedValue{type, bytes}; all multi-byte integers are little-endian. The full byte-level encoding is documented in PROTOCOL.md — grounded in Ultipa's official gqldb SDK and proto, and verified live against gqldb-grpc 6.2.130 CE.

Sample data pack

Get a real graph into your community-edition instance in seconds - 10 people (with photos), 3 companies, 3 projects, 5 skills and 62 relationships:

ruby examples/seed.rb ultipa://admin:PASSWORD@HOST:60061
# graph defaults to "default"; override with TINA4_ULTIPA_GRAPH

Then explore it:

MATCH (n:Person)-[e]->(m) RETURN n, e, m LIMIT 100
RETURN db.overview()

The pack lives in examples/ - people.json describes the graph and sample_data/faces/*.jpg are 64px portraits of AI-generated, non-existent people (thispersondoesnotexist.com) stored as BLOB properties, so it also demonstrates binary round-tripping through gqldb. Re-running is safe - it clears the demo labels (Person/Company/Project/Skill) first.

Testing

Real, no-mock tests run against a live server:

TINA4_TEST_ULTIPA_URL=ultipa://admin:password@host:60061 ruby test/test_driver.rb

They cover scalars, node/edge/list/map decoding, the full temporal/spatial/interval set, path decoding, and a BLOB image round-trip.

License

MIT. Note: the Ultipa community-edition server is licensed by Ultipa for personal / non-commercial use — review Ultipa's terms before you deploy it. This driver is an independent client and may not be officially supported by Ultipa.


Sponsored by Code Infinity · part of the Tina4 stack.