Skip to content

Commit a881cc3

Browse files
committed
Merge pull request #4 from AdysTech/wip
Add Querying InfluxDB functionality
2 parents 042d69e + 90db8d4 commit a881cc3

File tree

9 files changed

+155
-45
lines changed

9 files changed

+155
-45
lines changed

AdysTech.InfluxDB.Client.Net.Test/AdysTech.InfluxDB.Client.Net.Test.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<WarningLevel>4</WarningLevel>
3636
</PropertyGroup>
3737
<ItemGroup>
38+
<Reference Include="Microsoft.CSharp" />
3839
<Reference Include="System" />
3940
</ItemGroup>
4041
<Choose>

AdysTech.InfluxDB.Client.Net.Test/InfluxDBClientTest.cs

Lines changed: 53 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
using System;
22
using Microsoft.VisualStudio.TestTools.UnitTesting;
3+
using System.Dynamic;
34

45
using AdysTech.InfluxDB.Client.Net;
56
using System.Threading.Tasks;
67
using System.Collections.Generic;
8+
using System.Diagnostics;
79

810
namespace InfluxDB.Client.Test
911
{
@@ -52,7 +54,7 @@ public async Task TestGetInfluxDBNamesAsync()
5254
{
5355
try
5456
{
55-
57+
5658
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
5759
var r = await client.GetInfluxDBNamesAsync ();
5860
Assert.IsTrue (r != null && r.Count > 0, "GetInfluxDBNamesAsync retunred null or empty collection");
@@ -65,13 +67,12 @@ public async Task TestGetInfluxDBNamesAsync()
6567
}
6668
}
6769

68-
6970
[TestMethod]
70-
public async Task TestGetInfluxDBStructureAsync_InvalidDB()
71+
public async Task TestGetInfluxDBStructureAsync()
7172
{
7273
try
7374
{
74-
75+
7576
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
7677
var r = await client.GetInfluxDBStructureAsync ("InvalidDB");
7778
Assert.IsTrue (r != null && r.Count == 0, "GetInfluxDBNamesAsync retunred null or non empty collection");
@@ -84,6 +85,24 @@ public async Task TestGetInfluxDBStructureAsync_InvalidDB()
8485
}
8586
}
8687

88+
[TestMethod]
89+
public async Task TestGetInfluxDBStructureAsync_InvalidDB()
90+
{
91+
try
92+
{
93+
94+
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
95+
var r = await client.GetInfluxDBStructureAsync (dbName);
96+
Assert.IsTrue (r != null && r.Count >= 0, "GetInfluxDBNamesAsync retunred null or non empty collection");
97+
}
98+
catch ( Exception e )
99+
{
100+
Assert.Fail ("Unexpected exception of type {0} caught: {1}",
101+
e.GetType (), e.Message);
102+
return;
103+
}
104+
}
105+
87106
[TestMethod]
88107
[ExpectedException (typeof (ArgumentException))]
89108
public async Task TestCreateDatabaseAsync_InvalidName()
@@ -97,7 +116,7 @@ public async Task TestCreateDatabaseAsync()
97116
{
98117
try
99118
{
100-
119+
101120
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
102121
var r = await client.CreateDatabaseAsync (dbName);
103122
Assert.IsTrue (r, "CreateDatabaseAsync retunred false");
@@ -121,7 +140,7 @@ public async Task TestPostValueAsync()
121140
try
122141
{
123142
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
124-
var r = await client.PostValueAsync (dbName, measurementName, DateTime.UtcNow.ToEpoch(TimePrecision.Seconds),TimePrecision.Seconds,"testTag=testTagValue","Temp",33.05);
143+
var r = await client.PostValueAsync (dbName, measurementName, DateTime.UtcNow.ToEpoch (TimePrecision.Seconds), TimePrecision.Seconds, "testTag=testTagValue", "Temp", new Random ().NextDouble ());
125144
Assert.IsTrue (r, "PostValueAsync retunred false");
126145
}
127146
catch ( Exception e )
@@ -141,7 +160,7 @@ public async Task TestPostValuesAsync()
141160
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
142161
var val = new Random ();
143162
var values = new Dictionary<string, double> () { { "filed1", val.NextDouble () * 10 }, { "filed2", val.NextDouble () * 10 }, { "filed3", val.NextDouble () * 10 } };
144-
var r = await client.PostValuesAsync (dbName, measurementName, DateTime.UtcNow.ToEpoch (TimePrecision.Seconds), TimePrecision.Seconds, "testTag=testTagValue",values);
163+
var r = await client.PostValuesAsync (dbName, measurementName, DateTime.UtcNow.ToEpoch (TimePrecision.Seconds), TimePrecision.Seconds, "testTag=testTagValue", values);
145164
Assert.IsTrue (r, "PostValuesAsync retunred false");
146165
}
147166
catch ( Exception e )
@@ -152,5 +171,32 @@ public async Task TestPostValuesAsync()
152171
return;
153172
}
154173
}
174+
175+
[TestMethod]
176+
public async Task TestQueryAsync()
177+
{
178+
try
179+
{
180+
181+
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
182+
var r = await client.QueryDBAsync ("stress", "select * from performance limit 10");
183+
DateTime d;
184+
Assert.IsTrue (r != null && DateTime.TryParse(r[0].time,out d), "QueryDBAsync retunred null or invalid data");
185+
}
186+
catch ( Exception e )
187+
{
188+
Assert.Fail ("Unexpected exception of type {0} caught: {1}",
189+
e.GetType (), e.Message);
190+
return;
191+
}
192+
}
193+
194+
[TestMethod]
195+
[ExpectedException (typeof (ArgumentException))]
196+
public async Task TestQueryAsync_MultiSeries()
197+
{
198+
var client = new InfluxDBClient (influxUrl, dbUName, dbpwd);
199+
var r = await client.QueryDBAsync ("_internal", "Show Series");
200+
}
155201
}
156202
}

AdysTech.InfluxDB.Client.Net/AdysTech.InfluxDB.Client.Net.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
<Reference Include="System" />
3434
<Reference Include="System.Core" />
3535
<Reference Include="System.Net.Http" />
36+
<Reference Include="System.Runtime.Serialization" />
3637
<Reference Include="System.Xml.Linq" />
3738
<Reference Include="System.Data.DataSetExtensions" />
3839
<Reference Include="Microsoft.CSharp" />
@@ -43,6 +44,7 @@
4344
<Compile Include="ExtensionMethods.cs" />
4445
<Compile Include="IInfluxDBClient.cs" />
4546
<Compile Include="InfluxDBClient.cs" />
47+
<Compile Include="DataContracts\InfluxJsonTypes.cs" />
4648
<Compile Include="Properties\AssemblyInfo.cs" />
4749
<Compile Include="ServiceUnavailableException.cs" />
4850
</ItemGroup>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Runtime.Serialization;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace AdysTech.InfluxDB.Client.Net.DataContracts
9+
{
10+
[DataContract]
11+
public class Series
12+
{
13+
[DataMember(Name="name")]
14+
public string SeriesName { get; set; }
15+
16+
[DataMember (Name = "columns")]
17+
public List<string> ColumnHeaders { get; set; }
18+
19+
[DataMember (Name = "values")]
20+
public List<List<string>> Values { get; set; }
21+
}
22+
23+
[DataContract]
24+
public class Result
25+
{
26+
[DataMember (Name = "series")]
27+
public List<Series> Series { get; set; }
28+
}
29+
30+
[DataContract]
31+
public class InfluxResponse
32+
{
33+
[DataMember (Name = "results")]
34+
public List<Result> Results { get; set; }
35+
}
36+
}

AdysTech.InfluxDB.Client.Net/IInfluxDBClient.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ public interface IInfluxDBClient
1414
Task<bool> PostValueAsync(string dbName, string measurement, long timestamp, TimePrecision precision, string tags, string field, double value);
1515
Task<bool> PostValuesAsync(string dbName, string measurement, long timestamp, TimePrecision precision, string tags, IDictionary<string,double> values);
1616
Task<bool> PostRawValueAsync(string dbName, TimePrecision precision, string content);
17+
Task<List<dynamic>> QueryDBAsync(string dbName, string measurementQuery);
1718
}
1819
}

AdysTech.InfluxDB.Client.Net/InfluxDBClient.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
//Copyright: Adarsha@AdysTech
2+
23
using System;
34
using System.Collections.Generic;
5+
using System.Dynamic;
46
using System.IO;
57
using System.Linq;
68
using System.Net;
79
using System.Net.Http;
810
using System.Net.Http.Headers;
11+
using System.Runtime.Serialization.Json;
912
using System.Text;
1013
using System.Text.RegularExpressions;
1114
using System.Threading.Tasks;
15+
using AdysTech.InfluxDB.Client.Net.DataContracts;
1216

1317
namespace AdysTech.InfluxDB.Client.Net
1418
{
@@ -341,7 +345,52 @@ public async Task<bool> PostRawValueAsync(string dbName, TimePrecision precision
341345
return false;
342346
}
343347

348+
/// <summary>
349+
/// Queries Influx DB and gets a time series data back ideal for fetching measurement values,
350+
/// The return list is of dynamics, and each element in their will have properties named after columns in series
351+
/// </summary>
352+
/// <param name="dbName">Name of the database</param>
353+
/// <param name="measurementQuery">Query text, Only results with single series are supported for now</param>
354+
/// <returns>List of ExpandoObjects (in the form of dynamic).
355+
/// The objects will have columns as Peoperties with their current values</returns>
356+
public async Task<List<dynamic>> QueryDBAsync(string dbName, string measurementQuery)
357+
{
358+
var dbStructure = new Dictionary<string, List<string>> ();
359+
var query = new Uri (InfluxUrl + "/query?");
360+
var builder = new UriBuilder (query);
361+
builder.Query = await new FormUrlEncodedContent (new[] {
362+
new KeyValuePair<string, string>("db", dbName) ,
363+
new KeyValuePair<string, string>("q", measurementQuery)
364+
}).ReadAsStringAsync ();
365+
var response = await GetAsync (builder);
366+
if ( response.StatusCode == HttpStatusCode.OK )
367+
{
368+
var content = await response.Content.ReadAsStreamAsync ();
369+
DataContractJsonSerializer js = new DataContractJsonSerializer (typeof (InfluxResponse));
370+
var result = js.ReadObject (content) as InfluxResponse;
371+
372+
if ( result.Results.Count > 1 )
373+
throw new ArgumentException ("The query is resulting in Multi Series respone, which is not supported by this method");
344374

375+
if ( result.Results[0].Series.Count > 1 )
376+
throw new ArgumentException ("The query is resulting in Multi Series respone, which is not supported by this method");
377+
378+
var series = result.Results[0].Series[0];
379+
380+
var results = new List<dynamic> ();
381+
for ( var row = 0; row < series.Values.Count; row++ )
382+
{
383+
dynamic entry = new ExpandoObject ();
384+
results.Add (entry);
385+
for ( var col = 0; col < series.ColumnHeaders.Count; col++ )
386+
{
387+
( (IDictionary<string, object>) entry ).Add (series.ColumnHeaders[col], series.Values[row][col]);
388+
}
389+
}
390+
return results;
391+
}
392+
return null;
393+
}
345394
}
346395

347396
}

AdysTech.InfluxDB.Client.Net/Properties/AssemblyInfo (2).cs

Lines changed: 0 additions & 36 deletions
This file was deleted.

AdysTech.InfluxDB.Client.Net/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("0.0.0.1")]
36-
[assembly: AssemblyFileVersion("0.0.0.1")]
35+
[assembly: AssemblyVersion("0.1.1.0")]
36+
[assembly: AssemblyFileVersion("0.1.1.0")]

README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,14 @@ c. Deleting data
2626

2727
####Create new database
2828
`CreateDatabaseAsync("<db name>");`
29+
30+
###--------------New Version - 0.1.1.0 - 12/19/2015--------------------
31+
Added the functionality to query for existing data from InfluxDB
32+
33+
####Query for data points
34+
`await client.QueryDBAsync ("<db name>", "<query">);`
35+
36+
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.
37+
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.
38+
39+
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 commit comments

Comments
 (0)