Apache CouchDB

Warning

This section is outdated. It is recommended to use PostgreSQL and nginx instead to provide a REST API for timeseries data.

It is also possible to create an HTTP service based on Apache CouchDB. On FreeBSD, simply install the package:

# pkg install databases/couchdb2

The configuration can be altered by editing /usr/local/etc/couchdb2/local.ini. Some settings are recommended:

[chttpd]
port = 5984
bind_address = 0.0.0.0

[httpd]
WWW-Authenticate = Basic realm="Restricted"
enable_cors = true

[couch_httpd_auth]
require_valid_user = true

[cluster]
n = 1

[cors]
origins = *
credentials = true
methods = GET, PUT, POST, HEAD, DELETE
headers = Accept, Authorization, Content-Type, Origin, Referer, X-CSRF-Token

[admins]
admin = <secret passphrase>

It is important to enable Cross-Origin Resource Sharing (CORS) to allow remote access to the database from foreign hosts. Otherwise, only requests from the very same server are allowed.

You can either enable TLS-support in the configuration or run a reverse proxy like nginx in front of CouchDB to deal with the encryption. Be aware that only CouchDB should return CORS headers, not the reverse proxy.

Running CouchDB

On FreeBSD, add the service to /etc/rc.conf and then start the CouchDB instance:

# sysrc couchdb2_enable="YES"
# service couchdb2 onestart

Access the Fauxton web-interface to administrate the CouchDB instance, for example at https://couchdb.example.com/_utils/. Fauxton allows you to create new databases and define map/reduce functions for them. Make sure that all databases have at least one admin or member, otherwise they are publicly readable!

CouchDB Views

CouchDB provides an HTTP interface to access databases. In order to query a database, a view must be stored, containing a map/reduce function written in JavaScript. Use Fauxton to add views to databases.

The following map function returns a range of observation data sets, selected by project id, sensor node id, target name, and timestamp:

function (doc) {
  if (doc.type == "observation" && doc.project && doc.node && doc.id && doc.timestamp && doc.target) {
     emit([doc.project, doc.node, doc.target, doc.timestamp], doc);
  }
}

The function is stored in design document by_name with index name observations for database timeseries. Use curl to send a request to CouchDB:

$ curl -X GET --user <username>:<password> \
  -G 'https://couchdb.example.com/timeseries/_design/by_date/_view/observations' | jq

The output can be formatted with jq. Add startkey and endkey to the request to select a range of observations:

$ curl -X GET --user <username>:<password> \
  -G 'https://couchdb.example.com/timeseries/_design/by_date/_view/observations' \
  -d startkey='["project1","node1","p99","2016"]' \
  -d endkey='["project1","node1","p99","2018"]' | jq

This will limit the results to observations with given project id project1, sensor node id node1, target name p99, and timestamp between years 2016 and 2018.