Macro Approach

The QuestDBClient can be used with macros to write data to a QuestDB server instance.

API

The API that describes the implementation this macro-based approach can be found here:

Example

Note

The functions need to following order: table -> symbol -> others -> At/AtNow

others represent field types such as integers, floats etc. The terminal symbol for each chain should be @At() or @AtNow().

Not following the specified order will result in an exception being thrown.

Basic Example

using QuestDBClient

"""
Assumes the presence of a table called readings created using:

CREATE TABLE readings (
  timestamp TIMESTAMP,
  city SYMBOL,
  temperature DOUBLE,
  humidity DOUBLE,
  make SYMBOL
) TIMESTAMP(timestamp) PARTITION BY DAY;
"""

## Connects to the localhost at port 9009
sender = Sender()

## Connect the sender to the server first
connect(sender)

## Create ILP record statements
sender |>
    @table(:readings) |>
    @symbol(:make => :Omron) |>
    @symbol(:city => :Lisbon) |>
    @FloatColumn(:temperature => 24.8) |>
    @FloatColumn(:humidity => 0.334) |>
    @AtNow

sender |>
    @table(:readings) |>
    @symbol(:make => :HoneyWell) |>
    @symbol(:city => :Kisumu) |>
    @FloatColumn(:temperature => 30.2) |>
    @FloatColumn(:humidity => 0.54) |>
    @AtNow

sender |>
    @table(:readings) |>
    @symbol(:make => :Omron) |>
    @symbol(:city => :Berlin) |>
    @FloatColumn(:temperature => 26.1) |>
    @FloatColumn(:humidity => 0.45) |>
    @AtNow

## Flush the output to the server
QuestDBSender.flush(sender)

## Close the socket connection
## Close first calls QuestDBSender.flush(sender) as part of its definition
QuestDBSender.close(sender)

Working with DataFrames

DataFrames can also be used as a datasource in the QuestDBClient. However, some preprocessing is needed such as converting/casting the column types to supported types. The table also needs to be specified beforehand, the column that represents the designated timestamp and any symbols(tags) need to be specified too.

Supported types include: Symbol, Integer and subtypes, AbstractFloat and subtypes, Bool, Char, AbstractString and subtypes, Date, DateTime, UUID.

Example

Note

This example requires the installation of the DataFrames.jl package.

A DataFrame object with the following structure will be used in the example:

citymaketempareturehumidity
LondonOmron29.40.334
NairobiHoneywell24.00.51
using DataFrames
using QuestDBClient

## Create a DataFrame instance
df = DataFrame(city=["London", "Nairobi"], 
               make=[:Omron, :Honeywell], 
               temperature=[29.4, 24.0], 
               humidity=[0.334, 0.51])

## Create a sender instance that will connect to the localhost at port 9009
sender = Sender()

## Connect the sender to the server first
connect(sender)

## Map the dataframe data to ILP record statements
sender |> @source(df = df, table = :readings, symbols=[:city, :make])

## Flush the output to the server
QuestDBSender.flush(sender)

## Close the socket connection
## Close first calls QuestDBSender.flush(sender) as part of its definition
QuestDBSender.close(sender)

An example with the At field specified.

citymaketempareturehumiditycollection_time
LondonOmron29.40.3342023-04-10T13:09:31Z
NairobiHoneywell24.00.512023-04-10T13:09:42Z
using DataFrames
using Dates
using QuestDBClient

## A DataFrame instance
df = DataFrame(city=["London", "Nairobi"], 
               make=[:Omron, :Honeywell], 
               temperature=[29.4, 24.0], 
               humidity=[0.334, 0.51], 
               collection_time=["2023-04-10T13:09:31Z", "2023-04-10T13:09:42Z"])

## Cast the collection_time to DateTime
date_format = dateformat"y-m-dTH:M:SZ"
df[!, :collection_time] = DateTime.(df[:, :collection_time], date_format)

## Create a sender instance that will connect to the localhost at port 9009
sender = Sender()

## Connect the sender to the server first
connect(sender)

## Map the dataframe data to ILP record statements
sender |> @source(df = df, table = :readings, symbols = [:city, :make], at = :collection_time)

## Flush the output to the server
QuestDBSender.flush(sender)

## Close the socket connection
## Close first calls QuestDBSender.flush(sender) as part of its definition
QuestDBSender.close(sender)
Note

The sender attempts to write values to the QuestDB Database Server depending on whether the buffer size has been met or exceeded while reading the rows of the DataFrame. This is even before the flush or close function is called.