Skip to content
topofocusPublic

About

Ruby Interface to ArcadeDB

Resources

Stars

8 stars

Watchers

1 watching

Forks

Repository files navigation

ArcadeDB — Ruby Interface

Ruby interface to ArcadeDB via the HTTP-JSON API. Responses map to a Dry::Struct based ORM: each database type is a model class; graph, document and time-series queries are expressed in ruby. Includes a Query preprocessor and a MATCH statement generator.

Namespace Arcade
Requires Ruby ≥ 2.7, running ArcadeDB server (tested against 26.10.x)
Transport HTTP/JSON (HTTPX, persistent connection, basic auth)
Model layer Dry::Struct — not ActiveRecord
Sibling docs RAG_SCHEMA.md (vector schema), rails.md, CHANGELOG.md

Quick start

mkdir project && cd project
bundle init && bundle add arcadedb
mkdir model bin
# copy bin/console and arcade.yml from this repo, adapt arcade.yml

arcade.yml (project root or config/):

:environment:
  :test:        { dbname: playground, user: root, pass: xxx }
  :development: { dbname: devel,      user: root, pass: xxx }
  :production:  { dbname: production, user: root, pass: xxx }
:admin: { :host: localhost, :port: 2480, :user: root, :pass: xxx }
:logger: stdout

Console: bin/console t|d|p (test environment also loads the spec model files). Connection handle: always available as Arcade::Init.db after Arcade::Init.connect :production.

Architecture

Arcade::Base (Dry::Struct)          model layer
├── Arcade::Vertex   +nodes/edges/traverse/match
├── Arcade::Edge
├── Arcade::Document
├── Arcade::Revision / RevisionRecord    audit trail
└── Arcade::Timeseries                   native time-series types (0.5.4+)

Arcade::Database   db.query / db.execute / db.transmit   (transactions, model allocation)
Arcade::Api        raw HTTP primitives (query, execute, post_text, get_json, ...)

Class name maps to database type via snake_case (Demo::User → demo_user).

Models

# model/demo/user.rb
module Demo
  class User < Arcade::Vertex
    attribute :name, Types::Nominal::String

    def grandparents
      db.query("select in('is_family') from #{rid}")&.allocate_model
    end
  end
end
__END__
  CREATE PROPERTY demo_user.name STRING
  CREATE INDEX ON demo_user(name) UNIQUE

Demo::User.create_type creates the type and runs the __END__ DDL. Undeclared properties are schemaless — they arrive in the values attribute.

Person.insert name: "Hubert", age: 35
Person.update set: { age: 36 }, where: { name: 'Hubert' }
Person.where("age > 40").first.update age: 37
Person.upsert name: 'Hugo'                       # ensure-exists (needs index on field)
Person.delete all: true                          # or where: {...}

Graph: nodes, traverse, match

m = Person.insert(name: 'Hubert'); f = Person.insert(name: 'Pauline')
m.assign via: IsMarriedTo, vertex: f, divorced: false

hubert = Person.find(name: 'Hubert')
hubert.nodes(:out, via: IsMarriedTo, where: "age < 30").first   # or hubert.out.first
hubert.edges(via: IsMarriedTo)                                   # edges carry attributes

start_node.traverse :out, via: Connects, depth: 100              # depth-limited walk

MATCH generator — also from a vertex instance (handy when params[:rid] is given):

Arcade::Match.new(type: Person, as: :p).out(IsMarriedTo).node(where: 'age < 30').execute.select_results
Watchlist.match(symbol: 'iBit', as: :w).out(HasUnderlying).node(as: :u)
"#49:0".load_rid.match.out(HasUnderlying).node(as: :u)

Query objects nest (hybrid queries without string surgery):

Query.new(from: match_object, projection: 'median(note_count)', where: '$depth>=50')

TimeSeries (new in 0.5.4)

Native ArcadeDB time-series types: columnar, compressed, tag-partitioned. Not vertices — no rid, no edges. Membership lives in tags, values in typed fields.

# model/ohlcv.rb
class Ohlcv < Arcade::Timeseries
  timestamp :ts, precision: :second          # unit of values passed to .write only —
                                             # the engine stores milliseconds on every version
  tags   symbol: String, currency: String
  fields open: Float, high: Float, low: Float, close: Float, volume: Integer
end

Ohlcv.create_type

# write — Line Protocol under the hood, batched
Ohlcv.write symbol: 'SAPX:GER', currency: 'EUR', ts: Date.new(2026,10,20),
            open: 290.0, high: 296.0, low: 290.0, close: 295.5, volume: 120
Ohlcv.write_many rows   # array of hashes, one POST per batch

# read — SQL range pushdown
Ohlcv.range from: '2026-01-01', tags: { symbol: 'SAPX:GER' }
Ohlcv.latest tags: { symbol: 'SAPX:GER' }        # => {ts:, close:, ...} or nil

# weekly candles in one query
Ohlcv.bucket '7d', from: Date.new(2024,10,1), tags: { symbol: 'SAPX:GER' },
                   aggregate: { close: :last, high: :max, low: :min, volume: :sum }

Accepted ts values: Time, Date, DateTime, ISO-String, epoch-seconds — normalised to UTC and to the declared precision. Timezones: server timestamps are stored absolute; build Date objects UTC (Time.utc) or comparisons drift by the host offset.

RAG

require 'arcade/rag_schema' — vector + full-text schema for retrieval pipelines. See RAG_SCHEMA.md and examples/rag_example.rb.

Transactions & audit

db = Arcade::Init.db
db.begin_transaction; ... ; db.commit   # or db.rollback

Arcade::Revision subclasses keep an append-only protocol stack of Arcade::RevisionRecord entries; every update appends (old value + action + user).

Low-level access

Arcade::Api.query(db, "select from x")            # parsed :result (array)
Arcade::Api.execute(db) { "update x set ..." }    # write, retry on 'Please retry'
Arcade::Api.get_json("ts/#{db}/latest?type=...")  # raw body (ts endpoints)
Arcade::Api.post_text("ts/#{db}/write?precision=s", lines)  # Line Protocol
db.query("select ...").allocate_model             # hashes -> model objects

Query payload hash: { query:, language: :sql|:cypher|:gremlin|:neo4j, params:, limit:, serializer: :graph|:record }.

Server & version notes (verified ArcadeDB 26.10)

Observation Consequence
schema:types does not list timeseries types existence probe: POST /ts/{db}/query → 400 unknownTypes/does not exist = absent (type_exists? uses this)
CREATE TIMESERIES TYPE registers asynchronously (~1 s) create_type waits for visibility; a write right after create fails with unknownTypes otherwise
DELETE FROM <ts-type> WHERE ... reports a count but keeps rows (26.10.1) no window-replace possible; re-writing a timestamp appends — deduplicate on read
timestamps are stored in milliseconds on every server version declare PRECISION MILLISECOND; from 26.10.2 the server refuses the other precisions — on 26.10.1 they were accepted but honoured by no ingestion path, and RETENTION on a SECOND type deleted every sample (#9800). Types created with another precision pre-26.10.2 keep loading but report maintenanceSuspended: true in schema:types — retention/downsampling are suspended until confirmed
bucket is a reserved word in SQL aliased in backticks by bucket()
IF NOT EXISTS unsupported on CREATE TIMESERIES TYPE guarded by type_exists?
Line Protocol needs pre-created types (tsAutoCreateType off by default) write_many fails with unknownTypes if the type is missing
persistent HTTP connections die after ~3 s idle Primitives renew the handle after 2 s inactivity

Testing

bundle install && bundle exec rspec                  # needs a reachable ArcadeDB server
bundle exec rspec spec/lib/timeseries_spec.rb        # single file; spec/lib/* against arcade.yml :test db

Model files for specs live in spec/model/ (Zeitwerk-loaded). The spec suite is the authoritative behaviour reference — read spec/lib/* before assuming API shape.

Repository layout

lib/arcade.rb              entry point (requires below)
lib/arcade/                base, database, query, match, timeseries, api/, support/
lib/model/                 base classes (vertex, edge, document, revision, timeseries)
lib/models.rb              base-class loader
examples/                  documented use cases
spec/lib, spec/model       rspec suite (authoritative)
bin/console                irb shortcut per environment

Contributing

Bug reports and pull requests are welcome — this project follows the Contributor Covenant code of conduct (detail). New behaviour ships with a spec in spec/lib/ and, if server-quirk-sensitive, a row in the Server & version notes table.

About

Ruby Interface to ArcadeDB

Resources

Stars

8 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages