Skip to content

Bluetape4k Graph Part 1: Database Selection Map

A robotic engineer comparing Neo4j, Memgraph, AGE, TinkerGraph, and FalkorDB graph database blocks on a 3D workbench
Graph database selection is a workload decision, not a taste contest.

The earlier post When should we adopt GraphDB? answered the first question: use a graph database when relationships are the workload. This series starts at the next problem, the one that tends to last longer in real projects: once graph is the right shape, do we start with Neo4j, measure Memgraph, keep PostgreSQL with AGE, or run tests on something lighter?

bluetape4k-graph does not reimplement graph databases. It keeps Neo4j, Memgraph, Apache AGE, TinkerGraph, and FalkorDB in their own strengths and gives Kotlin/JVM services a common place for the repetitive work: CRUD wrappers, batch writes, traversal helpers, bulk I/O, and framework integration. Rebuilding a graph database would be a very creative way to accidentally start a small database company. That is not the goal here.

Graph storage selection map for Neo4j, Memgraph, Apache AGE, TinkerGraph, and FalkorDB
Separate testing, operations, read shape, and write shape before choosing graph storage.
Graph DBAPI shapeGood fit
Neo4jCypher over the Neo4j Java DriverDefault production choice when maturity matters
MemgraphNeo4j-compatible Cypher protocolLow-latency and write-heavy candidates
Apache AGECypher-over-SQL on PostgreSQLPostgreSQL-centered operations
TinkerGraphIn-memory TinkerPop/GremlinUnit tests, examples, local fixtures
FalkorDBRedis module with openCypher subsetRedis-aligned lightweight graph services

The table is not trying to crown a single winner. Tests can start with TinkerGraph, production candidates can be measured with Neo4j or Memgraph, AGE can make sense in PostgreSQL-centered operations, and FalkorDB is worth checking when Redis is already the operational center. For edge-heavy writes, measure first. Benchmarks are very good at humbling architecture opinions.

Cypher is a graph query language. SQL asks questions through tables and rows; Cypher asks by drawing a relationship pattern. The following query starts at a Person, follows a KNOWS edge, and returns connected people.

MATCH (alice:Person {email: $email})-[:KNOWS]->(friend:Person)
RETURN friend.name

The common GraphOperations API does not replace Cypher. Trying to do that would mostly produce a worse Cypher with a friendlier Kotlin wrapper. GraphOperations gives service code a portable starting point for CRUD, batch writes, traversal, and tests. When a production query needs planner control, projections, or storage-specific tuning, use the native Cypher path offered by Neo4j, Memgraph, Apache AGE, or FalkorDB.

bluetape4k-graph module layers from examples and framework adapters to graph-core, graph I/O, and graph storage adapters
graph-core sits in the middle; graph storage adapters, graph I/O, Spring Boot, and Ktor compose around it.

The common API is useful, but it should not erase graph storage semantics. Query planners, indexes, traversal behavior, and transaction semantics differ enough that a quiet no-op is worse than an early failure. Start with TinkerGraph for fast tests, then measure Neo4j, Memgraph, AGE, or FalkorDB with the workload you actually have.

val ops: GraphOperations = TinkerGraphOperations()
ops.createGraph("demo")
val alice = ops.createVertex("Person", mapOf("name" to "Alice"))
val bob = ops.createVertex("Person", mapOf("name" to "Bob"))
ops.createEdge(alice.id, bob.id, "KNOWS", mapOf("since" to 2026))

My default split is simple: Neo4j for production, TinkerGraph for tests. Add Memgraph when low-latency writes matter, AGE when PostgreSQL consolidation matters, and FalkorDB when Redis is already the operational center.

Comments

Leave a note or reaction with your GitHub account.