Key/Value

The key/value model is the simplest data model: every value is reachable through a single identifier (the key). ArcadeDB supports the key/value pattern natively without sacrificing the richer features of the multi-model engine — values can be primitive types, full documents, or even graph elements.

How Keys Work in ArcadeDB

Every record stored in ArcadeDB is automatically addressable by its Record ID (RID), a compact identifier of the form #bucket:offset. The RID provides O(1) direct access to any record, which means the entire database functions as a high-performance key/value store — no separate engine, no extra indexes required.

In addition to RIDs, you can layer your own keys on top:

  • Indexed keys — create a unique LSM-Tree index on any property to look up records by an arbitrary identifier such as userId, sku, or email.

  • Bucket-based keys — group records into buckets and use the bucket as a key/value namespace where records are inserted and retrieved by RID without requiring a type schema.

  • Composite keys — create indexes spanning multiple properties when the natural key is not a single value (for example, (tenantId, accountId)).

Key/Value with the Redis Wire Protocol

ArcadeDB speaks the Redis wire protocol, so any Redis client (here Jedis) can use it as a drop-in key/value backend. Simple string commands (SET, GET, INCR, …​) operate on a fast in-memory namespace:

final Jedis jedis = new Jedis("localhost", 6379);

jedis.set("foo", "1");                 // store
String value = jedis.get("foo");       // read -> "1"
boolean present = jedis.exists("foo"); // check
jedis.incr("foo");                     // atomic increment -> 2
jedis.incrBy("foo", 3);                // -> 5
String removed = jedis.getDel("foo");  // read + delete atomically

To persist values as queryable documents, back the key with a unique index and use the hash commands. The HGET/HEXISTS/HDEL key uses the form database.Type[indexName]:

// One-time schema: a type plus a unique index acting as the key
database.command("sqlscript",
    "CREATE DOCUMENT TYPE Account;" +
    "CREATE PROPERTY Account.id LONG;" +
    "CREATE INDEX ON Account (id) UNIQUE;" +
    "CREATE PROPERTY Account.email STRING;" +
    "CREATE INDEX ON Account (email) UNIQUE;");

// Store a JSON value under the "Account" type
jedis.hset("mydb", "Account", "{'id':1,'email':'jay@commodore.com','firstName':'Jay'}");

// Read it back by the indexed key (id == 1)
JSONObject doc = new JSONObject(jedis.hget("mydb.Account[id]", "1"));

// ...or by the email key
doc = new JSONObject(jedis.hget("mydb.Account[email]", "jay@commodore.com"));

boolean present = jedis.hexists("mydb.Account[id]", "1");
jedis.hdel("mydb.Account[id]", "1");   // delete by key

Records can also be addressed directly by their RID: jedis.hget("mydb", "#13:432").

Key/Value with the Java API

Create a unique index on the property you want to use as the key, then look records up with lookupByKey:

database.command("sql",
    "CREATE DOCUMENT TYPE Account;" +
    "CREATE PROPERTY Account.id LONG;" +
    "CREATE INDEX ON Account (id) UNIQUE");

// Single-key lookup
final IndexCursor result = database.lookupByKey("Account", "id", 12345L);
if (result.hasNext())
  Document value = result.next().asDocument();

// Composite-key lookup (e.g. multi-tenant natural keys)
final IndexCursor composite = database.lookupByKey("Account",
    new String[] { "tenantId", "accountId" },
    new Object[] { "acme", 42L });

For ephemeral, in-memory state (session data, counters) that does not need its own type, use database global variables, which are also readable from SQL:

database.setGlobalVariable("myKey", "myValue");
Object value = database.getGlobalVariable("myKey");   // "myValue"
// SELECT $myKey returns the value from SQL

Key/Value over HTTP

Over the HTTP API, key/value access is expressed as SQL through the /api/v1/command/<database> endpoint. A lookup by indexed key is a simple parameterized query:

# Read the value whose key id = 1
curl -X POST "http://localhost:2480/api/v1/command/mydb" \
  -u root:password -H "Content-Type: application/json" \
  -d '{"language":"sql","command":"SELECT FROM Account WHERE id = :id","params":{"id":1}}'

# Store / upsert a value by key
curl -X POST "http://localhost:2480/api/v1/command/mydb" \
  -u root:password -H "Content-Type: application/json" \
  -d '{"language":"sql","command":"UPDATE Account SET email = :e UPSERT WHERE id = :id","params":{"id":1,"e":"jay@commodore.com"}}'

This makes ArcadeDB usable as a drop-in key/value backend for caches, session stores, configuration stores, or any pattern where direct addressing by identifier is the primary access mode.

Comparison with Other Models

Relational Model Key/Value Model ArcadeDB Key/Value Model

Table

Bucket

Bucket

Row

Key/Value pair

Document

Column

not available

Document field or Vertex/Edge property

Relationship

not available

Relationship

Why ArcadeDB for Key/Value

Most key/value stores treat the value as opaque — a blob of bytes the database doesn’t understand. ArcadeDB treats every value as a structured record:

  • Rich values — values can be documents, vertices, or edges with typed properties, not just byte arrays.

  • Queryable — the same record retrievable by RID is also queryable with SQL, Cypher, Gremlin, and other languages, without re-indexing or duplicating data.

  • Transactional — key/value writes participate in full ACID transactions alongside graph and document operations.

  • Persistent — LSM-Tree storage with automatic compaction ensures durability and efficient disk usage at scale.

  • Multi-protocol — access the same data through HTTP, the Redis wire protocol, the Java API, or any other supported interface.

Further Reading

  • Record ID (RID) — how RIDs work and how to use them as primary keys

  • Buckets — physical storage units that double as key/value namespaces

  • HTTP API — run SQL commands and queries (including key lookups) over REST

  • Redis Wire Protocol — speak the Redis protocol against ArcadeDB