arangomanager

package module
v0.8.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 21, 2025 License: BSD-2-Clause Imports: 20 Imported by: 1

README

ArangoManager

License
Continuous integration GoDoc codecov

A Go library providing utilities and abstractions for working with ArangoDB.

Table of Contents

Features

  • 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

Installation

go get github.com/dictyBase/arangomanager

Quick Start

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)
    }
}

Main Components

Session

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)
Database

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)
ResultSet

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
}
Result

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
}
Transaction

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

Testing with TestArango

The testarango package provides utilities for writing tests against ArangoDB without affecting your production databases.

Features
  • 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
Usage

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
    // ...
}
Requirements
  • A running ArangoDB instance
  • An existing user with administrative privileges (to create test databases)
  • Environment variables set:
    • ARANGO_HOST: ArangoDB host
    • ARANGO_USER: ArangoDB username
    • ARANGO_PASS: ArangoDB password
    • ARANGO_PORT: (Optional) ArangoDB port, defaults to 8529

Query Package

The query package provides powerful utilities for building ArangoDB Query Language (AQL) filter statements, especially for handling complex filtering requirements with logical operations.

Features
  • 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
Usage
Parsing Filter Strings
// 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
}
Generating AQL Filter Statements
// 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)
Using Statement Parameters
// 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
}
Supported Operators
Type Operators Example
Standard ==, !=, >, <, >=, <= status==active
String =~ (contains), !~ (not contains) name=~John
Date $==, $>, $<, $>=, $<= created_at$>=2023-01-01
Array @==, @!=, @=~, @!~ tags@==important
Logical Operations
  • 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))

Collection Package

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.

Features
  • 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
Usage Examples
Transforming Data with Map
// Transform a slice of strings to uppercase
names := []string{"john", "alice", "bob"}
upperNames := collection.Map(names, strings.ToUpper)
// Result: ["JOHN", "ALICE", "BOB"]
Filtering Data
// 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]
Partitioning Data
// 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)
Function Composition with Pipe
// 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) },
)
Using Tuples for Multi-Value Returns
// Create and use a tuple
userStats := collection.NewTuple2("active_users", 1250)
fmt.Printf("Metric: %s, Value: %d\n", userStats.First, userStats.Second)
Available Functions
  • 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

Command Line Integration

Flag Package

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.

Features
  • 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
Usage
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)
    }
}
Available Flag Sets

The package provides two main flag sets:

  1. ArangoFlags() - Basic connection flags:

    • --arangodb-pass, --pass (required): ArangoDB password, can be set via ARANGODB_PASS env var
    • --arangodb-user, --user (required): ArangoDB username, can be set via ARANGODB_USER env var
    • --arangodb-host, --host (default: "arangodb"): ArangoDB host, can be set via ARANGODB_SERVICE_HOST env var
    • --arangodb-port (default: "8529"): ArangoDB port, can be set via ARANGODB_SERVICE_PORT env var
    • --is-secure: Flag for secured endpoint
  2. ArangodbFlags() - Extended flags that include all basic flags plus:

    • --arangodb-database, --db (required): ArangoDB database name, can be set via ARANGODB_DATABASE env var
    • Sets --is-secure to true by default

Advanced Usage

See the GoDoc for full API documentation.

License

BSD-2-Clause

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func FixedLenRandomString added in v0.4.0

func FixedLenRandomString(length int) string

func NewSessionDb

func NewSessionDb(connP *ConnectParams) (*Session, *Database, error)

NewSessionDb connects to arangodb and returns a new session and database instances.

func RandomInt added in v0.4.0

func RandomInt(num int) (int, error)

Generate a random number using crypto/rand.

func RandomString added in v0.4.0

func RandomString(min, max int) string

Generates a random string between a range(min and max) of length.

Types

type ConnectParams

type ConnectParams struct {
	User     string `validate:"required"`
	Pass     string `validate:"required"`
	Database string `validate:"required"`
	Host     string `validate:"required"`
	Port     int    `validate:"required"`
	Istls    bool
}

ConnectParams are the parameters required for connecting to arangodb.

type Database

type Database struct {
	// contains filtered or unexported fields
}

Database struct.

func (*Database) BeginTransaction added in v0.8.0

func (d *Database) BeginTransaction(
	ctx context.Context,
	opts *TransactionOptions,
) (*TransactionHandler, error)

func (*Database) Collection

func (d *Database) Collection(name string) (driver.Collection, error)

Collection returns collection attached to current database.

func (*Database) Count

func (d *Database) Count(query string) (int64, error)

Count query the database that is expected to return count of result.

func (*Database) CountWithParams

func (d *Database) CountWithParams(
	query string,
	bindVars map[string]interface{},
) (int64, error)

CountWithParams query the database with bind parameters that is expected to return count of result.

func (*Database) CreateCollection

func (d *Database) CreateCollection(
	name string,
	opt *driver.CreateCollectionOptions,
) (driver.Collection, error)

CreateCollection creates a collection in the database.

func (*Database) Do

func (d *Database) Do(query string, bindVars map[string]interface{}) error

Do is to run data modification query with bind parameters that is not expected to return any result.

func (*Database) DoRun

func (d *Database) DoRun(
	query string,
	bindVars map[string]interface{},
) (*Result, error)

DoRun is to run data modification query with bind parameters that is expected to return a result. It is an alias for GetRow.

func (*Database) Drop

func (d *Database) Drop() error

Drop removes the database.

func (*Database) EnsureGeoIndex added in v0.2.0

func (d *Database) EnsureGeoIndex(
	coll string, fields []string,
	opts *driver.EnsureGeoIndexOptions,
) (driver.Index, bool, error)

EnsureGeoIndex finds or creates a geo index on a specified collection.

func (*Database) EnsureHashIndex added in v0.2.0

func (d *Database) EnsureHashIndex(
	coll string, fields []string,
	opts *driver.EnsureHashIndexOptions,
) (driver.Index, bool, error)

EnsureHashIndex finds or creates a hash index on a specified collection.

func (*Database) EnsurePersistentIndex added in v0.2.0

func (d *Database) EnsurePersistentIndex(
	coll string, fields []string,
	opts *driver.EnsurePersistentIndexOptions,
) (driver.Index, bool, error)

EnsurePersistentIndex finds or creates a persistent index on a specified collection.

func (*Database) EnsureSkipListIndex added in v0.2.0

func (d *Database) EnsureSkipListIndex(
	coll string, fields []string,
	opts *driver.EnsureSkipListIndexOptions,
) (driver.Index, bool, error)

EnsureSkipListIndex finds or creates a skip list index on a specified collection.

func (*Database) Exec

func (d *Database) Exec(query string) error

Exec is to run data modification query that is not expected to return any result.

func (*Database) FindOrCreateCollection

func (d *Database) FindOrCreateCollection(
	name string,
	opt *driver.CreateCollectionOptions,
) (driver.Collection, error)

FindOrCreateCollection finds or creates a collection in the database. The method is expected to be called by the user who has privileges to create the collection.

func (*Database) FindOrCreateGraph

func (d *Database) FindOrCreateGraph(
	name string,
	defs []driver.EdgeDefinition,
) (driver.Graph, error)

FindOrCreateGraph finds or creates a named graph in the database.

func (*Database) Get

func (d *Database) Get(query string) (*Result, error)

Get query the database to return single row of result.

func (*Database) GetRow

func (d *Database) GetRow(
	query string,
	bindVars map[string]interface{},
) (*Result, error)

GetRow query the database with bind parameters that is expected to return single row of result.

func (*Database) Handler

func (d *Database) Handler() driver.Database

Handler returns the raw arangodb database handler.

func (*Database) Run

func (d *Database) Run(query string) (*Result, error)

Run is to run data modification query that is expected to return a result It is a convenient alias for Get method.

func (*Database) Search

func (d *Database) Search(query string) (*Resultset, error)

Search query the database that is expected to return multiple rows of result.

func (*Database) SearchRows

func (d *Database) SearchRows(
	query string,
	bindVars map[string]interface{},
) (*Resultset, error)

SearchRows query the database with bind parameters that is expected to return multiple rows of result.

func (*Database) Truncate added in v0.3.0

func (d *Database) Truncate(names ...string) error

Truncate removes all data from the collections without touching the indexes.

func (*Database) ValidateQ

func (d *Database) ValidateQ(q string) error

ValidateQ validates the query.

type DocExistsParams added in v0.8.0

type DocExistsParams struct {
	T           *testing.T
	DB          *Database
	Coll        driver.Collection
	FirstName   string
	LastName    string
	ShouldExist bool
}

DocExistsParams defines parameters for checking document existence

type DocParams added in v0.8.0

type DocParams struct {
	T         *testing.T
	TX        *TransactionHandler
	Coll      driver.Collection
	FirstName string
	LastName  string
}

DocParams defines parameters for document operations

type Result

type Result struct {
	// contains filtered or unexported fields
}

Result is a cursor for single row of data.

func (*Result) IsEmpty

func (r *Result) IsEmpty() bool

IsEmpty checks for empty result.

func (*Result) Read

func (r *Result) Read(iface interface{}) error

Read read the row of data to i interface.

type Resultset

type Resultset struct {
	// contains filtered or unexported fields
}

Resultset is a cursor for multiple rows of result.

func (*Resultset) Close

func (r *Resultset) Close() error

Close closes the resultset and releases resources. If the resultset is empty (r.empty is true), the cursor is nil and no closing operation is needed.

func (*Resultset) IsEmpty

func (r *Resultset) IsEmpty() bool

IsEmpty checks for empty resultset.

func (*Resultset) Read

func (r *Resultset) Read(iface interface{}) error

Read reads the row of data to interface i. Returns an error if the resultset is empty.

func (*Resultset) Scan

func (r *Resultset) Scan() bool

Scan advances resultset to the next row of data.

type Session

type Session struct {
	// contains filtered or unexported fields
}

Session is a connected database client.

func Connect

func Connect(
	host, user, password string,
	port int,
	istls bool,
) (*Session, error)

Connect is a constructor for new client.

func NewSessionFromClient

func NewSessionFromClient(client driver.Client) *Session

NewSessionFromClient creates a new Session from an existing client

 You could also do this
    &Session{client}
Funny isn't it

func (*Session) CreateDB

func (s *Session) CreateDB(
	name string,
	opt *driver.CreateDatabaseOptions,
) error

CreateDB creates database.

func (*Session) CreateUser

func (s *Session) CreateUser(user, pass string) error

CreateUser creates user.

func (*Session) CurrentDB

func (s *Session) CurrentDB() (*Database, error)

CurrentDB gets the default database(_system).

func (*Session) DB

func (s *Session) DB(name string) (*Database, error)

DB gets the database.

func (*Session) GrantDB

func (s *Session) GrantDB(database, user, grant string) error

GrantDB grants user permission to a database.

type TransactionHandler added in v0.8.0

type TransactionHandler struct {
	// contains filtered or unexported fields
}

TransactionHandler represents a transaction with begin/commit/abort capabilities

func (*TransactionHandler) Abort added in v0.8.0

func (t *TransactionHandler) Abort() error

Abort aborts the transaction

func (*TransactionHandler) Commit added in v0.8.0

func (t *TransactionHandler) Commit() error

Commit commits the transaction

func (*TransactionHandler) Context added in v0.8.0

func (t *TransactionHandler) Context() context.Context

Context returns the transaction context which should be used for all operations within the transaction

func (*TransactionHandler) Do added in v0.8.0

func (t *TransactionHandler) Do(
	query string,
	bindVars map[string]interface{},
) error

Do executes a query within the transaction.

func (*TransactionHandler) DoRun added in v0.8.0

func (t *TransactionHandler) DoRun(
	query string,
	bindVars map[string]interface{},
) (*Result, error)

DoRunTransaction executes a query within a transaction that returns a result.

func (*TransactionHandler) ID added in v0.8.0

ID returns the transaction ID

func (*TransactionHandler) Status added in v0.8.0

Status retrieves the current status of the transaction

type TransactionOptions added in v0.8.0

type TransactionOptions struct {
	// ReadCollections is a list of collections that will be read during the transaction
	ReadCollections []string
	// WriteCollections is a list of collections that will be written to during the transaction
	WriteCollections []string
	// ExclusiveCollections is a list of collections that will be exclusively locked during the transaction
	ExclusiveCollections []string
	// WaitForSync if set to true, will force the transaction to write all data to disk before returning
	WaitForSync bool
	// AllowImplicit if set to true, allows reading from undeclared collections (only for Transaction)
	AllowImplicit bool
	// LockTimeout the timeout for waiting on collection locks (in seconds)
	LockTimeout int
	// MaxTransactionSize the maximum size of the transaction in bytes
	MaxTransactionSize int
}

TransactionOptions represents options for transaction operations

func DefaultTransactionOptions added in v0.8.0

func DefaultTransactionOptions() *TransactionOptions

DefaultTransactionOptions returns default options for transactions

type TxParams added in v0.8.0

type TxParams struct {
	T        *testing.T
	DB       *Database
	Coll     driver.Collection
	ReadOnly bool
}

TxParams defines parameters for creating a test transaction

Directories

Path Synopsis
command
Package testarango is a golang library to write unit tests for arangodb based packages and applications.
Package testarango is a golang library to write unit tests for arangodb based packages and applications.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL