A Go library providing utilities and abstractions for working with ArangoDB.
- Features
- Installation
- Quick Start
- Main Components
- Testing with TestArango
- Query Package
- Collection Package
- Command Line Integration
- Advanced Usage
- License
- Connection management and session handling
- Database operations (create, find, drop)
- Collection operations (create, find, truncate)
- Query execution with support for parameters
- Result set handling
- Transaction support with ACID guarantees
- Index management (geo, hash, persistent, skiplist)
- Graph operations
- User management with access control
go get github.com/dictyBase/arangomanager
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/dictyBase/arangomanager"
)
func main() {
// Connect to ArangoDB
connParams := &arangomanager.ConnectParams{
User: "root",
Pass: "password",
Database: "mydb",
Host: "localhost",
Port: 8529,
Istls: false,
}
// Create session and database connection
session, db, err := arangomanager.NewSessionDb(connParams)
if err != nil {
log.Fatalf("failed to connect: %s", err)
}
// Execute a query
query := "FOR u IN users FILTER u.name == @name RETURN u"
bindVars := map[string]interface{}{
"name": "John",
}
result, err := db.GetRow(query, bindVars)
if err != nil {
log.Fatalf("query failed: %s", err)
}
if !result.IsEmpty() {
var user struct {
Name string `json:"name"`
Email string `json:"email"`
}
if err := result.Read(&user); err != nil {
log.Fatalf("failed to read result: %s", err)
}
fmt.Printf("User: %s, Email: %s\n", user.Name, user.Email)
}
// Execute operations in a transaction for ACID compliance
txOptions := &arangomanager.TransactionOptions{
WriteCollections: []string{"users"},
}
tx, err := db.BeginTransaction(context.Background(), txOptions)
if err != nil {
log.Fatalf("failed to begin transaction: %s", err)
}
// Execute a transaction operation
updateQuery := "UPDATE { _key: @key } WITH { lastLogin: @time } IN users"
txBindVars := map[string]interface{}{
"key": "user123",
"time": time.Now(),
}
if err := tx.Do(updateQuery, txBindVars); err != nil {
tx.Abort()
log.Fatalf("transaction operation failed: %s", err)
}
// Commit the transaction
if err := tx.Commit(); err != nil {
log.Fatalf("failed to commit transaction: %s", err)
}
}
The Session
type manages the connection to the ArangoDB server:
// Create a new session
session, err := arangomanager.Connect(host, user, password, port, isTLS)
// Get a database instance
db, err := session.DB("myDatabase")
// Create a new database
err := session.CreateDB("newDatabase", nil)
The Database
type provides methods for interacting with an ArangoDB database:
// Execute a query returning multiple rows
rs, err := db.SearchRows(query, bindVars)
// Execute a query returning a single row
row, err := db.GetRow(query, bindVars)
// Count results from a query
count, err := db.CountWithParams(query, bindVars)
// Execute a modification query
err := db.Do(query, bindVars)
// Create a collection
coll, err := db.CreateCollection("myCollection", nil)
// Find or create a collection
coll, err := db.FindOrCreateCollection("myCollection", nil)
// Create indices
idx, created, err := db.EnsureHashIndex("myCollection", []string{"field1"}, opts)
// Begin a transaction
txOptions := &arangomanager.TransactionOptions{
ReadCollections: []string{"users"},
WriteCollections: []string{"orders"},
}
tx, err := db.BeginTransaction(context.Background(), txOptions)
The Resultset
type handles query results with multiple rows:
rs, err := db.SearchRows(query, bindVars)
if err != nil {
// handle error
}
defer rs.Close()
// Check if result is empty
if rs.IsEmpty() {
// handle empty result
}
// Iterate through results
for rs.Scan() {
var item MyType
if err := rs.Read(&item); err != nil {
// handle error
}
// process item
}
The Result
type handles query results with a single row:
res, err := db.GetRow(query, bindVars)
if err != nil {
// handle error
}
// Check if result is empty
if res.IsEmpty() {
// handle empty result
}
// Read data into struct
var item MyType
if err := res.Read(&item); err != nil {
// handle error
}
The TransactionHandler
type provides methods for working with ArangoDB transactions for ACID-compliant operations:
// Configure transaction options
txOptions := &arangomanager.TransactionOptions{
ReadCollections: []string{"users"},
WriteCollections: []string{"orders"},
}
// Begin a transaction
tx, err := db.BeginTransaction(context.Background(), txOptions)
if err != nil {
// handle error
}
// Execute a write operation within the transaction
writeQuery := "INSERT { username: @username, created_at: @timestamp } INTO users"
bindVars := map[string]interface{}{
"username": "johndoe",
"timestamp": time.Now(),
}
if err := tx.Do(writeQuery, bindVars); err != nil {
tx.Abort() // abort on error
// handle error
}
// Execute a query and get results within the transaction
readQuery := "FOR o IN orders FILTER o.user_id == @user_id RETURN o"
readBindVars := map[string]interface{}{"user_id": 123}
result, err := tx.DoRun(readQuery, readBindVars)
if err != nil {
tx.Abort() // abort on error
// handle error
}
// Process results within the transaction context
// ...
// Commit the transaction when done
if err := tx.Commit(); err != nil {
// handle commit error
}
Transaction Features:
- ACID-compliant transactions (Atomicity, Consistency, Isolation, Durability)
- Access to transaction ID and status information
- Transaction-bound query execution
- Automatic transaction context handling
- Explicit transaction commit and abort operations
The testarango
package provides utilities for writing tests against ArangoDB
without affecting your production databases.
- Creates isolated, disposable test databases for your tests
- Automatically cleans up after tests complete
- Configurable via environment variables or direct parameter passing
- Works with any running ArangoDB instance
Set up the test environment in your TestMain
:
package mypackage
import (
"log"
"os"
"testing"
"github.com/dictyBase/arangomanager/testarango"
)
var testArangoDB *testarango.TestArango
func TestMain(m *testing.M) {
// Create a test database
ta, err := testarango.NewTestArangoFromEnv(true)
if err != nil {
log.Fatalf("failed to connect to test database: %s", err)
}
testArangoDB = ta
// Run tests
code := m.Run()
// Clean up the test database
db, err := ta.DB(ta.Database)
if err != nil {
log.Fatalf("error getting database: %s", err)
}
if err := db.Drop(); err != nil {
log.Fatalf("error dropping database: %s", err)
}
os.Exit(code)
}
func TestSomething(t *testing.T) {
// Use the test database
session, db, err := arangomanager.NewSessionDb(&arangomanager.ConnectParams{
User: testArangoDB.User,
Pass: testArangoDB.Pass,
Host: testArangoDB.Host,
Port: testArangoDB.Port,
Database: testArangoDB.Database,
})
// Run your tests with session and db
// ...
}
- A running ArangoDB instance
- An existing user with administrative privileges (to create test databases)
- Environment variables set:
ARANGO_HOST
: ArangoDB hostARANGO_USER
: ArangoDB usernameARANGO_PASS
: ArangoDB passwordARANGO_PORT
: (Optional) ArangoDB port, defaults to 8529
The query
package provides powerful utilities for building ArangoDB Query Language (AQL) filter statements, especially for handling complex filtering requirements with logical operations.
- Parse filter strings into structured Filter objects
- Generate AQL filter statements with support for:
- Standard comparison operators (
==
,!=
,>
,<
,>=
,<=
) - String pattern matching (
=~
,!~
) - Date comparison operators (prefixed with
$
, e.g.,$==
,$>
) - Array operation operators (prefixed with
@
, e.g.,@==
,@=~
) - Complex logical expressions with AND/OR operations
- Standard comparison operators (
// Parse a filter string with multiple conditions
// Format: field operator value[logic]
// where logic is "," for OR and ";" for AND
filters, err := query.ParseFilterString("created_at$>=2023-01-01;status==active,status==pending")
if err != nil {
// handle error
}
// Create a field mapping for the query
fieldMap := map[string]string{
"created_at": "doc.created_at",
"status": "doc.status",
}
// Generate AQL filter statement with qualified field names
aqlStatement, err := query.GenQualifiedAQLFilterStatement(fieldMap, filters)
if err != nil {
// handle error
}
// Use the filter in your AQL query
queryString := fmt.Sprintf(`
FOR doc IN collection
%s
RETURN doc
`, aqlStatement)
// Execute the query with the database
resultset, err := db.SearchRows(queryString, nil)
// For more control, use StatementParameters
params := &query.StatementParameters{
Fmap: fieldMap,
Filters: filters,
Doc: "doc", // document variable name in FOR loop
}
// Generate AQL filter statement
aqlStatement, err := query.GenAQLFilterStatement(params)
if err != nil {
// handle error
}
Type | Operators | Example |
---|---|---|
Standard | == , != , > , < , >= , <= |
status==active |
String | =~ (contains), !~ (not contains) |
name=~John |
Date | $== , $> , $< , $>= , $<= |
created_at$>=2023-01-01 |
Array | @== , @!= , @=~ , @!~ |
tags@==important |
- Use
,
between conditions for OR logic - Use
;
between conditions for AND logic
Example:
status==active;created_at$>=2023-01,created_at$<=2023-12
This translates to: (status equals "active") AND ((created_at >= 2023-01) OR (created_at <= 2023-12))
The collection
package provides functional programming utilities for working
with slices and collections in Go. These utilities enable more expressive and
concise code when manipulating data.
- Generic collection functions with full type safety
- Functional programming patterns like map, filter, and reduce
- Support for sequences and iterators
- Curried functions for partial application and composition
- Tuple types for multi-value returns
// Transform a slice of strings to uppercase
names := []string{"john", "alice", "bob"}
upperNames := collection.Map(names, strings.ToUpper)
// Result: ["JOHN", "ALICE", "BOB"]
// Filter a slice to only include even numbers
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
evenNumbers := collection.Filter(numbers, func(n int) bool {
return n%2 == 0
})
// Result: [2, 4, 6, 8]
// Partition users by age (adults and minors)
users := []User{
{Name: "John", Age: 30},
{Name: "Alice", Age: 16},
{Name: "Bob", Age: 25},
{Name: "Emma", Age: 17},
}
isAdult := func(u User) bool { return u.Age >= 18 }
adults, minors := collection.Partition(users, isAdult)
// Create a pipeline of operations
result := collection.Pipe3(
inputData,
func(data []int) []int { return collection.Filter(data, isEven) },
func(data []int) []int { return collection.Map(data, multiply) },
func(data []int) int { return sum(data) },
)
// Create and use a tuple
userStats := collection.NewTuple2("active_users", 1250)
fmt.Printf("Metric: %s, Value: %d\n", userStats.First, userStats.Second)
- Map: Transform elements using a mapping function
- Filter: Select elements that match a predicate
- Partition: Split a collection based on a predicate
- Include: Check if an element exists in a collection
- Pipe2/3/4: Create functional pipelines
- MapSeq: Transform iterator sequences
- RemoveStringItems: Remove specific strings from a slice
- IsEmpty: Check if a collection is empty
- CurriedXXX: Curried versions of the above functions for partial application
The command/flag
package provides convenient CLI flag definitions for ArangoDB connections, making it easy to integrate ArangoManager with command-line applications built with urfave/cli.
- Pre-defined flag sets for ArangoDB connection parameters
- Environment variable support for all parameters
- Sensible defaults for common settings
- Compatible with urfave/cli command-line application framework
package main
import (
"fmt"
"log"
"os"
"github.com/dictyBase/arangomanager"
"github.com/dictyBase/arangomanager/command/flag"
"github.com/urfave/cli"
)
func main() {
app := cli.NewApp()
app.Name = "my-arango-app"
app.Usage = "Example application using ArangoDB"
// Add ArangoDB flags to your application
app.Flags = flag.ArangodbFlags()
app.Action = func(c *cli.Context) error {
// Parse connection parameters from CLI flags
connParams := &arangomanager.ConnectParams{
User: c.String("arangodb-user"),
Pass: c.String("arangodb-pass"),
Database: c.String("arangodb-database"),
Host: c.String("arangodb-host"),
Port: c.Int("arangodb-port"),
Istls: c.Bool("is-secure"),
}
// Connect to ArangoDB
session, db, err := arangomanager.NewSessionDb(connParams)
if err != nil {
return cli.NewExitError(fmt.Sprintf("failed to connect: %s", err), 1)
}
// Your application logic here...
fmt.Printf("Connected to database: %s\n", c.String("arangodb-database"))
return nil
}
if err := app.Run(os.Args); err != nil {
log.Fatal(err)
}
}
The package provides two main flag sets:
-
ArangoFlags() - Basic connection flags:
--arangodb-pass, --pass
(required): ArangoDB password, can be set viaARANGODB_PASS
env var--arangodb-user, --user
(required): ArangoDB username, can be set viaARANGODB_USER
env var--arangodb-host, --host
(default: "arangodb"): ArangoDB host, can be set viaARANGODB_SERVICE_HOST
env var--arangodb-port
(default: "8529"): ArangoDB port, can be set viaARANGODB_SERVICE_PORT
env var--is-secure
: Flag for secured endpoint
-
ArangodbFlags() - Extended flags that include all basic flags plus:
--arangodb-database, --db
(required): ArangoDB database name, can be set viaARANGODB_DATABASE
env var- Sets
--is-secure
to true by default
See the GoDoc for full API documentation.
BSD-2-Clause