Table Of Contents

7.7. Go

7.7.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.7.2. Requirements

7.7.3. Installation

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

go get -d

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/

On a windows system:

# Download qdb-$version-windows-$ unzip qdb-$version-windows-$ $GOPATHsrcgithub.combureau14qdb-api-gothirdpartyquasardb

7.7.4. Reference

The entire reference is available on godoc website.

7.7.5. Getting started

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

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 := ClusterKeyFromFile("/path/to/cluster_public.key")
err = handle.AddClusterPublicKey(clusterKey)
user, secret, err := ClusterKeyFromFile("/path/to/cluster_public.key")
err = handle.AddUserCredentials(user, secret)

// connect
err = handle.Connect("qdb://

You want to connect to an unsecured cluster:

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

or, the panic on fail alternative:

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

You want to connect to a secured cluster:

handle, err := qdb.SetupSecureHandle("qdb://", "/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://", "/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(clusterURI, 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, NeverExpires())

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

updateContent := []byte("updated content")
blob.Update(updateContent, 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(clusterURI, 120*time.Second)
defer h.Close()

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

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

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

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

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

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:


and for example with an update call:

blob.Update(updateContent, 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.


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:

h := MustSetupHandle(clusterURI, 120*time.Second)
defer h.Close()
timeseries := h.Timeseries("alias")
timeseries.Create(24*time.Hour, NewTsColumnInfo("serie_column_blob", TsColumnBlob), NewTsColumnInfo("serie_column_double", TsColumnDouble))

Here your shard size is 24*time.Hour.


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:

column := timeseries.DoubleColumn("serie_column_double")
fmt.Println("column:", column.Name())

// Insert only one point:
column.Insert(NewTsDoublePoint(time.Now(), 3.2))

// Insert multiple points
doublePoints := make([]TsDoublePoint, 2)
doublePoints[0] = NewTsDoublePoint(time.Now(), 3.2)
doublePoints[1] = NewTsDoublePoint(time.Now(), 4.8)

err := column.Insert(doublePoints...)
if 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:

handle.TsBatch(TsBatchColumnInfo{"ts-0", "col-0", 10}, TsBatchColumnInfo{"ts-1", "col-0", 10}, TsBatchColumnInfo{"ts-0", "col-1", 10})

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

err = tsBatch.StartRow(time.Now())
if 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:

err = tsBatch.RowSetDouble(1, 3.2) // you can set the value of the second index
if err != nil {
    // handle error
err = tsBatch.RowSetInt64(0, 2000) // and then the value of the *(ts-0, col-0)*
if err != nil {
    // handle error

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

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

Once you have some data in there, you might want to retrieve it. With the same columns objects we created earlier:

column := timeseries.DoubleColumn("serie_column_double")

We can get the values of a specific range between two time points like so:

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

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

query := string("select * from 'ts-0' in range(1971, +10d)")
q := handle.Query(query)
result, err := q.Execute()
defer handle.Release(unsafe.Pointer(result))
for _, table := range result.Tables() {
  for rowIdx, row := range table.Rows() {
        columns := table.Columns(row)
        // the index 0 in the columns is the timestamp of the current row
        fmt.Println("timestamp:", columns[0].GetTimestamp().UTC(), "- col-0:", columns[1].GetInt64(), "- col-1:", columns[2].GetInt64())
7.6. C++
Next arrow_forward
7.8. Hadoop integration