Go Client

Official SDK v0.1.0 Go 1.18+

Getting Started

Installation

go get github.com/solisoft/solidb-go-client

Requirements: Go 1.18 or higher. The client uses msgpack/v5 for binary serialization.

Quick Start

package main

import (
    "fmt"
    "github.com/solisoft/solidb-go-client/solidb"
)

func main() {
    // Create client instance
    client := solidb.NewClient("127.0.0.1", 6745)

    // Establish connection
    if err := client.Connect(); err != nil {
        panic(err)
    }
    defer client.Close()

    // Authenticate
    if err := client.Auth("_system", "admin", "password"); err != nil {
        panic(err)
    }

    // Set database context (required for sub-clients)
    client.UseDatabase("mydb")

    // Basic CRUD operations
    doc, _ := client.Insert("mydb", "users", map[string]interface{}{
        "name": "Alice",
        "age":  30,
    }, nil)
    fmt.Println("Created:", doc["_key"])

    user, _ := client.Get("mydb", "users", doc["_key"].(string))
    fmt.Println("Retrieved:", user["name"])

    client.Update("mydb", "users", doc["_key"].(string), map[string]interface{}{
        "age": 31,
    }, true)

    // Query with SDBQL
    results, _ := client.Query("mydb", "FOR u IN users FILTER u.age > @min RETURN u",
        map[string]interface{}{"min": 25})
    fmt.Printf("Found %d users\n", len(results))

    // Use management sub-clients
    scripts, _ := client.Scripts().List()
    triggers, _ := client.Triggers().List()
    fmt.Printf("Scripts: %d, Triggers: %d\n", len(scripts), len(triggers))
}

Connection Management

// Initialize with host and port
client := solidb.NewClient("127.0.0.1", 6745)

// Connect (establishes TCP socket with magic header handshake)
err := client.Connect()
if err != nil {
    log.Fatal("Connection failed:", err)
}

// Check connection health
err = client.Ping()
if err != nil {
    log.Println("Connection unhealthy:", err)
}

// Close connection when done
client.Close()
Method Returns Description
NewClient(host, port)*ClientCreate client instance
Connect()errorEstablish TCP connection
Ping()errorCheck connection health
Close()errorClose connection
UseDatabase(name)*ClientSet database context for sub-clients

Authentication

// Authenticate with database, username, and password
err := client.Auth("_system", "admin", "password")
if err != nil {
    log.Fatal("Authentication failed:", err)
}

// Authentication is required for most operations
// The session remains authenticated until disconnected

Core Operations

Database Operations

// List all databases
databases, err := client.ListDatabases()
// => []string{"_system", "mydb", "testdb"}

// Create a new database
err = client.CreateDatabase("analytics")

// Delete a database
err = client.DeleteDatabase("old_db")
Method Returns Description
ListDatabases()[]string, errorList all database names
CreateDatabase(name)errorCreate new database
DeleteDatabase(name)errorDelete database

Collection Operations

// List collections in a database
collections, err := client.ListCollections("mydb")
// => []string{"users", "orders", "products"}

// Create a document collection
err = client.CreateCollection("mydb", "products", nil)

// Create an edge collection (for graphs)
edgeType := "edge"
err = client.CreateCollection("mydb", "relationships", &edgeType)

// Delete a collection
err = client.DeleteCollection("mydb", "old_collection")
Method Returns Description
ListCollections(db)[]string, errorList collections in database
CreateCollection(db, name, *type)errorCreate collection (type: nil/edge)
DeleteCollection(db, name)errorDelete collection

Document Operations (CRUD)

// INSERT - Create a new document
doc, err := client.Insert("mydb", "users", map[string]interface{}{
    "name":  "Alice",
    "email": "[email protected]",
    "age":   30,
}, nil)
fmt.Println(doc["_key"])  // Auto-generated key

// INSERT with custom key
customKey := "custom-key-123"
doc, err = client.Insert("mydb", "users", map[string]interface{}{
    "name": "Bob",
}, &customKey)

// GET - Retrieve a document by key
user, err := client.Get("mydb", "users", "custom-key-123")
// => map[string]interface{}{"_key": "custom-key-123", "name": "Bob", ...}

// UPDATE - Modify a document (merge = true for partial update)
err = client.Update("mydb", "users", "custom-key-123", map[string]interface{}{
    "age": 25,
}, true)

// UPDATE - Replace entire document (merge = false)
err = client.Update("mydb", "users", "custom-key-123", map[string]interface{}{
    "name": "Robert",
}, false)

// DELETE - Remove a document
err = client.Delete("mydb", "users", "custom-key-123")

// LIST - Paginated document listing
docs, err := client.List("mydb", "users", 50, 0)  // limit: 50, offset: 0
Method Returns Description
Insert(db, col, doc, *key)map, errorInsert document, returns doc with _key
Get(db, col, key)map, errorGet document by key
Update(db, col, key, doc, merge)errorUpdate document (merge or replace)
Delete(db, col, key)errorDelete document
List(db, col, limit, offset)[]interface{}, errorList documents with pagination

SDBQL Queries

// Simple query
users, err := client.Query("mydb", "FOR u IN users RETURN u", nil)

// Query with bind variables (recommended for security)
results, err := client.Query("mydb", `
    FOR u IN users
    FILTER u.age >= @min_age AND u.status == @status
    SORT u.created_at DESC
    LIMIT @limit
    RETURN { name: u.name, email: u.email }
`, map[string]interface{}{
    "min_age": 18,
    "status":  "active",
    "limit":   100,
})

// Aggregation query
stats, err := client.Query("mydb", `
    FOR u IN users
    COLLECT status = u.status WITH COUNT INTO count
    RETURN { status, count }
`, nil)

// Join query
orders, err := client.Query("mydb", `
    FOR o IN orders
    FOR u IN users FILTER u._key == o.user_id
    RETURN { order: o, user: u.name }
`, nil)

ACID Transactions

// Begin a transaction
isolation := "read_committed"
txID, err := client.BeginTransaction("mydb", &isolation)
// Isolation levels: read_uncommitted, read_committed, repeatable_read, serializable

if err != nil {
    log.Fatal(err)
}

// Perform operations within transaction
_, err = client.Insert("mydb", "accounts", map[string]interface{}{
    "id": 1, "balance": 1000,
}, nil)

_, err = client.Insert("mydb", "accounts", map[string]interface{}{
    "id": 2, "balance": 500,
}, nil)

if err != nil {
    // Rollback on any error
    client.RollbackTransaction(txID)
    log.Println("Transaction rolled back:", err)
} else {
    // Commit if all operations succeed
    err = client.CommitTransaction(txID)
    if err != nil {
        log.Println("Commit failed:", err)
    } else {
        log.Println("Transaction committed successfully")
    }
}
Method Returns Description
BeginTransaction(db, *isolation)interface{}, errorStart transaction, returns tx_id
CommitTransaction(txID)errorCommit transaction
RollbackTransaction(txID)errorRollback transaction

Management Sub-Clients

Sub-clients provide namespaced access to management APIs. Important: Call UseDatabase(name) first to set the database context.

client.Scripts()

Lua Script Endpoints
client.UseDatabase("mydb")

// Create a Lua script endpoint
script, err := client.Scripts().Create(
    "hello",                        // name
    "/api/hello",                   // path
    []string{"GET", "POST"},        // methods
    `return { message = "Hello!" }`, // code
    nil,                            // description (optional)
    nil,                            // collection (optional)
)
fmt.Println("Created script:", script["_key"])

// List all scripts
scripts, err := client.Scripts().List()
for _, s := range scripts {
    sm := s.(map[string]interface{})
    fmt.Printf("%s -> %s\n", sm["name"], sm["path"])
}

// Get a specific script
script, err := client.Scripts().Get("script_key")

// Update script
err = client.Scripts().Update("script_key", map[string]interface{}{
    "code":    `return { message = "Updated!" }`,
    "methods": []string{"GET"},
})

// Delete a script
err = client.Scripts().Delete("script_key")

// Get execution statistics
stats, err := client.Scripts().GetStats()
Method Parameters Description
Createname, path, methods, code, *desc, *colCreate Lua endpoint
List()-List all scripts
Get(scriptID)scriptIDGet script details
Update(scriptID, updates)scriptID, mapUpdate script properties
Delete(scriptID)scriptIDDelete script
GetStats()-Execution statistics

client.Jobs() & client.Cron()

Background Processing
client.UseDatabase("mydb")

// === JOBS ===

// List all queues
queues, _ := client.Jobs().ListQueues()
// => [{"name": "default", "pending": 5, "running": 2}, ...]

// List jobs in a queue with filters
jobs, _ := client.Jobs().ListJobs(
    "default",  // queue name
    nil,        // status filter (pending, running, completed, failed)
    nil,        // limit
    nil,        // offset
)

// Enqueue a new job
job, _ := client.Jobs().Enqueue(
    "default",                              // queue name
    "/scripts/process-order",               // script path
    map[string]interface{}{"order_id": 123}, // params
    nil,                                    // priority (higher = more urgent)
    nil,                                    // run_at (ISO8601 for delayed)
)
fmt.Println("Job ID:", job["_key"])

// Get job details
job, _ = client.Jobs().GetJob("job_id")
fmt.Println("Status:", job["status"])

// Cancel a pending job
client.Jobs().Cancel("job_id")

// === CRON ===

// List scheduled jobs
crons, _ := client.Cron().List()

// Create a cron job
cron, _ := client.Cron().Create(
    "daily-cleanup",            // name
    "0 2 * * *",                // schedule (every day at 2 AM)
    "/scripts/cleanup",         // script path
    nil,                        // params
    nil,                        // description
)

// Update cron schedule
client.Cron().Update("cron_id", map[string]interface{}{
    "schedule": "0 3 * * *",    // change to 3 AM
})

// Delete cron job
client.Cron().Delete("cron_id")

client.Triggers()

Database Triggers
client.UseDatabase("mydb")

// List all triggers
triggers, _ := client.Triggers().List()

// List triggers for a specific collection
triggers, _ = client.Triggers().ListByCollection("users")

// Create a trigger
trigger, _ := client.Triggers().Create(
    "on_user_created",              // name
    "users",                        // collection
    "insert",                       // operation: insert, update, delete
    "/scripts/on-user-create",      // script path
    nil,                            // description
)

// Get trigger details
trigger, _ = client.Triggers().Get("trigger_id")

// Update trigger
client.Triggers().Update("trigger_id", map[string]interface{}{
    "script_path": "/scripts/new-handler",
    "enabled":     false,
})

// Toggle trigger on/off
client.Triggers().Toggle("trigger_id")

// Delete trigger
client.Triggers().Delete("trigger_id")
Event Description
insertFires on document creation
updateFires on document modification
deleteFires on document removal

client.Roles() & client.Users()

Role-Based Access Control
// === ROLES ===

// List all roles
roles, _ := client.Roles().List()

// Create a role with permissions
role, _ := client.Roles().Create(
    "editor",
    []map[string]interface{}{
        {"action": "read", "scope": "database", "database": "mydb"},
        {"action": "write", "scope": "collection", "database": "mydb", "collection": "articles"},
        {"action": "execute", "scope": "script", "database": "mydb"},
    },
    nil,  // description
)

// Get role details
role, _ = client.Roles().Get("editor")

// Update role permissions
client.Roles().Update("editor", []map[string]interface{}{
    {"action": "read", "scope": "database", "database": "mydb"},
    {"action": "write", "scope": "database", "database": "mydb"},
}, nil)

// Delete role
client.Roles().Delete("editor")

// === USERS ===

// List all users
users, _ := client.Users().List()

// Create a user
user, _ := client.Users().Create("john", "secure_password", nil)

// Get user's assigned roles
roles, _ := client.Users().GetRoles("john")

// Assign a role to user
client.Users().AssignRole("john", "editor")

// Revoke a role from user
client.Users().RevokeRole("john", "editor")

// Get current authenticated user
me, _ := client.Users().Me()

// Get current user's permissions
permissions, _ := client.Users().MyPermissions()

// Delete user
client.Users().Delete("john")
Action Scopes Description
readdatabase, collectionRead documents and query
writedatabase, collectionCreate, update, delete documents
admindatabase, collectionManage indexes, schema, etc.
executescriptExecute Lua scripts

client.ApiKeys()

API Key Management
// Create API key
key, _ := client.ApiKeys().Create(
    "my-api-key",             // name
    []string{"mydb"},         // databases
    nil,                      // expiration
)
fmt.Println("API Key:", key["key"])  // Save this! Only shown once

// List all API keys
keys, _ := client.ApiKeys().List()

// Delete API key
client.ApiKeys().Delete("key_id")

Advanced Features

client.Vector()

Vector Search & AI
client.UseDatabase("mydb")

// Create a vector index
metric := "cosine"  // cosine, euclidean, dot_product
idx, _ := client.Vector().CreateIndex(
    "products",             // collection
    "product_embeddings",   // index name
    "embedding",            // field
    1536,                   // dimensions
    &metric,
)

// Search by vector (semantic search)
embedding := getEmbedding("wireless headphones")  // Your embedding function
results, _ := client.Vector().Search(
    "products",
    embedding,
    10,     // limit
    nil,    // filter
)

for _, r := range results {
    rm := r.(map[string]interface{})
    doc := rm["doc"].(map[string]interface{})
    fmt.Printf("%s - Score: %v\n", doc["name"], rm["score"])
}

// Search by existing document (find similar)
similar, _ := client.Vector().SearchByDocument(
    "products",
    "product-123",      // doc key
    "embedding",        // field
    5,                  // limit
    nil,                // filter
)

// Quantize index (reduce memory usage)
client.Vector().Quantize("products", "product_embeddings", "binary")

// Dequantize (restore full precision)
client.Vector().Dequantize("products", "product_embeddings")

// List vector indexes
indexes, _ := client.Vector().ListIndexes("products")

// Delete index
client.Vector().DeleteIndex("products", "product_embeddings")

client.Geo()

Geospatial Queries
client.UseDatabase("mydb")

// Create a geo index
idx, _ := client.Geo().CreateIndex("stores", "location_idx", "location")

// Find nearby locations (radius search)
nearby, _ := client.Geo().Near(
    "stores",
    48.8566,    // latitude
    2.3522,     // longitude
    5000,       // radius in meters
    20,         // limit
)

for _, r := range nearby {
    rm := r.(map[string]interface{})
    doc := rm["doc"].(map[string]interface{})
    fmt.Printf("%s - %vm away\n", doc["name"], rm["distance"])
}

// Find within polygon
polygon := [][]float64{
    {48.8, 2.3}, {48.9, 2.4}, {48.85, 2.35}, {48.8, 2.3},
}
within, _ := client.Geo().Within("stores", polygon, nil)

// List geo indexes
indexes, _ := client.Geo().ListIndexes("stores")

// Delete index
client.Geo().DeleteIndex("stores", "location_idx")

client.TTL()

Time-To-Live Indexes
client.UseDatabase("mydb")

// Create TTL index (auto-expire documents after 1 hour)
idx, _ := client.TTL().CreateIndex(
    "sessions",
    "session_ttl",
    "created_at",       // DateTime field to check
    3600,               // expire after seconds
)

// Update expiration time
client.TTL().UpdateExpiration("sessions", "session_ttl", 7200)  // 2 hours

// Get index info
info, _ := client.TTL().GetIndexInfo("sessions", "session_ttl")
fmt.Printf("Expires after: %vs\n", info["expire_after_seconds"])

// Manually trigger cleanup (normally runs automatically)
client.TTL().RunCleanup("sessions")

// List TTL indexes
indexes, _ := client.TTL().ListIndexes("sessions")

// Delete TTL index
client.TTL().DeleteIndex("sessions", "session_ttl")

client.Columnar()

Columnar/Analytics Storage
client.UseDatabase("mydb")

// Create a columnar table (optimized for analytics)
table, _ := client.Columnar().Create("metrics", []map[string]interface{}{
    {"name": "timestamp", "type": "datetime"},
    {"name": "metric_name", "type": "string"},
    {"name": "value", "type": "float"},
    {"name": "tags", "type": "string"},
})

// Insert rows (batch insert is efficient)
client.Columnar().Insert("metrics", []map[string]interface{}{
    {"timestamp": "2024-01-15T10:00:00Z", "metric_name": "cpu_usage", "value": 45.2, "tags": "server1"},
    {"timestamp": "2024-01-15T10:01:00Z", "metric_name": "cpu_usage", "value": 47.8, "tags": "server1"},
})

// Query with SQL-like syntax
results, _ := client.Columnar().Query("metrics",
    "SELECT * FROM metrics WHERE value > @min ORDER BY timestamp DESC LIMIT 100",
    map[string]interface{}{"min": 40.0},
)

// Aggregation
agg, _ := client.Columnar().Aggregate("metrics", map[string]interface{}{
    "group_by": []string{"metric_name", "tags"},
    "metrics": []map[string]interface{}{
        {"column": "value", "function": "avg"},
        {"column": "value", "function": "max"},
        {"column": "value", "function": "count"},
    },
})

// Get table statistics
stats, _ := client.Columnar().Stats("metrics")
fmt.Printf("Row count: %v\n", stats["row_count"])

// Add a column
client.Columnar().AddColumn("metrics", "host", "string", nil)

// Drop a column
client.Columnar().DropColumn("metrics", "host")

// List all columnar tables
tables, _ := client.Columnar().List()

// Delete table
client.Columnar().Delete("metrics")

client.Cluster()

Cluster Management
// Get cluster status
status, _ := client.Cluster().Status()
fmt.Printf("Mode: %v\n", status["mode"])       // standalone, cluster
fmt.Printf("Nodes: %v\n", status["node_count"])

// Get detailed cluster info
info, _ := client.Cluster().Info()

// Remove a node from cluster
client.Cluster().RemoveNode("node-id-to-remove")

// Trigger data rebalancing
client.Cluster().Rebalance()

// Cleanup orphaned data
client.Cluster().Cleanup()

// Reshard cluster
client.Cluster().Reshard(nil)  // or specify new shard count

client.Collections()

Advanced Collection Operations
client.UseDatabase("mydb")

// Truncate collection (delete all documents)
client.Collections().Truncate("logs")

// Compact collection (reclaim disk space)
client.Collections().Compact("users")

// Repair collection (fix inconsistencies)
client.Collections().Repair("orders")

// Get collection statistics
stats, _ := client.Collections().Stats("users")

// Set JSON schema validation
client.Collections().SetSchema("users", map[string]interface{}{
    "type":     "object",
    "required": []string{"name", "email"},
    "properties": map[string]interface{}{
        "name":  map[string]interface{}{"type": "string", "minLength": 1},
        "email": map[string]interface{}{"type": "string", "format": "email"},
    },
})

// Get current schema
schema, _ := client.Collections().GetSchema("users")

// Remove schema validation
client.Collections().DeleteSchema("users")

// Export collection
data, _ := client.Collections().Export("users", nil)

// Import data
client.Collections().Import("users_backup", data, nil)

client.Env()

Environment Variables
client.UseDatabase("mydb")

// List environment variables (for Lua scripts)
vars, _ := client.Env().List()

// Set an environment variable
client.Env().Set("API_KEY", "sk-xxx-your-api-key")
client.Env().Set("WEBHOOK_URL", "https://example.com/webhook")

// Delete an environment variable
client.Env().Delete("OLD_VAR")

Error Handling

package main

import (
    "errors"
    "log"
    "github.com/solisoft/solidb-go-client/solidb"
)

func main() {
    client := solidb.NewClient("127.0.0.1", 6745)

    err := client.Connect()
    if err != nil {
        var connErr *solidb.ConnectionError
        if errors.As(err, &connErr) {
            log.Fatal("Connection failed:", connErr.Message)
        }
    }
    defer client.Close()

    err = client.Auth("mydb", "user", "password")
    if err != nil {
        var serverErr *solidb.ServerError
        if errors.As(err, &serverErr) {
            log.Println("Server error:", serverErr.Message)
        }
    }

    doc, err := client.Get("mydb", "users", "nonexistent-key")
    if err != nil {
        var serverErr *solidb.ServerError
        var protoErr *solidb.ProtocolError

        switch {
        case errors.As(err, &serverErr):
            log.Println("Document not found or other server error:", serverErr.Message)
        case errors.As(err, &protoErr):
            log.Println("Protocol error:", protoErr.Message)
        default:
            log.Println("Unknown error:", err)
        }
    }
}

ConnectionError

Network failures, connection refused, timeouts, disconnections

ServerError

Document not found, permission denied, validation errors

ProtocolError

Invalid response format, message too large, serialization issues