The Drivine cow

DRIVINE

Graph database client for TypeScript & the JVM

Community project by Liberation Data.

npm install @liberation-data/drivineorg.drivine:drivine4j

THE DIVINE DRIVER

Get ready! You’re about to obtain supernatural graph database powers.

Drivine maps graphs to objects by composition. Declare the view a use case needs, made of node fragments and the relationships between them, and Drivine writes the statement. When you would rather write the Cypher yourself, the persistence manager is still there.

TYPESCRIPT · NODE.JS

Drivine

Built on NestJS. Decorated classes, a typed query DSL whose references come from the classes themselves, and injection with @InjectGraphObjectManager.

npm install @liberation-data/drivine

KOTLIN · JAVA · SPRING BOOT

drivine4j

The graph client for Embabel. Annotated models in Kotlin or Java, a DSL generated by KSP, and Spring Boot auto-configuration with test containers built in.

implementation("org.drivine:drivine4j:<version>")

One storage format. A store written from Node is read, stamped and checked alike from the JVM, and the other way round.

COMPOSITION OVER INHERITANCE

Compose views, not models.

A person can play many roles. Rather than one model that serves every use case, declare a view for each, and load it in a single statement.

  • @NodeFragment maps one node.
  • @GraphView composes fragments and relationships from a @Root.
  • The DSL’s references come from your classes, or from code KSP generates.
@NodeFragment({ labels: ['Person'] })
export class Person {
    @NodeId() uuid: string;
    name: string;
    bio?: string;
}

@GraphView()
export class PersonCareer {
    @Root() person: Person;

    @GraphRelationship({ type: 'WORKS_FOR', target: () => WorkHistory })
    employmentHistory: WorkHistory[] = [];
}

// Everyone whose bio mentions leading
const leads = await gom.loadAll(PersonCareer, (q) => ({
    where: q.person.bio.contains('Lead')
}));

IN BOTH LIBRARIES

Everything an object manager should do, and the Cypher when you want it.

A typed query DSL

Filters, ordering, keyset paging and index advice, accepted by loads, counts, deletes, patches and grouped reads.

Saves, patches and edges

save and saveAll with null policies and replace modes, update, create, patch, cascading deletes, relate and unrelate.

Optimistic locking

@NodeStamp refuses a save when the node changed since it was loaded, in a stamp format Node and the JVM share.

Vector and full-text search

loadNearest and loadMatching on views and fragments, with one score scale on every engine.

Schema management

Range, uniqueness, vector and full-text indexes declared on your classes, enforced at startup, with drift detection.

Test containers, or yours

A container for each datasource in CI. While you iterate, USE_LOCAL_NEO4J=true points tests at your own instance.

PERFORMANCE

One statement, one round trip.

Drivine is built to be fast. There's no lazy loading, no session cache to keep warm and no query for each relationship. A view, with everything related to it, loads in one statement, and a save is one statement too.

Graph Bench uses drivine4j to compare Neo4j, FalkorDB and Oracle on a graph of 1.1 million nodes and 4.5 million relationships, with the same Kotlin on each.

1.5–3.5 ms
to load an issue with its repo, author, assignees, topics and comments, on every engine
1
statement for each load or save, however many relationships it has

Sponsored by Jasper Blues, creator of Typhoon, and Rod Johnson, creator of the Spring Framework and of Embabel, the AI agent framework for the JVM, which uses Drivine as its graph client.

ENGINES

Neo4jMemgraphFalkorDBOracle · SQL/PGQAmazon NeptunePostgres · SQL

Dashed: through the persistence manager.

Up and running in five minutes.

Configure a connection, inject the object manager, load your first view.