Skip to content

Commit dab82cd

Browse files
committed
feat: enhance response parsers to support SQL query results and improve visualization creation flow
1 parent 9ea8857 commit dab82cd

10 files changed

Lines changed: 376 additions & 20 deletions

File tree

backend/src/main/java/com/park/utmstack/domain/chart_builder/UtmVisualization.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ public class UtmVisualization implements Serializable {
6969
@Column(name = "system_owner")
7070
private Boolean systemOwner;
7171

72-
@NotNull
7372
@Column(name = "id_pattern")
7473
private Long idPattern;
7574

@@ -104,10 +103,10 @@ public class UtmVisualization implements Serializable {
104103
@JsonDeserialize
105104
private AggregationType aggregationType;
106105

107-
@Column(name = "sql_query")
106+
@Column(name = "sql_query", nullable = true)
108107
private String sqlQuery;
109108

110-
@ManyToOne(fetch = FetchType.EAGER)
109+
@ManyToOne(fetch = FetchType.EAGER, optional = true)
111110
@JoinColumn(name = "id_pattern", referencedColumnName = "id", insertable = false, updatable = false)
112111
private UtmIndexPattern pattern;
113112

@@ -199,6 +198,10 @@ public void setQuery(String query) {
199198
}
200199

201200
public List<FilterType> getFilterType() throws UtmSerializationException {
201+
if (_filters == null) {
202+
return null;
203+
}
204+
202205
filterType = UtilSerializer.jsonDeserializeList(FilterType.class, _filters);
203206
return filterType;
204207
}

backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/responses/ResponseParserFactory.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,24 @@ public ResponseParser instance(ChartType chartType) throws UtmChartBuilderExcept
2828
final String ctx = CLASS_NAME + ".instance";
2929

3030
if (chartType.equals(ChartType.METRIC_CHART))
31-
return new ResponseParserForMetricChart();
31+
return new ResponseParserForMetricChart(); // Listo ELENA
3232
else if (chartType.equals(ChartType.PIE_CHART))
33-
return new ResponseParserForPieChart();
33+
return new ResponseParserForPieChart(); //Listo ELENA
3434
else if (chartType.equals(ChartType.GAUGE_CHART) || chartType.equals(ChartType.GOAL_CHART))
35-
return new ResponseParserForGaugeGoalChart();
35+
return new ResponseParserForGaugeGoalChart(); //Listo ELENA
3636
else if (chartType.equals(ChartType.TAG_CLOUD_CHART))
37-
return new ResponseParserForTagCloudChart();
37+
return new ResponseParserForTagCloudChart(); //Listo ELENA
3838
else if (chartType.equals(ChartType.TABLE_CHART))
39-
return new ResponseParserForTableChart();
39+
return new ResponseParserForTableChart(); // Listo ELENA
4040
else if (chartType.equals(ChartType.LINE_CHART) || chartType.equals(ChartType.AREA_CHART) || chartType.equals(
4141
ChartType.VERTICAL_BAR_CHART) || chartType.equals(ChartType.HORIZONTAL_BAR_CHART))
42-
return new ResponseParserForBarChart();
42+
return new ResponseParserForBarChart(); // Listo ELENA
4343
else if (chartType.equals(ChartType.HEATMAP_CHART))
44-
return new ResponseParserForHeatMapChart();
44+
return new ResponseParserForHeatMapChart(); // Listo ELENA
4545
else if (chartType.equals(ChartType.COORDINATE_MAP_CHART))
46-
return responseParserForCoordinateMapChart;
46+
return responseParserForCoordinateMapChart; // Listo ELENA
4747
else if (chartType.equals(ChartType.LIST_CHART))
48-
return new ResponseParserForListChart();
48+
return new ResponseParserForListChart(); // Error en el original ELENA
4949

5050
throw new UtmChartBuilderException(ctx + ": No implementation founded for chart type: " + chartType.name());
5151
}

backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/responses/impl/bar_chart/ResponseParserForBarChart.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.utmstack.opensearch_connector.parsers.DateHistogramAggregateParser;
1111
import com.utmstack.opensearch_connector.parsers.TermAggregateParser;
1212
import com.utmstack.opensearch_connector.types.BucketAggregation;
13+
import com.utmstack.opensearch_connector.types.SearchSqlResponse;
1314
import org.opensearch.client.opensearch._types.aggregations.*;
1415
import org.opensearch.client.opensearch.core.SearchResponse;
1516
import org.springframework.util.CollectionUtils;
@@ -201,4 +202,49 @@ private void parseMetric(SearchResponse<?> result, List<Metric> metrics) {
201202
throw new RuntimeException(ctx + ": " + e.getLocalizedMessage());
202203
}
203204
}
205+
206+
@Override
207+
public List<BarChartResult> parse(UtmVisualization visualization, SearchSqlResponse<Map> result) {
208+
final String ctx = CLASSNAME + ".parse(SearchSqlResponse)";
209+
try {
210+
BarChartResult retValue = new BarChartResult();
211+
212+
// Serie única
213+
BarChartResult.Serie serie = new BarChartResult.Serie();
214+
serie.setMetricId("1"); // id de la métrica COUNT
215+
serie.setName(""); // nombre vacío como en tu ejemplo
216+
217+
// Recorremos filas y llenamos categorías + datos
218+
for (Object rowObj : result.getData()) {
219+
if (!(rowObj instanceof Map)) {
220+
continue;
221+
}
222+
Map<String, Object> row = (Map<String, Object>) rowObj;
223+
224+
String category = null;
225+
Double value = 0.0;
226+
227+
for (Map.Entry<String, Object> entry : row.entrySet()) {
228+
Object val = entry.getValue();
229+
if (val instanceof Number) {
230+
value = ((Number) val).doubleValue();
231+
} else {
232+
category = val != null ? val.toString() : "UNKNOWN";
233+
}
234+
}
235+
236+
if (category != null) {
237+
retValue.addCategory(category);
238+
serie.addData(value);
239+
}
240+
}
241+
242+
retValue.addSerie(serie);
243+
244+
return Collections.singletonList(retValue);
245+
} catch (Exception e) {
246+
throw new RuntimeException(ctx + ": " + e.getMessage());
247+
}
248+
}
249+
204250
}

backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/responses/impl/coordinate_map/ResponseParserForCoordinateMapChart.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,17 @@
1111
import com.park.utmstack.util.exceptions.UtmIpInfoException;
1212
import com.utmstack.opensearch_connector.parsers.TermAggregateParser;
1313
import com.utmstack.opensearch_connector.types.BucketAggregation;
14+
import com.utmstack.opensearch_connector.types.SearchSqlResponse;
1415
import org.opensearch.client.opensearch.core.SearchResponse;
1516
import org.slf4j.Logger;
1617
import org.slf4j.LoggerFactory;
1718
import org.springframework.stereotype.Component;
19+
import org.springframework.util.Assert;
1820
import org.springframework.util.StringUtils;
1921

2022
import java.util.ArrayList;
2123
import java.util.List;
24+
import java.util.Map;
2225
import java.util.stream.Collectors;
2326

2427
@Component
@@ -110,5 +113,61 @@ public static boolean isValidIPv6(String ip) {
110113
return ip.matches(regex);
111114
}
112115

116+
@Override
117+
public List<CoordinateMapChartResult> parse(UtmVisualization visualization, SearchSqlResponse<Map> result) {
118+
final String ctx = CLASSNAME + ".parse(SearchSqlResponse)";
119+
List<CoordinateMapChartResult> retValue = new ArrayList<>();
120+
121+
try {
122+
Assert.notNull(visualization, "Param visualization must not be null");
123+
124+
for (Object rowObj : result.getData()) {
125+
if (!(rowObj instanceof Map)) continue;
126+
Map<String, Object> row = (Map<String, Object>) rowObj;
127+
128+
String ip = null;
129+
Double metricValue = null;
130+
131+
// Buscar IP y métrica en la fila
132+
for (Map.Entry<String, Object> entry : row.entrySet()) {
133+
Object val = entry.getValue();
134+
if (val == null) continue;
135+
136+
String strVal = val.toString();
137+
if (ip == null && isValidIP(strVal)) {
138+
ip = strVal;
139+
} else if (metricValue == null && val instanceof Number) {
140+
metricValue = ((Number) val).doubleValue();
141+
}
142+
}
113143

144+
if (ip == null || metricValue == null) continue;
145+
146+
// Resolver lat/long con tu servicio de GeoIP
147+
GeoIp ipInfo;
148+
try {
149+
ipInfo = ipInfoService.getIpInfo(ip);
150+
if (ipInfo == null) continue;
151+
} catch (UtmIpInfoException e) {
152+
log.error(e.getMessage());
153+
continue;
154+
}
155+
156+
// Construir resultado
157+
CoordinateMapChartResult chartResult = new CoordinateMapChartResult();
158+
chartResult.setName(ip);
159+
chartResult.setValue(new Double[] {
160+
ipInfo.getLatitude(),
161+
ipInfo.getLongitude(),
162+
metricValue
163+
});
164+
165+
retValue.add(chartResult);
166+
}
167+
168+
return retValue;
169+
} catch (Exception e) {
170+
throw new RuntimeException(ctx + ": " + e.getMessage(), e);
171+
}
172+
}
114173
}

backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/responses/impl/gauge_goal_chart/ResponseParserForGaugeGoalChart.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.park.utmstack.util.chart_builder.elasticsearch_dsl.responses.ResponseParser;
1010
import com.utmstack.opensearch_connector.parsers.TermAggregateParser;
1111
import com.utmstack.opensearch_connector.types.BucketAggregation;
12+
import com.utmstack.opensearch_connector.types.SearchSqlResponse;
1213
import org.opensearch.client.opensearch._types.aggregations.*;
1314
import org.opensearch.client.opensearch.core.SearchResponse;
1415
import org.springframework.util.Assert;
@@ -111,4 +112,48 @@ private List<GaugeGoalChartResult> parseTermAggregation(Map<String, Aggregate> r
111112
}
112113
return rturn;
113114
}
115+
116+
@Override
117+
public List<GaugeGoalChartResult> parse(UtmVisualization visualization, SearchSqlResponse<Map> result) {
118+
final String ctx = CLASSNAME + ".parse(SearchSqlResponse)";
119+
try {
120+
Assert.notNull(visualization, "Param visualization must not be null");
121+
122+
List<GaugeGoalChartResult> results = new ArrayList<>();
123+
124+
// IDs: puedes obtenerlos de la visualización o usar valores por defecto
125+
String metricId = "1";
126+
String bucketId = "1000";
127+
128+
for (Object rowObj : result.getData()) {
129+
if (!(rowObj instanceof Map)) continue;
130+
Map<String, Object> row = (Map<String, Object>) rowObj;
131+
132+
String bucketKey = "UNKNOWN";
133+
Double metricValue = null;
134+
135+
// recorrer dinámicamente las columnas
136+
for (Map.Entry<String, Object> entry : row.entrySet()) {
137+
Object val = entry.getValue();
138+
if (val == null) continue;
139+
140+
if (bucketKey.equals("UNKNOWN") && val instanceof String) {
141+
bucketKey = val.toString();
142+
} else if (metricValue == null && val instanceof Number) {
143+
metricValue = ((Number) val).doubleValue();
144+
}
145+
}
146+
147+
if (metricValue == null) {
148+
metricValue = 0.0;
149+
}
150+
151+
results.add(new GaugeGoalChartResult(metricId, metricValue, bucketKey, bucketId));
152+
}
153+
154+
return results;
155+
} catch (Exception e) {
156+
throw new RuntimeException(ctx + ": " + e.getMessage(), e);
157+
}
158+
}
114159
}

backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/responses/impl/heat_map_chart/ResponseParserForHeatMapChart.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,10 @@
1010
import com.utmstack.opensearch_connector.parsers.DateHistogramAggregateParser;
1111
import com.utmstack.opensearch_connector.parsers.TermAggregateParser;
1212
import com.utmstack.opensearch_connector.types.BucketAggregation;
13+
import com.utmstack.opensearch_connector.types.SearchSqlResponse;
1314
import org.opensearch.client.opensearch._types.aggregations.Aggregate;
1415
import org.opensearch.client.opensearch.core.SearchResponse;
16+
import org.springframework.util.Assert;
1517
import org.springframework.util.CollectionUtils;
1618
import org.springframework.util.StringUtils;
1719

@@ -150,4 +152,61 @@ private void parseBuckets(Bucket bucket, Map<String, Aggregate> aggregation, Met
150152
}
151153
}
152154
}
155+
156+
@Override
157+
public List<HeatMapChartResult> parse(UtmVisualization visualization, SearchSqlResponse<Map> result) {
158+
final String ctx = CLASSNAME + ".parse(SearchSqlResponse)";
159+
try {
160+
Assert.notNull(visualization, "Param visualization must not be null");
161+
162+
HeatMapChartResult retValue = new HeatMapChartResult();
163+
164+
// Definir ejes
165+
retValue.addYAxis("Count"); // métrica COUNT
166+
// XAxis se llenará dinámicamente con las claves encontradas
167+
168+
int index = 0;
169+
for (Object rowObj : result.getData()) {
170+
if (!(rowObj instanceof Map)) continue;
171+
Map<String, Object> row = (Map<String, Object>) rowObj;
172+
173+
// Buscar bucketKey (ej. IP)
174+
String bucketKey = null;
175+
Double metricValue = null;
176+
177+
for (Map.Entry<String, Object> entry : row.entrySet()) {
178+
Object val = entry.getValue();
179+
if (val == null) continue;
180+
181+
// Si es IP o string, lo usamos como bucketKey
182+
if (bucketKey == null && val instanceof String) {
183+
bucketKey = entry.getKey() + ":=" + val.toString();
184+
}
185+
// Si es número, lo usamos como métrica
186+
if (metricValue == null && val instanceof Number) {
187+
metricValue = ((Number) val).doubleValue();
188+
}
189+
}
190+
191+
if (bucketKey == null || metricValue == null) continue;
192+
193+
// Añadir XAxis
194+
retValue.addXAxis(bucketKey);
195+
196+
// Construir punto [x, y, value]
197+
Double[] data = new Double[3];
198+
data[0] = (double) index; // posición en XAxis
199+
data[1] = 0.0; // único eje Y (Count)
200+
data[2] = metricValue;
201+
202+
retValue.addData(data);
203+
index++;
204+
}
205+
206+
return Collections.singletonList(retValue);
207+
} catch (Exception e) {
208+
throw new RuntimeException(ctx + ": " + e.getMessage(), e);
209+
}
210+
}
211+
153212
}

backend/src/main/java/com/park/utmstack/util/chart_builder/elasticsearch_dsl/responses/impl/metric_chart/ResponseParserForMetricChart.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.park.utmstack.util.chart_builder.elasticsearch_dsl.responses.ResponseParser;
1010
import com.utmstack.opensearch_connector.parsers.TermAggregateParser;
1111
import com.utmstack.opensearch_connector.types.BucketAggregation;
12+
import com.utmstack.opensearch_connector.types.SearchSqlResponse;
1213
import org.opensearch.client.opensearch._types.aggregations.*;
1314
import org.opensearch.client.opensearch.core.SearchResponse;
1415
import org.springframework.util.Assert;
@@ -112,4 +113,48 @@ private List<MetricChartResult> parseTermAggregation(Map<String, Aggregate> resu
112113
throw new RuntimeException(ctx + ": " + e.getLocalizedMessage());
113114
}
114115
}
116+
117+
@Override
118+
public List<MetricChartResult> parse(UtmVisualization visualization, SearchSqlResponse<Map> result) {
119+
final String ctx = CLASSNAME + ".parse(SearchSqlResponse)";
120+
try {
121+
Assert.notNull(visualization, "Param visualization must not be null");
122+
123+
List<MetricChartResult> results = new ArrayList<>();
124+
125+
// IDs: puedes obtenerlos de la visualización o usar valores por defecto
126+
String metricId = "1";
127+
String bucketId = "1000";
128+
129+
for (Object rowObj : result.getData()) {
130+
if (!(rowObj instanceof Map)) continue;
131+
Map<String, Object> row = (Map<String, Object>) rowObj;
132+
133+
String bucketKey = "UNKNOWN";
134+
Double metricValue = null;
135+
136+
// recorrer dinámicamente las columnas
137+
for (Map.Entry<String, Object> entry : row.entrySet()) {
138+
Object val = entry.getValue();
139+
if (val == null) continue;
140+
141+
if (bucketKey.equals("UNKNOWN") && val instanceof String) {
142+
bucketKey = val.toString();
143+
} else if (metricValue == null && val instanceof Number) {
144+
metricValue = ((Number) val).doubleValue();
145+
}
146+
}
147+
148+
if (metricValue == null) {
149+
metricValue = 0.0;
150+
}
151+
152+
results.add(new MetricChartResult(metricId, metricValue, bucketKey, bucketId));
153+
}
154+
155+
return results;
156+
} catch (Exception e) {
157+
throw new RuntimeException(ctx + ": " + e.getMessage(), e);
158+
}
159+
}
115160
}

0 commit comments

Comments
 (0)