1+ // Copyright © Datalust and contributors.
2+ //
3+ // Licensed under the Apache License, Version 2.0 (the "License");
4+ // you may not use this file except in compliance with the License.
5+ // You may obtain a copy of the License at
6+ //
7+ // http://www.apache.org/licenses/LICENSE-2.0
8+ //
9+ // Unless required by applicable law or agreed to in writing, software
10+ // distributed under the License is distributed on an "AS IS" BASIS,
11+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+ // See the License for the specific language governing permissions and
13+ // limitations under the License.
14+
15+ using System ;
16+ using System . Collections . Generic ;
17+ using System . Linq ;
18+ using Seq . Api . Model . Data ;
19+
20+ namespace SeqCli . Mcp . Data ;
21+
22+ public static class QueryResultHelper
23+ {
24+ /// <summary>
25+ /// Convert <paramref name="result"/> into a flat table. Seq reduces browser-side processing and optimizes
26+ /// response sizes by constructing result trees for some grouped/time-sliced query results.
27+ /// </summary>
28+ public static void Flatten ( QueryResultPart result , Action < IEnumerable < object ? > > writeRow )
29+ {
30+ if ( result . Error != null )
31+ return ;
32+
33+ if ( result . Rows != null )
34+ {
35+ writeRow ( result . Columns ! ) ;
36+ foreach ( var row in result . Rows )
37+ {
38+ writeRow ( row ) ;
39+ }
40+ }
41+ else if ( result . Slices != null )
42+ {
43+ writeRow ( new object [ ] { "time" } . Concat ( result . Columns ! ) ) ;
44+
45+ var empty = result . Columns ! . Select ( _ => "" ) . ToArray ( ) ;
46+ foreach ( var slice in result . Slices )
47+ {
48+ var any = false ;
49+ foreach ( var row in slice . Rows )
50+ {
51+ any = true ;
52+ writeRow ( new object [ ] { DateTimeOffset . Parse ( slice . Time ) . UtcDateTime } . Concat ( row ) ) ;
53+ }
54+ if ( ! any )
55+ {
56+ writeRow ( new object [ ] { DateTimeOffset . Parse ( slice . Time ) . UtcDateTime } . Concat ( empty ) ) ;
57+ }
58+ }
59+ }
60+ else if ( result . Series != null )
61+ {
62+ writeRow ( MergeColumns ( result . Columns ! , result . Series . FirstOrDefault ( ) ) ) ;
63+ foreach ( var series in result . Series )
64+ {
65+ foreach ( var slice in series . Slices )
66+ {
67+ var empty = result . Columns ! . Take ( series . Key . Length ) . Select ( _ => ( object ? ) null ) . ToArray ( ) ;
68+ var any = false ;
69+ foreach ( var row in slice . Rows )
70+ {
71+ any = true ;
72+ writeRow ( series . Key . Concat ( [ DateTimeOffset . Parse ( slice . Time ) . UtcDateTime ] ) . Concat ( row ) ) ;
73+ }
74+ if ( ! any )
75+ {
76+ writeRow ( series . Key . Concat ( [ DateTimeOffset . Parse ( slice . Time ) . UtcDateTime ] ) . Concat ( empty ) ) ;
77+ }
78+ }
79+ }
80+ }
81+ else
82+ {
83+ throw new NotImplementedException ( "Query result set does not conform to any expected pattern." ) ;
84+ }
85+ }
86+
87+ static IEnumerable < object > MergeColumns ( string [ ] columns , TimeseriesPart ? firstSeries )
88+ {
89+ if ( firstSeries == null )
90+ yield break ;
91+
92+ var i = 0 ;
93+ for ( ; i < firstSeries . Key . Length ; ++ i )
94+ {
95+ yield return columns [ i ] ;
96+ }
97+
98+ yield return "time" ;
99+
100+ for ( ; i < columns . Length ; ++ i )
101+ {
102+ yield return columns [ i ] ;
103+ }
104+ }
105+ }
0 commit comments