Skip to content

Latest commit

 

History

55 Commits

Folders and files

Repository files navigation

Quickwit Go SDK

A Go SDK for the Quickwit search engine.

Compatibility: requires Quickwit 0.9.0 or later.

Installation

go get github.com/akbariandev/quickwit-gosdk

Features

Feature Status Documentation
Client setup (timeout, transport) ✅ Creating a Client
Index CRUD ✅ Index CRUD
Index update & describe ✅ Index Update & Describe
Ingest ✅ Ingest
Search ✅ Search
Count & multi-index search ✅ Count & Multi-Index Search
Scroll search ✅ Scroll Search
Result pagination ✅ Paginating Large Result Sets
Delete by query ✅ Delete by Query
Sources management ✅ Sources
Splits ✅ Splits
Index templates ✅ Index Templates
Error handling ✅ Error Handling

Usage

Creating a Client

import "github.com/akbariandev/quickwit-gosdk/client"

// Basic
c := client.New("http://localhost:7280")

// With custom timeout
c := client.New("http://localhost:7280",
    client.WithTimeout(30 * time.Second),
)

// With custom transport (e.g. for custom TLS or an auth-injecting reverse proxy)
c := client.New("http://localhost:7280",
    client.WithTransport(customTransport),
)

Index CRUD

import (
    "encoding/json"

    "github.com/akbariandev/quickwit-gosdk/index"
    "github.com/akbariandev/quickwit-gosdk/types"
)

// Create — version, index_id, and doc_mapping are required.
metadata, err := index.Create(c, index.CreateRequest{
    Version: "0.9",
    IndexID: "my-index",
    DocMapping: types.DocMapping{
        Mode: "lenient",
        FieldMappings: []types.FieldMapping{
            {Name: "title", Type: "text", Stored: true, Indexed: true},
            {Name: "body",  Type: "text", Stored: true, Indexed: true},
            {Name: "ts",    Type: "datetime", Stored: true, Indexed: true, Fast: &types.FastField{Enabled: true}},
        },
        TagFields:      []string{},
        TimestampField: "ts",
        StoreSource:    true,
    },
    // Optional fields below — omitted when empty:
    IndexingSettings: types.IndexingSettings{
        CommitTimeoutSecs:  60,
        SplitNumDocsTarget: 10000000,
        MergePolicy: &types.MergePolicy{
            Type:        "stable_log",
            MergeFactor: 10,
        },
        Resources: &types.IndexingResources{
            HeapSize: json.Number("2000000000"), // or "2 GB"
        },
    },
    IngestSettings: types.IngestSettings{
        MinShards:    1,
        ValidateDocs: true,
    },
    SearchSettings: types.SearchSettings{
        DefaultSearchFields: []string{"title", "body"},
    },
    Retention: &types.Retention{
        Period:   "90 days",
        Schedule: "daily",
    },
})

// Get
metadata, err := index.Get(c, "my-index")

// List all
indexes, err := index.List(c)

// Access typed index fields via Config:
//   metadata.Config.IndexID                      // "my-index"
//   metadata.Config.IndexURI                     // "file:///..."
//   metadata.Config.DocMapping.Mode              // "lenient"
//   metadata.Config.DocMapping.FieldMappings[0].Name  // "title"
//   metadata.Config.SearchSettings.DefaultSearchFields
//   metadata.IndexUID                            // "my-index:01XYZ"
//   metadata.CreateTimestamp.Time                // creation time
//   metadata.Sources[0].SourceID                 // "_ingest-source"

// Delete (dry run — returns affected splits)
splits, err := index.Delete(c, "my-index", true)

// Delete (for real — returns empty array)
splits, err = index.Delete(c, "my-index", false)

// Clear (remove all data, keep index)
err = index.Clear(c, "my-index")

Index Update & Describe

// Update replaces an index's configuration (fields that can be updated).
metadata, err = index.Update(c, "my-index", index.UpdateRequest{
    Version: "0.9",
    SearchSettings: types.SearchSettings{
        DefaultSearchFields: []string{"title", "body"},
    },
})

// Describe returns physical/statistical details for an index.
stats, err := index.Describe(c, "my-index")
fmt.Printf("docs=%d splits=%d bytes=%d\n", stats.NumDocuments, stats.NumSplits, stats.NumBytes)

Ingest

import "github.com/akbariandev/quickwit-gosdk/ingest"

// Send a batch of documents
resp, err := ingest.Send(c, "my-index", []interface{}{
    map[string]interface{}{"title": "doc1", "body": "hello world"},
    map[string]interface{}{"title": "doc2", "body": "quickwit sdk"},
})
fmt.Printf("Persisted: %d, Failed: %d\n", resp.NumPersisted, resp.NumFailed)

// Send from a reader (NDJSON)
reader := strings.NewReader(`{"title":"doc1"}
{"title":"doc2"}`)
resp, err := ingest.SendFromReader(c, "my-index", reader)

// Force merge
err = ingest.ForceMerge(c, "my-index")

Search

import (
    "github.com/akbariandev/quickwit-gosdk/search"
    "github.com/akbariandev/quickwit-gosdk/types"
)

resp, err := search.Do(c, "my-index", search.Request{
    Query:           "events:error",
    DefaultOperator: "AND",
    MaxHits:         10,
    SearchFields:    []string{"message", "title"},
    SortByField:     &types.SortByField{FieldName: "timestamp", Order: "desc"},
    SnippetFields:   &types.SnippetRequest{FieldName: "message", MaxNumCharsPerFragment: 200},
    TagFilters:      []string{"tag1:value1", "tag2:value2"},
    Filter:          "status_code >= 400",
    Format:          types.FormatPrettyJSON,
})

fmt.Printf("Found %d hits\n", resp.NumHits)
for _, hit := range resp.Hits {
    fmt.Printf("  %v\n", hit.Fields)
}

Count & Multi-Index Search

// Count returns only the number of matching documents.
n, err := search.Count(c, "my-index", search.CountRequest{Query: "status:5xx"})

// DoMulti searches across multiple targets: a comma-separated list or a
// wildcard pattern. The targets are passed through to Quickwit unencoded.
resp, err := search.DoMulti(c, "logs-*,events-*", search.Request{
    Query:   "error",
    MaxHits: 10,
})

Scroll Search

Scroll is enabled by setting ScrollTTLSecs on a regular search request:

scrollTTL := uint64(60) // seconds
resp, err := search.Do(c, "my-index", search.Request{
    Query:         "all logs",
    MaxHits:       1000,
    ScrollTTLSecs: &scrollTTL,
})

Paginating Large Result Sets

Since the stream endpoint was removed, paginate using start_offset/max_hits:

const pageSize = 100
offset := uint64(0)
for {
    resp, err := search.Do(c, "my-index", search.Request{
        Query:       "body:error",
        MaxHits:     pageSize,
        StartOffset: offset,
    })
    if err != nil {
        log.Fatal(err)
    }
    for _, hit := range resp.Hits {
        fmt.Printf("  %v\n", hit.Fields)
    }
    if len(resp.Hits) < pageSize {
        break // no more results
    }
    offset += pageSize
}

Delete by Query

import "github.com/akbariandev/quickwit-gosdk/delete"

// Submit a delete task
resp, err := delete.Submit(c, "my-index", delete.Request{
    Query:  "old_logs:true",
    Filter: "timestamp < 1704067200",
})

// Check task status
task, err := delete.GetTask(c, "my-index", resp.TaskID)
fmt.Printf("Status: %s\n", task.Status) // "running", "success", "error", "cancelled"

// List all delete tasks for an index
tasks, err := delete.ListTasks(c, "my-index")

Index Templates

Index templates automatically create indexes according to predefined rules when an ingest targets an index id matching one of the template's patterns.

import "github.com/akbariandev/quickwit-gosdk/templates"

tmpl, err := templates.Create(c, templates.Template{
    TemplateID:      "hdfs-dev",
    IndexIDPatterns: []string{"hdfs-dev-*"},
    Priority:        100,
    DocMapping: types.DocMapping{
        Mode: "lenient",
        FieldMappings: []types.FieldMapping{
            {Name: "body", Type: "text"},
        },
    },
})

// Get / List / Update / Delete
tmpl, err = templates.Get(c, "hdfs-dev")
all, err := templates.List(c)
tmpl, err = templates.Update(c, "hdfs-dev", templates.Template{Priority: 50})
err = templates.Delete(c, "hdfs-dev")

Sources

Sources define how documents are ingested into an index (Kafka, Kinesis, Pulsar, file, etc.). This package manages their lifecycle.

import "github.com/akbariandev/quickwit-gosdk/sources"

// Create a Kafka source
src, err := sources.Create(c, "my-index", sources.Config{
    SourceID:   "my-kafka-source",
    SourceType: "kafka",
    Params: map[string]interface{}{
        "topic": "my-topic",
        "client_params": map[string]interface{}{
            "bootstrap.servers": "localhost:9092",
        },
    },
})

// Update / Toggle / Reset checkpoint / Delete
src, err = sources.Update(c, "my-index", "my-kafka-source", sources.Config{
    SourceID: "my-kafka-source", SourceType: "kafka",
})
err = sources.Toggle(c, "my-index", "my-kafka-source", false) // disable
err = sources.ResetCheckpoint(c, "my-index", "my-kafka-source")
err = sources.Delete(c, "my-index", "my-kafka-source")

Sources attached to an index are also returned by index.Get via metadata.Sources.

Splits

A split is the atomic unit of storage and search in Quickwit. List the splits belonging to an index, optionally filtered by state and timestamp.

import "github.com/akbariandev/quickwit-gosdk/splits"

spls, err := splits.List(c, "my-index", splits.ListFilter{})

// Filter by state and time range
offset := 0
limit := 10
start := int64(1704067200)
spls, err = splits.List(c, "my-index", splits.ListFilter{
    Offset:          &offset,
    Limit:           &limit,
    SplitStates:     []string{"Published"},
    StartTimestamp:  &start,
})
for _, s := range spls {
    fmt.Printf("split %s: %d docs, state=%s\n", s.SplitID, s.NumDocs, s.SplitState)
}

Error Handling

All API errors are returned as *types.Error with the HTTP status code and Quickwit's error message:

import "github.com/akbariandev/quickwit-gosdk/types"

resp, err := search.Do(c, "my-index", search.Request{Query: "test"})
if err != nil {
    var apiErr *types.Error
    if errors.As(err, &apiErr) {
        fmt.Printf("Quickwit API error %d: %s\n", apiErr.StatusCode, apiErr.Message)
    }
    return
}

Contributing

Contributions are welcome! The simplest way to contribute:

  1. Fork the repository on GitHub.
  2. Clone your fork and create a feature branch from main:
    git clone https://github.com/<your-username>/quickwit-gosdk.git
    cd quickwit-gosdk
    git checkout -b feat/my-feature
  3. Make your changes and add tests for any new functionality.
  4. Verify everything passes locally:
    go build ./...
    go vet ./...
    go test ./...
  5. Commit and push to your fork.
  6. Open a pull request against main, describing what changed and why.

Please keep pull requests focused (one feature or fix per PR), follow the existing code style, and update the README where relevant.

Releases

Packages

Used by

Contributors

Languages