Skip to content

Commit

Permalink
Merge pull request #4 from AdysTech/wip
Browse files Browse the repository at this point in the history
Add Querying InfluxDB functionality
  • Loading branch information
mvadu committed Dec 19, 2015
2 parents 042d69e + 90db8d4 commit a881cc3
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="System" />
</ItemGroup>
<Choose>
Expand Down
60 changes: 53 additions & 7 deletions AdysTech.InfluxDB.Client.Net.Test/InfluxDBClientTest.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Dynamic;

using AdysTech.InfluxDB.Client.Net;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Diagnostics;

namespace InfluxDB.Client.Test
{
Expand Down Expand Up @@ -52,7 +54,7 @@ public async Task TestGetInfluxDBNamesAsync()
{
try
{

var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
var r = await client.GetInfluxDBNamesAsync ();
Assert.IsTrue (r != null && r.Count > 0, "GetInfluxDBNamesAsync retunred null or empty collection");
Expand All @@ -65,13 +67,12 @@ public async Task TestGetInfluxDBNamesAsync()
}
}


[TestMethod]
public async Task TestGetInfluxDBStructureAsync_InvalidDB()
public async Task TestGetInfluxDBStructureAsync()
{
try
{

var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
var r = await client.GetInfluxDBStructureAsync ("InvalidDB");
Assert.IsTrue (r != null && r.Count == 0, "GetInfluxDBNamesAsync retunred null or non empty collection");
Expand All @@ -84,6 +85,24 @@ public async Task TestGetInfluxDBStructureAsync_InvalidDB()
}
}

[TestMethod]
public async Task TestGetInfluxDBStructureAsync_InvalidDB()
{
try
{

var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
var r = await client.GetInfluxDBStructureAsync (dbName);
Assert.IsTrue (r != null && r.Count >= 0, "GetInfluxDBNamesAsync retunred null or non empty collection");
}
catch ( Exception e )
{
Assert.Fail ("Unexpected exception of type {0} caught: {1}",
e.GetType (), e.Message);
return;
}
}

[TestMethod]
[ExpectedException (typeof (ArgumentException))]
public async Task TestCreateDatabaseAsync_InvalidName()
Expand All @@ -97,7 +116,7 @@ public async Task TestCreateDatabaseAsync()
{
try
{

var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
var r = await client.CreateDatabaseAsync (dbName);
Assert.IsTrue (r, "CreateDatabaseAsync retunred false");
Expand All @@ -121,7 +140,7 @@ public async Task TestPostValueAsync()
try
{
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
var r = await client.PostValueAsync (dbName, measurementName, DateTime.UtcNow.ToEpoch(TimePrecision.Seconds),TimePrecision.Seconds,"testTag=testTagValue","Temp",33.05);
var r = await client.PostValueAsync (dbName, measurementName, DateTime.UtcNow.ToEpoch (TimePrecision.Seconds), TimePrecision.Seconds, "testTag=testTagValue", "Temp", new Random ().NextDouble ());
Assert.IsTrue (r, "PostValueAsync retunred false");
}
catch ( Exception e )
Expand All @@ -141,7 +160,7 @@ public async Task TestPostValuesAsync()
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
var val = new Random ();
var values = new Dictionary<string, double> () { { "filed1", val.NextDouble () * 10 }, { "filed2", val.NextDouble () * 10 }, { "filed3", val.NextDouble () * 10 } };
var r = await client.PostValuesAsync (dbName, measurementName, DateTime.UtcNow.ToEpoch (TimePrecision.Seconds), TimePrecision.Seconds, "testTag=testTagValue",values);
var r = await client.PostValuesAsync (dbName, measurementName, DateTime.UtcNow.ToEpoch (TimePrecision.Seconds), TimePrecision.Seconds, "testTag=testTagValue", values);
Assert.IsTrue (r, "PostValuesAsync retunred false");
}
catch ( Exception e )
Expand All @@ -152,5 +171,32 @@ public async Task TestPostValuesAsync()
return;
}
}

[TestMethod]
public async Task TestQueryAsync()
{
try
{

var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
var r = await client.QueryDBAsync ("stress", "select * from performance limit 10");
DateTime d;
Assert.IsTrue (r != null && DateTime.TryParse(r[0].time,out d), "QueryDBAsync retunred null or invalid data");
}
catch ( Exception e )
{
Assert.Fail ("Unexpected exception of type {0} caught: {1}",
e.GetType (), e.Message);
return;
}
}

[TestMethod]
[ExpectedException (typeof (ArgumentException))]
public async Task TestQueryAsync_MultiSeries()
{
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
var r = await client.QueryDBAsync ("_internal", "Show Series");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
Expand All @@ -43,6 +44,7 @@
<Compile Include="ExtensionMethods.cs" />
<Compile Include="IInfluxDBClient.cs" />
<Compile Include="InfluxDBClient.cs" />
<Compile Include="DataContracts\InfluxJsonTypes.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ServiceUnavailableException.cs" />
</ItemGroup>
Expand Down
36 changes: 36 additions & 0 deletions AdysTech.InfluxDB.Client.Net/DataContracts/InfluxJsonTypes.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;

namespace AdysTech.InfluxDB.Client.Net.DataContracts
{
[DataContract]
public class Series
{
[DataMember(Name="name")]
public string SeriesName { get; set; }

[DataMember (Name = "columns")]
public List<string> ColumnHeaders { get; set; }

[DataMember (Name = "values")]
public List<List<string>> Values { get; set; }
}

[DataContract]
public class Result
{
[DataMember (Name = "series")]
public List<Series> Series { get; set; }
}

[DataContract]
public class InfluxResponse
{
[DataMember (Name = "results")]
public List<Result> Results { get; set; }
}
}
1 change: 1 addition & 0 deletions AdysTech.InfluxDB.Client.Net/IInfluxDBClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,6 @@ public interface IInfluxDBClient
Task<bool> PostValueAsync(string dbName, string measurement, long timestamp, TimePrecision precision, string tags, string field, double value);
Task<bool> PostValuesAsync(string dbName, string measurement, long timestamp, TimePrecision precision, string tags, IDictionary<string,double> values);
Task<bool> PostRawValueAsync(string dbName, TimePrecision precision, string content);
Task<List<dynamic>> QueryDBAsync(string dbName, string measurementQuery);
}
}
49 changes: 49 additions & 0 deletions AdysTech.InfluxDB.Client.Net/InfluxDBClient.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
//Copyright: Adarsha@AdysTech

using System;
using System.Collections.Generic;
using System.Dynamic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Runtime.Serialization.Json;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using AdysTech.InfluxDB.Client.Net.DataContracts;

namespace AdysTech.InfluxDB.Client.Net
{
Expand Down Expand Up @@ -341,7 +345,52 @@ public async Task<bool> PostRawValueAsync(string dbName, TimePrecision precision
return false;
}

/// <summary>
/// Queries Influx DB and gets a time series data back ideal for fetching measurement values,
/// The return list is of dynamics, and each element in their will have properties named after columns in series
/// </summary>
/// <param name="dbName">Name of the database</param>
/// <param name="measurementQuery">Query text, Only results with single series are supported for now</param>
/// <returns>List of ExpandoObjects (in the form of dynamic).
/// The objects will have columns as Peoperties with their current values</returns>
public async Task<List<dynamic>> QueryDBAsync(string dbName, string measurementQuery)
{
var dbStructure = new Dictionary<string, List<string>> ();
var query = new Uri (InfluxUrl + "/query?");
var builder = new UriBuilder (query);
builder.Query = await new FormUrlEncodedContent (new[] {
new KeyValuePair<string, string>("db", dbName) ,
new KeyValuePair<string, string>("q", measurementQuery)
}).ReadAsStringAsync ();
var response = await GetAsync (builder);
if ( response.StatusCode == HttpStatusCode.OK )
{
var content = await response.Content.ReadAsStreamAsync ();
DataContractJsonSerializer js = new DataContractJsonSerializer (typeof (InfluxResponse));
var result = js.ReadObject (content) as InfluxResponse;

if ( result.Results.Count > 1 )
throw new ArgumentException ("The query is resulting in Multi Series respone, which is not supported by this method");

if ( result.Results[0].Series.Count > 1 )
throw new ArgumentException ("The query is resulting in Multi Series respone, which is not supported by this method");

var series = result.Results[0].Series[0];

var results = new List<dynamic> ();
for ( var row = 0; row < series.Values.Count; row++ )
{
dynamic entry = new ExpandoObject ();
results.Add (entry);
for ( var col = 0; col < series.ColumnHeaders.Count; col++ )
{
( (IDictionary<string, object>) entry ).Add (series.ColumnHeaders[col], series.Values[row][col]);
}
}
return results;
}
return null;
}
}

}
36 changes: 0 additions & 36 deletions AdysTech.InfluxDB.Client.Net/Properties/AssemblyInfo (2).cs

This file was deleted.

4 changes: 2 additions & 2 deletions AdysTech.InfluxDB.Client.Net/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,5 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.0.0.1")]
[assembly: AssemblyFileVersion("0.0.0.1")]
[assembly: AssemblyVersion("0.1.1.0")]
[assembly: AssemblyFileVersion("0.1.1.0")]
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,14 @@ c. Deleting data

####Create new database
`CreateDatabaseAsync("<db name>");`

###--------------New Version - 0.1.1.0 - 12/19/2015--------------------
Added the functionality to query for existing data from InfluxDB

####Query for data points
`await client.QueryDBAsync ("<db name>", "<query">);`

This function uses dynamic object (`ExpandoObject` to be exact), so `var r = await client.QueryDBAsync ("stress", "select * from performance limit 10");` will result in list of objects, where each object has properties with its value set to measument value.
So the result can be used like `r[0].time`. This also opens up a way to have an update mechanism as you can now query for data, change some values/tags etc, and write back. Since Influx uses combination of timestamp, tags as primary key, if you don't change tags, the values will be overwritten.

Also unknown little quirk was Influx's need for . (dot) to treat a number as a number, so non US local code can beak Influx data writes. Thanks to @spamik, now double to string conversion will work in any locale.

0 comments on commit a881cc3

Please sign in to comment.