Go Embedded
Run Traverse as an in-process graph database in Go applications.
Install
Download the Traverse Go package from truespar.com, then add a replace directive in your go.mod:
require github.com/truespar/traverse v0.6.2
replace github.com/truespar/traverse => ./traverse
Note: No CGo required. The Go bindings use syscall.LoadDLL on Windows and dlopen on Linux to load the native library at runtime. Set TRAVERSE_FFI_LIB to specify the library path, or place it alongside your executable.
Open a Database
package main
import (
"fmt"
"log"
"github.com/truespar/traverse"
)
func main() {
// Open or create a database
db, err := traverse.Open("mydb.tvdb")
if err != nil {
log.Fatal(err)
}
defer db.Close()
}
Run Queries
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
result, err := tx.Execute("MATCH (n:Person) RETURN n.name, n.age")
if err != nil {
tx.Abort()
log.Fatal(err)
}
for _, row := range result.Rows {
fmt.Println(row["n.name"], row["n.age"])
}
tx.Abort() // read-only, no changes to commit
Write Data
tx, err := db.Begin()
if err != nil {
log.Fatal(err)
}
_, err = tx.Execute("CREATE (p:Person {name: 'Alice', age: 30})")
if err != nil {
tx.Abort()
log.Fatal(err)
}
_, err = tx.Execute("CREATE (p:Person {name: 'Bob', age: 25})")
if err != nil {
tx.Abort()
log.Fatal(err)
}
if err := tx.Commit(); err != nil {
log.Fatal(err)
}
Note: You must call Commit() to persist changes. Call Abort() to discard them. Once committed or aborted, the transaction cannot be reused.
QueryResult
tx, _ := db.Begin()
result, _ := tx.Execute("MATCH (n:Person) RETURN n.name, n.age")
fmt.Println(result.Columns) // [n.name n.age]
fmt.Println(result.RowCount) // number of rows
// Rows are []map[string]interface{}
for _, row := range result.Rows {
name := row["n.name"].(string)
age := row["n.age"].(int64)
fmt.Printf("%s is %d years old\n", name, age)
}
// Node values
result, _ = tx.Execute("MATCH (n:Person) RETURN n LIMIT 1")
node := result.Rows[0]["n"].(traverse.Node)
fmt.Println(node.ID, node.Labels, node.Properties)
// Edge values
result, _ = tx.Execute("MATCH ()-[r:KNOWS]->() RETURN r LIMIT 1")
edge := result.Rows[0]["r"].(traverse.Edge)
fmt.Println(edge.Type, edge.Source, edge.Target)
tx.Abort()
Bulk Writer
The BulkWriter bypasses MVCC for high-throughput imports (2–5M rows/s).
writer, err := db.BulkWriter()
if err != nil {
log.Fatal(err)
}
nid1, err := writer.CreateNode(
[]string{"Person"},
map[string]string{"name": "Alice", "age": "30"},
)
if err != nil {
writer.Abort()
log.Fatal(err)
}
nid2, err := writer.CreateNode(
[]string{"Person"},
map[string]string{"name": "Bob", "age": "25"},
)
if err != nil {
writer.Abort()
log.Fatal(err)
}
_, err = writer.CreateEdge(nid1, nid2, "KNOWS", map[string]string{"since": "2024"})
if err != nil {
writer.Abort()
log.Fatal(err)
}
if err := writer.Commit(); err != nil {
log.Fatal(err)
}
Note: The Go FFI bulk writer uses map[string]string for properties — all values are passed as strings through the C FFI boundary.
Indexes
Create indexes via the bulk writer or Cypher:
// Via BulkWriter
writer, _ := db.BulkWriter()
writer.CreateIndex("idx_person_name", "Person", "name")
writer.CreateEdgeIndex("idx_knows_since", "KNOWS", "since")
writer.CreateEdgeIndex("idx_knows", "KNOWS", "") // type-only index (empty property)
writer.Commit()
// Via Cypher
tx, _ := db.Begin()
tx.Execute("CREATE INDEX idx_person_age FOR (n:Person) ON (n.age)")
tx.Commit()
Flush and Close
// Flush dirty pages to disk
if err := db.Flush(); err != nil {
log.Fatal(err)
}
// Close the database
if err := db.Close(); err != nil {
log.Fatal(err)
}
Note: Traverse is an in-memory database. Call Flush() to persist data to the .tvdb file. Data written since the last flush is lost if the process crashes.
Error Handling
db, err := traverse.Open("mydb.tvdb")
if err != nil {
// Check error type
if tvErr, ok := err.(*traverse.TraverseError); ok {
fmt.Println("Error code:", tvErr.Code)
fmt.Println("Message:", tvErr.Message)
}
}
Error codes include:
traverse.ErrIO // I/O error
traverse.ErrSyntax // Cypher syntax error
traverse.ErrType // Type mismatch
traverse.ErrSemantic // Semantic error
traverse.ErrGraph // Graph constraint violation
traverse.ErrTransaction // Transaction error
traverse.ErrInvalidArg // Invalid argument