Quick Start With Golang
This quick start guide will walk you through the process of installing and using the ReductStore Go Client SDK to read and write data to a ReductStore instance.
Installing the Go SDK
To start using the ReductStore Go SDK, add the sdk to your go.mod
file:
go get github.com/reductstore/reduct-go
Then, import the reduct-go
package in your Go code:
import reduct "github.com/reductstore/reduct-go"
Running ReductStore
If you don't already have a ReductStore instance running, you can easily spin up one as a Docker container. To do this, run the following command:
docker run -p 8383:8383 -e RS_API_TOKEN="my-token" reduct/store:latest
This will start a ReductStore instance listening on port 8383 on your local machine. The RS_API_TOKEN
environment variable is used to authenticate requests to the ReductStore instance. You can set it to any value you like, but you will need to use the same value when creating a Client
object in your code.
If Docker is not an option, you can also download the ReductStore binaries. Check the Download Page.
Hello World Example
Now when you have the SDK installed and a ReductStore instance running, you can start using the SDK to interact with the ReductStore database. Here is an example of using the SDK to perform basic operations on a ReductStore instance:
package main
import (
"context"
reduct "github.com/reductstore/reduct-go"
model "github.com/reductstore/reduct-go/model"
"time"
)
func main() {
ctx := context.Background()
// 1. Create a ReductStore client
client := reduct.NewClient("http://localhost:8383", reduct.ClientOptions{
APIToken: "my-token",
})
// 2. Get or create a bucket with 1Gb quota
settings := model.NewBucketSettingBuilder().
WithQuotaType(model.QuotaTypeFifo).
WithQuotaSize(1_000_000_000).
Build()
bucket, err := client.CreateOrGetBucket(ctx, "my-bucket", &settings)
if err != nil {
panic(err)
}
// 3. Write some data with timestamps in the 'entry-1' entry
ts := time.Now().UnixMicro()
writer := bucket.BeginWrite(ctx, "entry-1",
&reduct.WriteOptions{Timestamp: ts, Labels: map[string]any{"score": 10}})
err = writer.Write("<Blob data>")
if err != nil {
panic(err)
}
writer = bucket.BeginWrite(ctx, "entry-1",
&reduct.WriteOptions{Timestamp: ts + 1, Labels: map[string]any{"score": 20}})
err = writer.Write("<Blob data 2>")
if err != nil {
panic(err)
}
// 4. Query the data by time range and condition
queryRequest := reduct.NewQueryOptionsBuilder().
WithStart(ts).
WithStop(ts + 2).
WithWhen(map[string]any{"&score": map[string]any{"$gt": 15}}).
Build()
query, err := bucket.Query(ctx, "entry-1", &queryRequest)
if err != nil {
panic(err)
}
for rec := range query.Records() {
data, err := rec.Read()
if err != nil {
panic(err)
}
timestamp := rec.Time()
labels := rec.Labels()
println("Record at time:", timestamp)
println("Labels:", labels)
println("Data:", string(data))
}
}
Let's break down what this example is doing.
Creating a Client
Before you can interact with a ReductStore instance, you must create a Client
object that represents a connection to the ReductStore instance.
To create a ReductStore client, you should use the reduct.NewClient
method from the reduct_go
package.
You must pass the URL of the ReductStore server as an argument and specify the client options using the reduct.ClientOptions
struct:
client := reduct.NewClient("http://localhost:8383", reduct.ClientOptions{
APIToken: "my-token",
})
Creating a Bucket
ReductStore organizes data into buckets, each of which has its own quota and settings. It's a necessary step to create a bucket before writing data to it. You can read more about buckets in the Buckets Guide, but for now, let's just create one.
To create a bucket, you can use the CreateOrGetBucket
or CreateBucket
methods on a Client
instance. The method takes the name of
the bucket you want to create as an argument and settings. The settings are optional, and you can customize them using the BucketSettingBuilder
struct as shown below:
settings := model.NewBucketSettingBuilder().
WithQuotaType(model.QuotaTypeFifo).
WithQuotaSize(1_000_000_000).
Build()
bucket, err := client.CreateOrGetBucket(ctx, "my-bucket", &settings)
if err != nil {
panic(err)
}
In this example we create a bucket with a FIFO quota of 1GB. This means that the oldest data will be deleted when the bucket reaches 1GB.
Data Ingestion
Time series data is stored in entries within a bucket. An entry is a collection of records with unique timestamps. It must have a unique name within the bucket and usually represents a data source, such as a vibration sensor or a CV camera.
To write a timestamped record to an entry in a bucket, you should use the BeginWrite
method on a Bucket
instance.
Pass the name of the entry you want to write to as an argument and options to specify the timestamp and labels for the record.
The method returns a WritableRecord
instance to write record content to the entry:
ts := time.Now().UnixMicro()
writer := bucket.BeginWrite(ctx, "entry-1",
&reduct.WriteOptions{Timestamp: ts, Labels: map[string]any{"score": 10}})
err = writer.Write("<Blob data>")
if err != nil {
panic(err)
}
writer = bucket.BeginWrite(ctx, "entry-1",
&reduct.WriteOptions{Timestamp: ts + 1, Labels: map[string]any{"score": 20}})
err = writer.Write("<Blob data 2>")
if err != nil {
panic(err)
}
This is the simplest case of writing data with the SDK. You can also stream data in chunks and annotate records with many labels. You can find more information and examples in the Data Ingestion Guide.
Data Querying
Usually, we don't read a particular record by its timestamp, but query records in a time range.
To iterate over all records within a specified time range, you should use the Query
method on a bucket instance. Pass the
name of the entry to iterate over and query options to specify the time range. Use the QueryBuilder
to build the query options.
You can also provide a condition to filter records based on labels using the WithWhen
method.
Read more about the query syntax in the Conditional Query Reference.
When the query is executed, it returns a QueryResult
object that you can iterate over to access the records:
queryRequest := reduct.NewQueryOptionsBuilder().
WithStart(ts).
WithStop(ts + 2).
WithWhen(map[string]any{"&score": map[string]any{"$gt": 15}}).
Build()
query, err := bucket.Query(ctx, "entry-1", &queryRequest)
if err != nil {
panic(err)
}
for rec := range query.Records() {
data, err := rec.Read()
if err != nil {
panic(err)
}
timestamp := rec.Time()
labels := rec.Labels()
println("Record at time:", timestamp)
println("Labels:", labels)
println("Data:", string(data))
}
}
The query method has many parameters for filtering and returning sample records. For more information and examples, see the Data Querying Guide.
Next Steps
As you can see to get started with the Client SDK is very easy. However,it doesn't cover all the features of the SDK and the database. Check our Guides to learn more about the ReductStore features and how to use them.