Table Of Contents

7.8. Go

7.8.1. Introduction

This document is an introduction to the quasardb golang API. It introduces most quasardb api capabilities, thought, with a a strong focus on timeseries.

7.8.2. Requirements

7.8.3. Installation

For Go version >= 1.11

go mod was introduced in Go version 1.11 and above. It’ll help you manage the dependency of your project more easily. To use qdb-go-api with go mod, you only have to import qdb-api-go in one of your files:

import qdb "github.com/bureau14/qdb-api-go"

After your code is ready, run this commands to generate a dependency list and download your dependency:

go mod init
go get

Go will automatically get the latest api version for you. To specify the version, edit go.mod. For v3.2.0, it will look like the line below. Don’t forget to run go get again to download the new version:

require github.com/bureau14/qdb-api-go v3.2.0

Then you need to download and extract the c api into the thirdparty/quasardb folder. Make sure your GOPATH is set in your environment variables.

On a unix system:

wget http://path/to/qdb-$version-$system-c-api.tar.gz
tar xzf qdb-$version-$system-c-api.tar.gz -C $GOPATH/pkg/mod/github.com/bureau14/qdb-api-go/thirdparty/quasardb

On a windows system:

# Download qdb-$version-windows-$architecture-c-api.zip
unzip qdb-$version-windows-$architecture-c-api.zip $GOPATH\pkg\mod\github.com\bureau14\qdb-api-go\thirdparty\quasardb

For Go version <= 1.10

Download the go api through the go package manager, without installing it directly:

go get -d github.com/bureau14/qdb-api-go

Then you need to download and extract the c api into the thirdparty/quasardb folder. Make sure your GOPATH is set in your environment variables.

On a unix system:

wget http://path/to/qdb-$version-$system-c-api.tar.gz
tar xzf qdb-$version-$system-c-api.tar.gz -C $GOPATH/src/github.com/bureau14/qdb-api-go/thirdparty/quasardb

On a windows system:

# Download qdb-$version-windows-$architecture-c-api.zip
unzip qdb-$version-windows-$architecture-c-api.zip $GOPATH\src\github.com\bureau14\qdb-api-go\thirdparty\quasardb

7.8.4. Reference

The entire reference is available on godoc website.

7.8.5. Getting started

This guide will go through the basic calls to communicate with the quasardb daemon.

Import API

First, you have to import the api:

import qdb "github.com/bureau14/qdb-api-go"

Create a handle

Based on your intent and context there are different ways to create your handle.

You want to manage everything yourself:

handle, err := qdb.NewHandle()

// Set timeout
err = handle.SetTimeout(time.Duration(120) * time.Second)

// Set encryption if enabled server side
err = handle.SetEncryption(qdb.EncryptAES)

// add security if enabled server side
clusterKey, err := qdb.ClusterKeyFromFile("/path/to/cluster_public.key")
err = handle.AddClusterPublicKey(clusterKey)
user, secret, err := qdb.ClusterKeyFromFile("/path/to/cluster_public.key")
err = handle.AddUserCredentials(user, secret)

// connect
err = handle.Connect("qdb://127.0.0.1:2836)

You want to connect to an unsecured cluster:

handle, err := qdb.SetupHandle("qdb://127.0.0.1:2836", time.Duration(120) * time.Second)

or, the panic on fail alternative:

handle := qdb.MustSetupHandle("qdb://127.0.0.1:2836", time.Duration(120) * time.Second)

You want to connect to a secured cluster:

handle, err := qdb.SetupSecureHandle("qdb://127.0.0.1:2836", "/path/to/cluster_public.key", "/path/to/user_private.key", time.Duration(120) * time.Second, qdb.EncryptNone)

or, the panic on fail alternative:

handle := qdb.MustSetupSecureHandle("qdb://127.0.0.1:2836", "/path/to/cluster_public.key", "/path/to/user_private.key", time.Duration(120) * time.Second, qdb.EncryptNone)

Entry 101

An entry type is an expirable key/value that can be inserted/updated and removed from quasardb.

There are several entry types. We will introduce here blobs and integers.

You can put, get, update and remove an entry like so:

h := qdb.MustSetupHandle("qdb://127.0.0.1:2836", 120*time.Second)
defer h.Close()

alias := "BlobAlias"
blob := h.Blob(alias)
defer blob.Remove() // remove blob when we are finished with it

content := []byte("content")
blob.Put(content, qdb.NeverExpires())

obtainedContent, _ := blob.Get()
fmt.Println("value:", string(obtainedContent)) // prints "value: content"


updateContent := []byte("updated content")
blob.Update(updateContent, qdb.PreserveExpiration())

obtainedContent, _ = blob.Get()
fmt.Println("value:", string(obtainedContent)) // prints "value: updated content"

The same operations are available for integers, and another one the add operation:

h := qdb.MustSetupHandle("qdb://127.0.0.1:2836", 120*time.Second)
defer h.Close()

alias := "IntAlias"
integer := h.Integer(alias)

integer.Put(int64(3), qdb.NeverExpires())
defer integer.Remove()

obtainedContent, _ := integer.Get()
fmt.Println("value:", obtainedContent) // prints "value: 3"

newContent := int64(87)
integer.Update(newContent, qdb.NeverExpires())

obtainedContent, _ = integer.Get()
fmt.Println("value:", obtainedContent) // prints "value: 87"

integer.Add(3)
obtainedContent, _ = integer.Get()
fmt.Println("value:", obtainedContent) // prints "value: 90"

You can set the expiry time of an entry with a fixed point in time:

myEntry.ExpiresAt(time.Now().Add(1 * time.Hour))

or a duration:

myEntry.ExpiresFromNow(1 * time.Hour)

Two constants for never expires and preserve expiration are accessible with:

qdb.NeverExpires()

and for example with an update call:

blob.Update(updateContent, qdb.PreserveExpiration())

Timeseries 101

A timeseries is also an entry, but it needs its own section as they are quite a few operations you may need.

A timeseries, is column oriented timestamp based storage. A column can have four types: Blob, Int64, Double, and Timestamp Each value in each column is associated with a timestamp.

A timeseries organize itself in shards. A shard contains all values related to a specific range between two time points. It means that you need to define a shard size, the duration between the two aforementioned time points.

Create

To create a timeseries you need to define the columns of said timeseries.

You can define a column with TsColumnInfo, it has the type and the name of the column you wish to use or create:

handle := qdb.MustSetupHandle("qdb://127.0.0.1:2836", 120*time.Second)
defer handle.Close()

timeseries := handle.Timeseries("ts")
columnInfo := []qdb.TsColumnInfo{
        qdb.NewTsColumnInfo("blob_col", qdb.TsColumnBlob),
        qdb.NewTsColumnInfo("double_col", qdb.TsColumnDouble),
}
timeseries.Create(24*time.Hour, columnInfo...)

Here your shard size is 24*time.Hour.

Insert

We assume here that you already created a handle, and a timeseries object like you did before the Create call in the previous example.

You have two choices to insert into a timeseries, either you do it column-by-column:

blobColumn := timeseries.BlobColumn("blob_col")
doubleColumn := timeseries.DoubleColumn("double_col")

// insert one point on each column:
{
        now := time.Now()
        doublePoint := qdb.NewTsDoublePoint(now, 1.1)
        if err := doubleColumn.Insert(doublePoint); err != nil {
                // handle error
        }
        blobPoint := qdb.NewTsBlobPoint(now, []byte("abc"))
        if err := blobColumn.Insert(blobPoint); err != nil {
                // handle error
        }
}

// insert multiple points
{
        doublePoints := make([]qdb.TsDoublePoint, 2)
        doublePoints[0] = qdb.NewTsDoublePoint(time.Now(), 2.2)
        doublePoints[1] = qdb.NewTsDoublePoint(time.Now(), 3.3)
        if err := doubleColumn.Insert(doublePoints...); err != nil {
                // handle error
        }
}

Or you can use a batch table. As its name suggest, it is a table of multiple columns from one, or optionally, multiple timeseries in whatever order you may need. It pushes data in a batch fashion once you’re finished writing to it. It’s also row oriented.

To create it you just need the name of the timeseries and the column for each column of the batch table. The third parameter is a preallocation count if you have a good guess of the number of elements per shard:

tsBatch, err := handle.TsBatch(
        qdb.TsBatchColumnInfo{Timeseries: "ts", Column: "blob_col", ElementCountHint: 10},
        qdb.TsBatchColumnInfo{Timeseries: "ts", Column: "double_col", ElementCountHint: 10},
)
if err != nil {
        // handle error
}

Since it’s row oriented you need to start the row with:

if err := tsBatch.StartRow(time.Now()); err != nil {
        // handle error
}

Then set as many value as you need in the row, say the columns from ts-0 are two int64s and the column from ts-1 is a double:

if err := tsBatch.RowSetBlob(0, []byte("def")); err != nil {
        // handle error
}
if err = tsBatch.RowSetDouble(1, 4.4); err != nil {
        // handle error
}

Once you’re finished setting your rows of data, push them:

if err := tsBatch.Push(); err != nil {
        // handle error
}

Query

Once you have some data in there, you might want to retrieve it. With the same columns objects we created earlier, We can get the values of a specific range between two time points like so:

doublePoints, err := doubleColumn.GetRanges(qdb.NewRange(time.Unix(42, 0), time.Unix(42, 5)))
if err != nil {
        // handle error
}
for _, point := range doublePoints {
        fmt.Println("timestamp:", point.Timestamp().UTC(), point.Content())
}

Or you can directly use the query language (see Query language). We will reuse the ts timeseries defined earlier, it has two columns (blob_col and double_col), and both are int64s:

query := string("select * from ts")
q := handle.Query(query)
result, err := q.Execute()
defer handle.Release(unsafe.Pointer(result))
for _, table := range result.Tables() {
        for _, row := range table.Rows() {
                columns := table.Columns(row)

                // the index 0 in the columns is the timestamp of the current row
                timestamp, err := columns[0].GetTimestamp()
                if err != nil {
                        // handle error
                }

                var stringValue string
                blobValue, err := columns[1].GetBlob()
                if err != nil {
                        stringValue = "(nil)"
                } else {
                        stringValue = string(blobValue)
                }

                doubleValue, err := columns[2].GetDouble()
                if err != nil {
                        // handle error
                }

                fmt.Println("timestamp:", timestamp.UTC(), stringValue, doubleValue)
        }
}
arrow_backPrevious
7.7. Excel integration
Next arrow_forward
7.9. Hadoop integration