// This file is part of OpenTSDB.
// Copyright (C) 2015 The OpenTSDB Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2.1 of the License, or (at your
// option) any later version. This program is distributed in the hope that it
// will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
// General Public License for more details. You should have received a copy
// of the GNU Lesser General Public License along with this program. If not,
// see <http://www.gnu.org/licenses/>.
package net.opentsdb.examples;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.stumbleupon.async.Callback;
import com.stumbleupon.async.Deferred;
import net.opentsdb.core.DataPoint;
import net.opentsdb.core.DataPoints;
import net.opentsdb.core.Query;
import net.opentsdb.core.SeekableView;
import net.opentsdb.core.TSDB;
import net.opentsdb.core.TSQuery;
import net.opentsdb.core.TSSubQuery;
import net.opentsdb.query.filter.TagVFilter;
import net.opentsdb.utils.Config;
import net.opentsdb.utils.DateTime;
/**
* One example on how to query.
* Taken from
* <a href="https://groups.google.com/forum/#!searchin/opentsdb/java$20api/opentsdb/6MKs-FkSLoA/gifHF327CIAJ">
* this thread</a>
* The metric and key query arguments assume that you've input data from the Quick Start tutorial
* <a href="http://opentsdb.net/docs/build/html/user_guide/quickstart.html">here.</a>
*/
public class QueryExample {
public static void main(final String[] args) throws IOException {
// Set these as arguments so you don't have to keep path information in
// source files
String pathToConfigFile = (args != null && args.length > 0 ? args[0] : null);
// Create a config object with a path to the file for parsing. Or manually
// override settings.
// e.g. config.overrideConfig("tsd.storage.hbase.zk_quorum", "localhost");
final Config config;
if (pathToConfigFile != null && !pathToConfigFile.isEmpty()) {
config = new Config(pathToConfigFile);
} else {
// Search for a default config from /etc/opentsdb/opentsdb.conf, etc.
config = new Config(true);
}
final TSDB tsdb = new TSDB(config);
// main query
final TSQuery query = new TSQuery();
// use any string format from
// http://opentsdb.net/docs/build/html/user_guide/query/dates.html
query.setStart("1h-ago");
// Optional: set other global query params
// at least one sub query required. This is where you specify the metric and
// tags
final TSSubQuery subQuery = new TSSubQuery();
subQuery.setMetric("my.tsdb.test.metric");
// filters are optional but useful.
final List<TagVFilter> filters = new ArrayList<TagVFilter>(1);
filters.add(new TagVFilter.Builder()
.setType("literal_or")
.setFilter("example1")
.setTagk("script")
.setGroupBy(true)
.build());
subQuery.setFilters(filters);
// you do have to set an aggregator. Just provide the name as a string
subQuery.setAggregator("sum");
// IMPORTANT: don't forget to add the subQuery
final ArrayList<TSSubQuery> subQueries = new ArrayList<TSSubQuery>(1);
subQueries.add(subQuery);
query.setQueries(subQueries);
query.setMsResolution(true); // otherwise we aggregate on the second.
// make sure the query is valid. This will throw exceptions if something
// is missing
query.validateAndSetQuery();
// compile the queries into TsdbQuery objects behind the scenes
Query[] tsdbqueries = query.buildQueries(tsdb);
// create some arrays for storing the results and the async calls
final int nqueries = tsdbqueries.length;
final ArrayList<DataPoints[]> results = new ArrayList<DataPoints[]>(
nqueries);
final ArrayList<Deferred<DataPoints[]>> deferreds =
new ArrayList<Deferred<DataPoints[]>>(nqueries);
// this executes each of the sub queries asynchronously and puts the
// deferred in an array so we can wait for them to complete.
for (int i = 0; i < nqueries; i++) {
deferreds.add(tsdbqueries[i].runAsync());
}
// Start timer
long startTime = DateTime.nanoTime();
// This is a required callback class to store the results after each
// query has finished
class QueriesCB implements Callback<Object, ArrayList<DataPoints[]>> {
public Object call(final ArrayList<DataPoints[]> queryResults)
throws Exception {
results.addAll(queryResults);
return null;
}
}
// Make sure to handle any errors that might crop up
class QueriesEB implements Callback<Object, Exception> {
@Override
public Object call(final Exception e) throws Exception {
System.err.println("Queries failed");
e.printStackTrace();
return null;
}
}
// this will cause the calling thread to wait until ALL of the queries
// have completed.
try {
Deferred.groupInOrder(deferreds)
.addCallback(new QueriesCB())
.addErrback(new QueriesEB())
.join();
} catch (Exception e) {
e.printStackTrace();
}
// End timer.
double elapsedTime = DateTime.msFromNanoDiff(DateTime.nanoTime(), startTime);
System.out.println("Query returned in: " + elapsedTime + " milliseconds.");
// now all of the results are in so we just iterate over each set of
// results and do any processing necessary.
for (final DataPoints[] dataSets : results) {
for (final DataPoints data : dataSets) {
System.out.print(data.metricName());
Map<String, String> resolvedTags = data.getTags();
for (final Map.Entry<String, String> pair : resolvedTags.entrySet()) {
System.out.print(" " + pair.getKey() + "=" + pair.getValue());
}
System.out.print("\n");
final SeekableView it = data.iterator();
/*
* An important point about SeekableView:
* Because no data is copied during iteration and no new object gets
* created, the DataPoint returned must not be stored and gets
* invalidated as soon as next is called on the iterator (actually it
* doesn't get invalidated but rather its contents changes). If you want
* to store individual data points, you need to copy the timestamp and
* value out of each DataPoint into your own data structures.
*
* In the vast majority of cases, the iterator will be used to go once
* through all the data points, which is why it's not a problem if the
* iterator acts just as a transient "view". Iterating will be very
* cheap since no memory allocation is required (except to instantiate
* the actual iterator at the beginning).
*/
while (it.hasNext()) {
final DataPoint dp = it.next();
System.out.println(" " + dp.timestamp() + " "
+ (dp.isInteger() ? dp.longValue() : dp.doubleValue()));
}
System.out.println("");
}
}
// Gracefully shutdown connection to TSDB
try {
tsdb.shutdown().join();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}