zoobzio January 6, 2025 Edit this page

Quickstart

Get type-safe storage working in under 5 minutes.

Requirements

  • Go 1.24 or higher

Installation

go get github.com/zoobz-io/grub

Install providers as needed:

# Key-value stores
go get github.com/zoobz-io/grub/redis
go get github.com/zoobz-io/grub/badger
go get github.com/zoobz-io/grub/bolt

# Blob storage
go get github.com/zoobz-io/grub/s3
go get github.com/zoobz-io/grub/minio
go get github.com/zoobz-io/grub/gcs
go get github.com/zoobz-io/grub/azure

# Vector search
go get github.com/zoobz-io/grub/qdrant
go get github.com/zoobz-io/grub/pinecone
go get github.com/zoobz-io/grub/milvus
go get github.com/zoobz-io/grub/weaviate

Basic Usage

Key-Value Store with Redis

package main

import (
    "context"
    "fmt"
    "time"

    "github.com/zoobz-io/grub"
    "github.com/zoobz-io/grub/redis"
    goredis "github.com/redis/go-redis/v9"
)

type Session struct {
    UserID    string `json:"user_id"`
    Token     string `json:"token"`
    ExpiresAt int64  `json:"expires_at"`
}

func main() {
    ctx := context.Background()

    // Connect to Redis
    client := goredis.NewClient(&goredis.Options{Addr: "localhost:6379"})
    defer client.Close()

    // Create type-safe store
    sessions := grub.NewStore[Session](redis.New(client))

    // Store with TTL
    session := &Session{
        UserID:    "user:123",
        Token:     "abc123",
        ExpiresAt: time.Now().Add(24 * time.Hour).Unix(),
    }
    _ = sessions.Set(ctx, "session:abc123", session, 24*time.Hour)

    // Retrieve
    s, _ := sessions.Get(ctx, "session:abc123")
    fmt.Println(s.UserID) // user:123
}

Embedded Store with BadgerDB

No external services required:

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/zoobz-io/grub"
    "github.com/zoobz-io/grub/badger"
    badgerdb "github.com/dgraph-io/badger/v4"
)

type Config struct {
    Debug   bool   `json:"debug"`
    Version string `json:"version"`
}

func main() {
    ctx := context.Background()

    // Open embedded database
    opts := badgerdb.DefaultOptions("./data")
    opts.Logger = nil // Silence logs
    db, _ := badgerdb.Open(opts)
    defer db.Close()

    // Create store
    configs := grub.NewStore[Config](badger.New(db))

    // Store (TTL=0 means no expiration)
    _ = configs.Set(ctx, "app:config", &Config{Debug: true, Version: "1.0"}, 0)

    // Retrieve
    cfg, _ := configs.Get(ctx, "app:config")
    fmt.Printf("Debug: %v, Version: %s\n", cfg.Debug, cfg.Version)

    // Cleanup
    os.RemoveAll("./data")
}

Blob Storage with S3

package main

import (
    "context"
    "fmt"

    "github.com/zoobz-io/grub"
    "github.com/zoobz-io/grub/s3"
    "github.com/aws/aws-sdk-go-v2/config"
    awss3 "github.com/aws/aws-sdk-go-v2/service/s3"
)

type Document struct {
    Title   string `json:"title"`
    Content string `json:"content"`
}

func main() {
    ctx := context.Background()

    // Configure AWS client
    cfg, _ := config.LoadDefaultConfig(ctx)
    client := awss3.NewFromConfig(cfg)

    // Create bucket wrapper
    docs := grub.NewBucket[Document](s3.New(client, "my-bucket"))

    // Store object
    _ = docs.Put(ctx, &grub.Object[Document]{
        Key:         "docs/readme.json",
        ContentType: "application/json",
        Data:        Document{Title: "README", Content: "Hello, world!"},
    })

    // Retrieve
    obj, _ := docs.Get(ctx, "docs/readme.json")
    fmt.Println(obj.Data.Title) // README
}

Vector Search with Qdrant

package main

import (
    "context"
    "fmt"

    "github.com/qdrant/go-client/qdrant"
    "github.com/zoobz-io/grub"
    grubqdrant "github.com/zoobz-io/grub/qdrant"
)

type Embedding struct {
    Category string `json:"category"`
    Source   string `json:"source"`
}

func main() {
    ctx := context.Background()

    // Connect to Qdrant
    client, _ := qdrant.NewClient(&qdrant.Config{
        Host: "localhost",
        Port: 6334,
    })
    defer client.Close()

    // Create type-safe index
    embeddings := grub.NewIndex[Embedding](grubqdrant.New(client, grubqdrant.Config{
        Collection: "documents",
    }))

    // Store vector with metadata
    vector := []float32{0.1, 0.2, 0.3}
    _ = embeddings.Upsert(ctx, "doc:1", vector, &Embedding{
        Category: "tech",
        Source:   "blog",
    })

    // Similarity search
    query := []float32{0.15, 0.25, 0.35}
    results, _ := embeddings.Search(ctx, query, 10, nil)
    for _, r := range results {
        fmt.Printf("ID: %s, Score: %f\n", r.ID, r.Score)
    }
}

What's Happening

  1. Provider wraps clientredis.New(client) creates a StoreProvider, qdrant.New(client) creates a VectorProvider
  2. Wrapper adds type safetygrub.NewStore[T], grub.NewBucket[T], grub.NewIndex[T] wrap providers
  3. Codec handles serialization — JSON by default, Gob available
  4. Operations return typed valuesGet returns *T, Search returns []*Vector[T]

Next Steps