GLQL: Add showTrends support (for stats and tables)
Add showTrends configuration support to GLQL, enabling percentage change indicators in both stat and table displays.
This is a GLQL-level visualization feature that works across multiple display types (stat, table) and any data type once their aggregation engines support time-series data.
Part 6 of 6 visualization types needed for SDLC dashboard migration:
- Tables (#592262 (closed)) - 18.11 GA
✅ - Stats (#592780 (moved)) - Post-18.11
- Sparklines (#592781 (moved)) - Post-18.11
- Bar Charts (#592782 (moved)) - Post-18.11
- Column Charts (#592784 (moved)) - Post-18.11
- Area Charts (#592783 (moved)) - Post-18.11
- showTrends (this issue) - Post-18.11 - Works with stats and tables
Aligned with research issue #589575 (closed) for GLQL visualization syntax.
SDLC Dashboard use cases (CodeSuggestion):
- Single stat showing total suggestions with % change vs previous period
- Table showing acceptance rate by language with % change column
- Any metric display that benefits from trend comparison
Overview
showTrends is a boolean configuration option that adds percentage change indicators:
- In stats: Shows "▲ 12.5%" or "▼ 8.3%" below the main value
- In tables: Adds a "Change %" column showing trend for each row
Frontend calculates the percentage change by comparing the last two periods in the time-series data.
Acceptance Criteria
General
-
displayConfig.showTrends: trueis supported in bothstatandtabledisplay types -
Requires time dimension using
timeSegment()in aggregate - Frontend calculates % change from time-series data (comparing last two periods)
- Up arrow (▲) shown for increases, down arrow (▼) for decreases
-
Feature flag
glql_trends_display_typegates functionality using agitlab_com_deriskflag - When flag is disabled but showTrends requested, returns error
- Works with any GLQL data type that has time-series data
- No JavaScript console errors
For Stat Display
- Trend shows below main value as "▲ 12.5%" or "▼ 8.3%"
- Query validation: Must have 1 metric and 1 time dimension
- Frontend extracts last two data points from time-series
-
Calculates:
(current - previous) / previous * 100
For Table Display
- Adds implicit "Change %" column after all metrics
- Shows trend for each row comparing first period to last period
- Query validation: Must have time dimension in aggregate
- Each row shows "▲ X.X%" or "▼ X.X%" based on that row's trend
Testing
- Frontend tests cover showTrends in stat display
- Frontend tests cover showTrends in table display
- Error handling when showTrends used without time dimension
Example GLQL Queries
Stat with showTrends
type: CodeSuggestion
mode: analytics
query: timestamp >= -60d
display: stat
displayConfig:
showTrends: true
aggregate:
dimensions: timeSegment(1m) on timestamp
metrics: totalCount as 'Total Suggestions'
Expected output:
┌─────────────────────┐
│ Total Suggestions │
│ │
│ 1,234 │
│ ▲ 12.5% │
└─────────────────────┘
Frontend calculation logic:
- Query returns:
[{month: 1, value: 1100}, {month: 2, value: 1234}] - Frontend shows last value:
1,234 - Frontend calculates:
(1234 - 1100) / 1100 * 100 = 12.18% - Displays:
▲ 12.5%(rounded)
Table with showTrends
type: CodeSuggestion
mode: analytics
query: timestamp >= -60d
display: table
displayConfig:
showTrends: true
aggregate:
dimensions: language as 'Language', timeSegment(1m) on timestamp
metrics: totalCount as 'Total', acceptanceRate as 'Acceptance Rate'
sort: totalCount desc
Expected output:
| Language | Total | Acceptance Rate | Change % |
| ---------- | ----- | --------------- | -------- |
| Ruby | 1,234 | 77.6% | ▲ 12.5% |
| JavaScript | 2,456 | 68.4% | ▼ 3.2% |
| Python | 987 | 81.2% | ▲ 8.1% |
Frontend calculation logic:
- For each row (e.g., Ruby):
- Extract time-series:
[{month: 1, total: 1100}, {month: 2, total: 1234}] - Compare first to last:
(1234 - 1100) / 1100 * 100 = 12.18% - Display:
▲ 12.5%
- Extract time-series:
Invalid Example
type: CodeSuggestion
mode: analytics
display: stat
displayConfig:
showTrends: true
aggregate:
metrics: totalCount as 'Total'
# Error: "showTrends requires a time dimension using timeSegment()"
Implementation Notes
Files to modify:
-
app/assets/javascripts/glql/- Add showTrends renderer for both stat and table
Cross-display type feature:
- This is the first GLQL feature that works across multiple display types
- Shared logic should be extracted to common utility
- Both stat and table renderers will use the same calculation logic
Frontend calculation (shared):
function calculateTrend(timeSeriesData) {
if (timeSeriesData.length < 2) return null;
const previous = timeSeriesData[timeSeriesData.length - 2].value;
const current = timeSeriesData[timeSeriesData.length - 1].value;
const percentChange = ((current - previous) / previous) * 100;
const arrow = percentChange >= 0 ? '▲' : '▼';
const formatted = Math.abs(percentChange).toFixed(1);
return `${arrow} ${formatted}%`;
}
Stat-specific rendering:
- Trend appears below the main value
- Centered alignment
- Slightly smaller font than main value
- Green color for ▲, red color for ▼
Table-specific rendering:
- Adds "Change %" column after all metrics
- Each row calculates trend independently
- Uses same color scheme (green ▲, red ▼)
- Column ordering: dimensions, metrics, sparklines, trends (FIFO)
Time dimension requirement:
- Must use
timeSegment()function syntax - Examples:
timeSegment(1m),timeSegment(1w),timeSegment(1d) - Frontend identifies time-series data by this dimension
Validation:
- Error if
showTrends: truebut no time dimension present - Error message: "showTrends requires a time dimension using timeSegment()"
Feature flag behavior:
-
glql_trends_display_typegates this functionality - When disabled and showTrends is requested, return error message
Comparison logic:
- Always compares last two periods in the time-series
- User controls the comparison by choosing the time range in the query
- Example: Query with
-60dandtimeSegment(1m)compares last 2 months - Example: Query with
-14dandtimeSegment(1d)compares last 2 days
Future enhancements (document but don't implement):
- Custom comparison periods:
displayConfig:
showTrends: true
trendComparison: -7d # Compare to specific period instead of previous
- Configurable positive direction:
displayConfig:
showTrends: true
positiveDirection: down # For error rates where down is good
- Multiple trend columns in tables:
displayConfig:
trends:
- column: '7d Change'
compareWith: -7d
- column: '30d Change'
compareWith: -30d
Related Issues
- Depends on: #592261 (moved) (GLQL Parser support)
- Works with: #592780 (moved) (Stat display), #592262 (closed) (Table display)
- Epic: &21212 (closed) (CodeSuggestion aggregations)
- Related: #592781 (moved) (Sparklines), #592782 (moved) (Bar Charts), #592783 (moved) (Area Charts), #592784 (moved) (Column Charts)
- Research: #589575 (closed) (GLQL visualization requirements and syntax)
- Note: This replaces the original "change indicators" concept, now as a cross-display feature