diff --git a/CHANGELOG.md b/CHANGELOG.md index d2f09f3f6..df0fc79c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## 4.7.0 [unreleased] +### Features +1. [#376](https://github.com/influxdata/influxdb-client-csharp/pull/376): Added `FluxRecord.Row` which stores response data in a list + ### Dependencies Update dependencies: diff --git a/Client.Core/Flux/Domain/FluxRecord.cs b/Client.Core/Flux/Domain/FluxRecord.cs index 9d4fad56e..a35195b3c 100644 --- a/Client.Core/Flux/Domain/FluxRecord.cs +++ b/Client.Core/Flux/Domain/FluxRecord.cs @@ -23,6 +23,11 @@ public class FluxRecord /// public Dictionary Values { get; } = new Dictionary(); + /// + /// The record's columns. + /// + public List Row { get; } = new List(); + public FluxRecord(int table) { Table = table; diff --git a/Client.Core/Flux/Internal/FluxCsvParser.cs b/Client.Core/Flux/Internal/FluxCsvParser.cs index f4efc17aa..6d1544f22 100644 --- a/Client.Core/Flux/Internal/FluxCsvParser.cs +++ b/Client.Core/Flux/Internal/FluxCsvParser.cs @@ -267,7 +267,11 @@ private FluxRecord ParseRecord(int tableIndex, FluxTable table, CsvReader csv) var strValue = csv[fluxColumn.Index + 1]; - record.Values.Add(columnName, ToValue(strValue, fluxColumn)); + var value = ToValue(strValue, fluxColumn); + + record.Values[columnName] = value; + + record.Row.Add(value); } return record; @@ -389,6 +393,16 @@ private void AddColumnNamesAndTags(FluxTable table, CsvReader columnNames) var fluxColumn = GetFluxColumn(ii, table); fluxColumn.Label = columnNames[ii + 1]; } + + var duplicates = table.Columns.GroupBy(col => col.Label) + .Where(rec => rec.Count() > 1) + .Select(label => label.Key).ToList(); + if (duplicates.Any()) + { + Console.WriteLine( + $"The response contains columns with duplicated names: {string.Join(", ", duplicates)}\n" + + "You should use the 'FluxRecord.Row to access your data instead of 'FluxRecord.Values' dictionary."); + } } private FluxColumn GetFluxColumn(int columnIndex, FluxTable table) diff --git a/Client.Legacy.Test/FluxCsvParserTest.cs b/Client.Legacy.Test/FluxCsvParserTest.cs index eed209251..d6a85d986 100644 --- a/Client.Legacy.Test/FluxCsvParserTest.cs +++ b/Client.Legacy.Test/FluxCsvParserTest.cs @@ -771,6 +771,28 @@ public void ParseWithoutDatatype() Assert.AreEqual("west", tables[0].Records[0].GetValueByKey("region")); } + [Test] + public void ParseDuplicateColumnNames() + { + const string data = + "#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,string,string,double\n" + + "#group,false,false,true,true,false,true,true,false\n" + + "#default,_result,,,,,,,\n" + + " ,result,table,_start,_stop,_time,_measurement,location,result\n" + + ",,0,2022-09-13T06:14:40.469404272Z,2022-09-13T06:24:40.469404272Z,2022-09-13T06:24:33.746Z,my_measurement,Prague,25.3\n" + + ",,0,2022-09-13T06:14:40.469404272Z,2022-09-13T06:24:40.469404272Z,2022-09-13T06:24:39.299Z,my_measurement,Prague,25.3\n" + + ",,0,2022-09-13T06:14:40.469404272Z,2022-09-13T06:24:40.469404272Z,2022-09-13T06:24:40.454Z,my_measurement,Prague,25.3\n"; + + _parser = new FluxCsvParser(FluxCsvParser.ResponseMode.OnlyNames); + var tables = ParseFluxResponse(data); + Assert.AreEqual(1, tables.Count); + Assert.AreEqual(8, tables[0].Columns.Count); + Assert.AreEqual(3, tables[0].Records.Count); + Assert.AreEqual(7, tables[0].Records[0].Values.Count); + Assert.AreEqual(8, tables[0].Records[0].Row.Count); + Assert.AreEqual(25.3, tables[0].Records[0].Row[7]); + } + private List ParseFluxResponse(string data) { var consumer = new FluxCsvParser.FluxResponseConsumerTable(); diff --git a/Examples/README.md b/Examples/README.md index 1cd144a73..6eca76b7a 100644 --- a/Examples/README.md +++ b/Examples/README.md @@ -6,3 +6,5 @@ ## Others - [InvokableScripts.cs](InvokableScripts.cs) - How to use Invokable scripts Cloud API to create custom endpoints that query data - [ParametrizedQuery.cs](ParametrizedQuery.cs) - How to use parameterized Flux queries +- [RecordRowExample.cs](RecordRowExample.cs) - How to use FluxRecord.Row (List) instead of FluxRecord.Values (Dictionary), +in case of duplicity column names \ No newline at end of file diff --git a/Examples/RecordRowExample.cs b/Examples/RecordRowExample.cs new file mode 100644 index 000000000..1bd0e9808 --- /dev/null +++ b/Examples/RecordRowExample.cs @@ -0,0 +1,56 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using InfluxDB.Client; +using InfluxDB.Client.Api.Domain; + + +namespace Examples +{ + public static class RecordRowExample + { + public static async Task Main() + { + const string url = "http://localhost:9999/"; + const string token = "my-token"; + const string bucket = "my-bucket"; + const string org = "my-org"; + + using var client = InfluxDBClientFactory.Create(url, token.ToCharArray()); + + // + // Prepare Data + // + var writeApi = client.GetWriteApiAsync(); + for (var i = 1; i <= 5; i++) + await writeApi.WriteRecordAsync($"point,table=my-table result={i}", WritePrecision.Ns, bucket, org); + + // + // Query data with pivot + // + var queryApi = client.GetQueryApi(); + var fluxQuery = $"from(bucket: \"{bucket}\")\n" + + " |> range(start: -1m)" + + " |> filter(fn: (r) => (r[\"_measurement\"] == \"point\"))" + + " |> pivot(rowKey:[\"_time\"], columnKey: [\"_field\"], valueColumn: \"_value\")"; + var tables = await queryApi.QueryAsync(fluxQuery, org); + + // + // Write data to output + // + if (tables != null) + { + // using FluxRecord.Values - Dictionary - can`t contains duplicity key names + Console.WriteLine("-------------------------------- FluxRecord.Values -------------------------------"); + foreach (var fluxRecord in tables.SelectMany(fluxTable => fluxTable.Records)) + Console.WriteLine("{" + string.Join(", ", + fluxRecord.Values.Select(kv => kv.Key + ": " + kv.Value).ToArray()) + "}"); + + // using FluxRecord.Row - List> - contains all data + Console.WriteLine("--------------------------------- FluxRecord.Row ---------------------------------"); + foreach (var fluxRecord in tables.SelectMany(fluxTable => fluxTable.Records)) + Console.WriteLine("{" + string.Join(", ", fluxRecord.Row) + "}"); + } + } + } +} \ No newline at end of file diff --git a/Examples/RunExamples.cs b/Examples/RunExamples.cs index beb7e9133..edeb48d95 100644 --- a/Examples/RunExamples.cs +++ b/Examples/RunExamples.cs @@ -69,6 +69,9 @@ public static async Task Main(string[] args) case "ParametrizedQuery": await ParametrizedQuery.Main(args); break; + case "RecordRowExample": + await RecordRowExample.Main(); + break; } } else @@ -78,7 +81,7 @@ public static async Task Main(string[] args) "FluxClientPocoExample, PlatformExample, WriteEventHandlerExample, WriteApiAsyncExample, " + "CustomDomainMapping, PocoQueryWriteExample, CustomDomainMappingAndLinq, " + "SynchronousQuery, InfluxDB18Example, QueryLinqCloud, ManagementExample, " + - " InvokableScripts, ParametrizedQuery"); + " InvokableScripts, ParametrizedQuery, RecordRowExample"); } } }