Skip to content

Commit

Permalink
feat: Add IsMeasurement option to Column attribute for dynamic measur…
Browse files Browse the repository at this point in the history
…ement names in POCO classes (#240)
  • Loading branch information
Chris Cameron authored and Chris Cameron committed Oct 1, 2021
1 parent 533c46f commit 785222f
Show file tree
Hide file tree
Showing 18 changed files with 244 additions and 21 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features
1. [#239](https://github.com/influxdata/influxdb-client-csharp/pull/239): Add support for Asynchronous queries [LINQ]
1. [#240](https://github.com/influxdata/influxdb-client-csharp/pull/240): Add IsMeasurement option to Column attribute for dynamic measurement names in POCO classes

## 3.0.0 [2021-09-17]

Expand All @@ -11,7 +12,6 @@ Adds a `Type` overload for POCOs to `QueryAsync`. This will add `object ConvertT
### Features
1. [#232](https://github.com/influxdata/influxdb-client-csharp/pull/232): Add a `Type` overload for POCOs to `QueryAsync`.
1. [#233](https://github.com/influxdata/influxdb-client-csharp/pull/233): Add possibility to follow HTTP redirects
1. [#239](https://github.com/influxdata/influxdb-client-csharp/pull/239): Add support for Asynchronous queries [LINQ]

### Bug Fixes
1. [#236](https://github.com/influxdata/influxdb-client-csharp/pull/236): Mapping `long` type into Flux AST [LINQ]
Expand Down
6 changes: 3 additions & 3 deletions Client.Core.Test/AbstractTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,13 +114,13 @@ private async Task InfluxDbRequest(HttpRequestMessage request)
try
{
var response = await httpClient.SendAsync(request);
Assert.IsTrue(response.IsSuccessStatusCode);
Assert.IsTrue(response.IsSuccessStatusCode, $"Failed to make HTTP request: {response.ReasonPhrase}");

Thread.Sleep(DefaultInfluxDBSleep);
}
catch (Exception)
catch (Exception e)
{
Assert.Fail("Unexpected exception");
Assert.Fail("Unexpected exception: " + e);
}
}
}
Expand Down
4 changes: 4 additions & 0 deletions Client.Core/Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace InfluxDB.Client.Core
/// <summary>
/// The annotation is used for mapping POCO class into line protocol.
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public sealed class Measurement : Attribute
{
public string Name { get; }
Expand All @@ -18,10 +19,13 @@ public Measurement(string name)
/// <summary>
/// The annotation is used to customize bidirectional mapping between POCO and Flux query result or Line Protocol.
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public sealed class Column : Attribute
{
public string Name { get; }

public bool IsMeasurement { get; set; }

public bool IsTag { get; set; }

public bool IsTimestamp { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion Client.Core/Flux/Internal/AttributesCache.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public PropertyInfo[] GetProperties(Type type)
}

/// <summary>
/// Get Mapping attribute for specified propery.
/// Get Mapping attribute for specified property.
/// </summary>
/// <param name="property">property of DomainObject</param>
/// <returns>Property Attribute</returns>
Expand Down
4 changes: 4 additions & 0 deletions Client.Core/Flux/Internal/FluxResultMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ internal object ToPoco(FluxRecord record, Type type)
{
var attribute = _attributesCache.GetAttribute(property);

if (attribute != null && attribute.IsMeasurement)
{
SetFieldValue(poco, property, record.GetMeasurement());
}
if (attribute != null && attribute.IsTimestamp)
{
SetFieldValue(poco, property, record.GetTime());
Expand Down
12 changes: 12 additions & 0 deletions Client.Linq.Test/DomainObjects.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ class SensorDateTimeOffset
[Column(IsTimestamp = true)] public DateTimeOffset Timestamp { get; set; }
}

class SensorWithCustomMeasurement
{
[Column(IsMeasurement = true)]
public string Measurement { get; set; }

[Column("sensor_id", IsTag = true)]
public string SensorId { get; set; }

[Column("data")]
public int Value { get; set; }
}

class SensorCustom
{
public Guid Id { get; set; }
Expand Down
30 changes: 30 additions & 0 deletions Client.Linq.Test/InfluxDBQueryVisitorTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -428,6 +428,36 @@ where month11 > s.Timestamp
}
}

[Test]
public void ResultOperatorByMeasurement()
{
var settings = new QueryableOptimizerSettings
{
DropMeasurementColumn = false
};

var query = from s in InfluxDBQueryable<SensorWithCustomMeasurement>.Queryable("my-bucket", "my-org", _queryApi, settings)
where s.Value > 10
where s.Measurement == "my-measurement"
select s;
var visitor = BuildQueryVisitor(query);

const string expected = "start_shifted = int(v: time(v: p2))\n\nfrom(bucket: p1) " +
"|> range(start: time(v: start_shifted)) " +
"|> filter(fn: (r) => (r[\"_measurement\"] == p4)) " +
"|> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\") " +
"|> drop(columns: [\"_start\", \"_stop\"]) " +
"|> filter(fn: (r) => (r[\"data\"] > p3))";

Assert.AreEqual(expected, visitor.BuildFluxQuery());

var ast = visitor.BuildFluxAST();

var measurementAssignment = ((OptionStatement)ast.Body[3]).Assignment as VariableAssignment;
Assert.AreEqual("p4", measurementAssignment?.Id.Name);
Assert.AreEqual("my-measurement", (measurementAssignment?.Init as StringLiteral)?.Value);
}

[Test]
public void TimestampAsDateTimeOffset()
{
Expand Down
6 changes: 6 additions & 0 deletions Client.Linq/IMemberNameResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public interface IMemberNameResolver

public enum MemberType
{
Measurement,
Tag,
Field,
Timestamp,
Expand All @@ -52,6 +53,11 @@ public MemberType ResolveMemberType(MemberInfo memberInfo)

if (attribute != null)
{
if (attribute.IsMeasurement)
{
return MemberType.Measurement;
}

if (attribute.IsTag)
{
return MemberType.Tag;
Expand Down
37 changes: 35 additions & 2 deletions Client.Linq/InfluxDBQueryable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,50 @@ public class QueryableOptimizerSettings
public QueryableOptimizerSettings()
{
QueryMultipleTimeSeries = false;
DropMeasurementColumn = true;
DropStartColumn = true;
DropStopColumn = true;
}

/// <summary>
/// Gets or sets whether the drive is used to query multiple time series.
/// Setting this variable to true will change how the produced Flux Query looks like:
/// <list type="bullet">
/// <item>appends <a href="https://docs.influxdata.com/influxdb/v2.0/reference/flux/stdlib/built-in/transformations/group/">group operator</a></item>
/// <item>enable use default sorting: <i>sort(columns: ["_time"], desc: false)</i></item>
/// <item>Appends <a href="https://docs.influxdata.com/flux/v0.x/stdlib/universe/group/">group operator</a></item>
/// <item>Enable use default sorting: <i>sort(columns: ["_time"], desc: false)</i></item>
/// </list>
/// </summary>
public bool QueryMultipleTimeSeries { get; set; }

/// <summary>
/// Gets or sets whether the _measurement column will be dropped from query results.
/// Setting this variable to true will change how the produced Flux Query looks like:
/// <list type="bullet">
/// <item>Appends <a href="https://docs.influxdata.com/flux/v0.x/stdlib/universe/drop/">drop operator</a></item>
/// <item>Drops the _measurement column: <i>drop(columns: ["_measurement"])</i></item>
/// </list>
/// </summary>
public bool DropMeasurementColumn { get; set; }

/// <summary>
/// Gets or sets whether the _start column will be dropped from query results.
/// Setting this variable to true will change how the produced Flux Query looks like:
/// <list type="bullet">
/// <item>Appends <a href="https://docs.influxdata.com/flux/v0.x/stdlib/universe/drop/">drop operator</a></item>
/// <item>Drops the _start column: <i>drop(columns: ["_start"])</i></item>
/// </list>
/// </summary>
public bool DropStartColumn { get; set; }

/// <summary>
/// Gets or sets whether the _stop column will be dropped from query results.
/// Setting this variable to true will change how the produced Flux Query looks like:
/// <list type="bullet">
/// <item>Appends <a href="https://docs.influxdata.com/flux/v0.x/stdlib/universe/drop/">drop operator</a></item>
/// <item>Drops the _stop column: <i>drop(columns: ["_stop"])</i></item>
/// </list>
/// </summary>
public bool DropStopColumn { get; set; }
}

/// <summary>
Expand Down
3 changes: 3 additions & 0 deletions Client.Linq/Internal/Expressions/ColumnName.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public void AppendFlux(StringBuilder builder)
{
switch (_memberResolver.ResolveMemberType(_member))
{
case MemberType.Measurement:
builder.Append("_measurement");
break;
case MemberType.Timestamp:
builder.Append("_time");
break;
Expand Down
22 changes: 22 additions & 0 deletions Client.Linq/Internal/Expressions/MeasurementColumnName.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System.Reflection;
using System.Text;

namespace InfluxDB.Client.Linq.Internal.Expressions
{
internal class MeasurementColumnName : IExpressionPart
{
private readonly ColumnName _delegate;

internal MeasurementColumnName(MemberInfo member, IMemberNameResolver memberNameResolver)
{
_delegate = new ColumnName(member, memberNameResolver);
}

public void AppendFlux(StringBuilder builder)
{
builder.Append("r[\"");
_delegate.AppendFlux(builder);
builder.Append("\"]");
}
}
}
41 changes: 37 additions & 4 deletions Client.Linq/Internal/QueryAggregator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,18 @@ internal string BuildFluxQuery(QueryableOptimizerSettings settings)
BuildOperator("from", "bucket", _bucketAssignment),
BuildRange(transforms),
BuildFilter(_filterByTags),
"pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")",
"drop(columns: [\"_start\", \"_stop\", \"_measurement\"])",
settings.QueryMultipleTimeSeries ? "group()" : "",
BuildFilter(_filterByFields)
"pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")"
};

var drop = BuildDrop(settings);
if (!string.IsNullOrEmpty(drop))
{
parts.Add(drop);
}

parts.Add(settings.QueryMultipleTimeSeries ? "group()" : "");
parts.Add(BuildFilter(_filterByFields));

// https://docs.influxdata.com/influxdb/cloud/reference/flux/stdlib/built-in/transformations/sort/
foreach (var ((column, columnVariable, descending, descendingVariable), index) in _orders.Select((value, i) => (value, i)))
{
Expand Down Expand Up @@ -203,6 +209,33 @@ internal string BuildFluxQuery(QueryableOptimizerSettings settings)
return query.ToString();
}

private string BuildDrop(QueryableOptimizerSettings settings)
{
var columns = new List<string>();

if (settings.DropStartColumn)
{
columns.Add("\"_start\"");
}

if (settings.DropStopColumn)
{
columns.Add("\"_stop\"");
}

if (settings.DropMeasurementColumn)
{
columns.Add("\"_measurement\"");
}

if (columns.Count == 0)
{
return null;
}

return $"drop(columns: [{string.Join(", ", columns)}])";
}

private string BuildRange(List<string> transforms)
{
string rangeStartShift = null;
Expand Down
3 changes: 3 additions & 0 deletions Client.Linq/Internal/QueryExpressionTreeVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ protected override Expression VisitMember(MemberExpression expression)
{
switch (_context.MemberResolver.ResolveMemberType(expression.Member))
{
case MemberType.Measurement:
_expressionParts.Add(new MeasurementColumnName(expression.Member, _context.MemberResolver));
break;
case MemberType.Timestamp:
_expressionParts.Add(new TimeColumnName(expression.Member, _context.MemberResolver));
break;
Expand Down
5 changes: 3 additions & 2 deletions Client.Linq/Internal/QueryVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ public override void VisitWhereClause(WhereClause whereClause, QueryModel queryM
var tagFilter = new List<IExpressionPart>();
var fieldFilter = new List<IExpressionPart>();

// Map LINQ filter expresion to right place: range, tag filtering, field filtering
// Map LINQ filter expression to right place: range, tag filtering, field filtering
foreach (var expression in expressions)
{
switch (expression)
Expand All @@ -86,6 +86,7 @@ public override void VisitWhereClause(WhereClause whereClause, QueryModel queryM
break;
// Tag
case TagColumnName _:
case MeasurementColumnName _:
tagFilter.Add(expression);
break;
// Field
Expand Down Expand Up @@ -187,7 +188,7 @@ private string ConcatExpression(IEnumerable<IExpressionPart> expressions)
}).ToString();
}

private void AddFilterByRange(List<IExpressionPart> rangeFilter)
private void AddFilterByRange(List<IExpressionPart> rangeFilter)
{
var rangeBinaryIndexes = Enumerable.Range(0, rangeFilter.Count)
.Where(i => rangeFilter[i] is BinaryOperator)
Expand Down
6 changes: 3 additions & 3 deletions Client.Test/ItTasksApiTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -527,9 +527,9 @@ public async Task Runs()
Assert.IsNotEmpty(run.Id);
Assert.AreEqual(task.Id, run.TaskID);
Assert.AreEqual(Run.StatusEnum.Success, run.Status);
Assert.Greater(DateTime.Now, run.StartedAt);
Assert.Greater(DateTime.Now, run.FinishedAt);
Assert.Greater(DateTime.Now, run.ScheduledFor);
Assert.Greater(DateTime.UtcNow, run.StartedAt);
Assert.Greater(DateTime.UtcNow, run.FinishedAt);
Assert.Greater(DateTime.UtcNow, run.ScheduledFor);
Assert.IsNull(run.RequestedAt);

task = await _tasksApi.FindTaskByIdAsync(task.Id);
Expand Down
Loading

0 comments on commit 785222f

Please sign in to comment.