A Go SDK for the Quickwit search engine.
Compatibility: requires Quickwit 0.9.0 or later.
go get github.com/akbariandev/quickwit-gosdk| 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 |
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),
)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")// 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)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")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 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 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,
})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
}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 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 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.
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)
}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
}Contributions are welcome! The simplest way to contribute:
- Fork the repository on GitHub.
- 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
- Make your changes and add tests for any new functionality.
- Verify everything passes locally:
go build ./... go vet ./... go test ./... - Commit and push to your fork.
- 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.