IndraDB

Test crates.io Released API docs

A graph database written in rust.

IndraDB consists of a server and an underlying library. Most users would use the server, which is available via releases as pre-compiled binaries. But if you're a rust developer that wants to embed a graph database directly in your application, you can use the library.

IndraDB's original design was heavily inspired by TAO, facebook's graph datastore. In particular, IndraDB emphasizes simplicity of implementation and query semantics, and is similarly designed with the assumption that it may be representing a graph large enough that full graph processing is not possible. Over time, richer query semantics and other features have been added to IndraDB, so it can no longer be called TAO-like, although we try to stay true to some of those original goals.

For more details, see the homepage. See also a complete demo of IndraDB for browsing the wikipedia article link graph.

Features

Usage

IndraDB offers a variety ways to work with it: as a server with cross-language support, as a rust library, and via CLI. What follows are a few examples of each use case.

Server

The server uses gRPC to facilitate cross-language support. gRPC supports many languages; see the official list, though many more are unofficially supported as well. We have official bindings available for python and rust. These examples will require you to have a running server, e.g. to start an in-memory server, simply run indradb-server.

Python

Python bindings are available here and published to pypi as indradb. An example:

```python import indradb import uuid

Connect to the server and make sure it's up

client = indradb.Client("localhost:27615") client.ping()

Create a couple of vertices

outv = indradb.Vertex(uuid.uuid4(), "person") inv = indradb.Vertex(uuid.uuid4(), "movie") client.createvertex(outv) client.createvertex(inv)

Add an edge between the vertices

edge = indradb.Edge(outv.id, "bar", inv.id) client.create_edge(edge)

Query for the edge

results = list(client.get(indradb.SpecificEdgeQuery(edge)) print(results) ```

For further reference, see the docs and python bindings tests.

Rust

The gRPC bindings library is available as indradb-proto. An example:

```rust use indradb; use indradb_proto as proto;

// Connect to the server and make sure it's up let mut client = proto::Client::new("grpc://127.0.0.1:27615".try_into()?).await?; client.ping().await?;

// Create a couple of vertices let outv = indradb::Vertex::new(indradb::Identifier::new("person")?); let inv = indradb::Vertex::new(indradb::Identifier::new("movie")?); client.createvertex(&outv).await?; client.createvertex(&inv).await?;

// Add an edge between the vertices let edge = indradb::Edge::new(outv.id, indradb::Identifier::new("likes")?, inv.id); client.create_edge(&edge).await?;

// Query for the edge let output: Vec = client.get(indradb::SpecificEdgeQuery::single(edge.clone())).await?; // Convenience function to extract out the edges from the query results let e = indradb::util::extractedges(output).unwrap(); asserteq!(e.len(), 1); assert_eq!(edge, e[0]); ```

The rust gRPC bindings library is built to closely mirror the rust library. But if you're using 100% rust, and don't need a server, you can skip all the gRPC rigmarole and just use the rust library directly. For further reference, see the docs and the wikipedia indexing example, which heavily relies on indradb-proto.

Other languages

If you're looking to contribute, adding bindings for your favorite language is a great way to start! The gRPC/protobuf definitions are here.

Rust library

Add IndraDB to your Cargo.toml:

toml indradb-lib = { version = "*", features = ["rocksdb-datastore"] }

(You might want to pin the version, or not include the RocksDB datastore and only support in-memory.)

Here's a brief example:

```rust use indradb;

// Create an in-memory datastore let db: indradb::Database = indradb::MemoryDatastore::default();

// Create a couple of vertices let outv = indradb::Vertex::new(indradb::Identifier::new("person")?); let inv = indradb::Vertex::new(indradb::Identifier::new("movie")?); db.createvertex(&outv)?; db.createvertex(&inv)?;

// Add an edge between the vertices let edge = indradb::Edge::new(outv.id, indradb::Identifier::new("likes")?, inv.id); db.create_edge(&edge)?;

// Query for the edge let output: Vec = db.get(indradb::SpecificEdgeQuery::single(edge.clone()))?; // Convenience function to extract out the edges from the query results let e = indradb::util::extractedges(output).unwrap(); asserteq!(e.len(), 1); assert_eq!(edge, e[0]); ```

For further reference, see the docs and library tests.

CLI

The CLI interacts with a running server.

First start the server: indradb-server.

Then, e.g. count the number of vertices: indradb-client grpc://127.0.0.1:27615 count vertex.

Installation

Releases

We offer pre-compiled releases for linux and macOS.

This should start the default datastore.

From source

To build and install from source:

Docker

If you want to run IndraDB in docker, follow the below instructions.

Server

Build the image for the server:

bash DOCKER_BUILDKIT=1 docker build --target server -t indradb-server .

Run the server:

bash docker run --network host --rm indradb-server -a 0.0.0.0:27615

Client

Build the image for the client:

bash DOCKER_BUILDKIT=1 docker build --target client -t indradb-client .

Run the client:

bash docker run --network host --rm indradb-client grpc://localhost:27615 ping

Datastores

IndraDB offers several different datastores with trade-offs in durability, transaction capabilities, and performance.

Memory

By default, IndraDB starts a datastore that stores all values in-memory. This is the fastest implementation, but there's no support for graphs larger than what can fit in-memory, and data is only persisted to disk when explicitly requested.

If you want to use the standard datastore without support for persistence, don't pass a subcommand; e.g.:

bash indradb-server [options]

If you want to use the standard datastore but persist to disk:

bash indradb-server memory --persist-path=[/path/to/memory/image]

You'll need to explicitly call Sync() when you want to save the graph.

RocksDB

If you want to use the rocksdb-backed datastore, use the rocksdb subcommand; e.g.:

bash indradb-server rocksdb [/path/to/rocksdb.rdb] [options]

Postgres, Sled, etc.

It's possible to develop other datastores implementations in separate crates, since the IndraDB exposes the necessary traits to implement:

Plugins

The IndraDB server includes support for plugins to extend functionality available to clients. Plugins are loaded via dynamically linked libraries.

See the hello world plugin and naive vertex plugin for demonstrations of how to author plugins.

To include plugins, see the --plugins argument for indradb-server, e.g. indradb-server --plugins=plugins/*.so. They are then callable via the gRPC ExecutePlugin function.

Testing

First follow the source building instructions above.

Unit tests

Use make test to run the test suite. Note that this will run the full test suite across the entire workspace, including tests for all datastore implementations. You can filter which tests run via the TEST_NAME environment variable. e.g. TEST_NAME=create_vertex make test will run tests with create_vertex in the name across all datastore implementations. All unit tests will run in CI.

Benchmarks

Microbenchmarks can be run via make bench.

Fuzzing

A fuzzer is available, ensuring the the RocksDB and in-memory datastores operate identically. Run it via make fuzz.

Checks

Lint and formatting checks can be run via make check. Equivalent checks will be run in CI.