Skip to content

Commit 19286ce

Browse files
committed
feat: add index pattern selection and dynamic SQL query handling
Signed-off-by: Manuel Abascal <mjabascal10@gmail.com>
1 parent 35a5f39 commit 19286ce

File tree

4 files changed

+79
-12
lines changed

4 files changed

+79
-12
lines changed

frontend/src/app/graphic-builder/chart-builder/chart-builder.component.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ <h5 class="mb-0 label-header px-3 py-2">
9797
[(ngModel)]="sqlQuery"
9898
(ngModelChange)="clearMessages()"
9999
[customKeywords]="loadFieldNames().concat(indexPatternNames)"
100+
(indexPatternChange)="onIndexPatternChange($event)"
100101
[consoleOptions]="codeEditorOptions">
101102
</app-code-editor>
102103
</div>

frontend/src/app/graphic-builder/chart-builder/chart-builder.component.ts

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ import {VisualizationService} from '../visualization/shared/services/visualizati
4040
import {VisualizationSaveComponent} from '../visualization/visualization-save/visualization-save.component';
4141

4242
import {DashboardStatusEnum} from '../dashboard-builder/shared/enums/dashboard-status.enum';
43+
import {HttpResponse} from "@angular/common/http";
44+
import {IndexPatternService} from "../../shared/services/elasticsearch/index-pattern.service";
45+
import {map} from "rxjs/operators";
4346

4447

4548
@Component({
@@ -75,6 +78,7 @@ export class ChartBuilderComponent implements OnInit, AfterViewChecked {
7578
indexPatternNames: string[] = [];
7679
codeEditorOptions: ConsoleOptions = {lineNumbers: 'off'};
7780
loading = true;
81+
indexPattern: UtmIndexPattern[];
7882

7983
constructor(private spinner: NgxSpinnerService,
8084
private route: ActivatedRoute,
@@ -86,7 +90,8 @@ export class ChartBuilderComponent implements OnInit, AfterViewChecked {
8690
private location: Location,
8791
private router: Router,
8892
private localFieldService: LocalFieldService,
89-
private sqlValidationService: SqlValidationService) {
93+
private sqlValidationService: SqlValidationService,
94+
private indexPatternService: IndexPatternService) {
9095
route.queryParams.subscribe(params => {
9196
this.chart = params[VisualizationQueryParamsEnum.CHART];
9297
this.mode = params[VisualizationQueryParamsEnum.MODE];
@@ -138,6 +143,8 @@ export class ChartBuilderComponent implements OnInit, AfterViewChecked {
138143
};
139144
this.loading = false;
140145
}
146+
147+
this.getIndexPatterns();
141148
}
142149

143150
getFields() {
@@ -343,13 +350,37 @@ export class ChartBuilderComponent implements OnInit, AfterViewChecked {
343350

344351
nullifyUnusedFields() {
345352
this.visualization.aggregationType = null;
346-
this.visualization.pattern = null;
347-
this.visualization.idPattern = null;
348-
// this.visualization.filterType = null;
349353
}
350354

351355
clearMessages(): void {
352356
this.errorMessage = '';
353357
}
354358

359+
getIndexPatterns() {
360+
const req = {
361+
page: 0,
362+
size: 1000,
363+
sort: 'id,asc',
364+
'isActive.equals': true,
365+
};
366+
this.indexPatternService.query(req)
367+
.pipe(
368+
map((res: HttpResponse<any>) => res.body || [])
369+
)
370+
.subscribe({
371+
next: data => {
372+
this.indexPatternNames = data.map((pattern: UtmIndexPattern) => pattern.pattern);
373+
this.indexPattern = data;
374+
},
375+
error: () => this.indexPatternNames = [],
376+
});
377+
}
378+
379+
onIndexPatternChange($event: string) {
380+
const indexPattern = this.indexPattern.find(p => p.pattern === $event);
381+
382+
if (indexPattern) {
383+
this.indexPatternSelected(indexPattern);
384+
}
385+
}
355386
}

frontend/src/app/shared/chart/types/visualization.type.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1+
import {ChartBuilderQueryLanguageEnum} from "../../enums/chart-builder-query-language.enum";
12
import {ChartTypeEnum} from '../../enums/chart-type.enum';
23
import {DataNatureTypeEnum} from '../../enums/nature-data.enum';
34
import {ElasticFilterType} from '../../types/filter/elastic-filter.type';
45
import {UtmIndexPattern} from '../../types/index-pattern/utm-index-pattern';
56
import {MetricDataType} from './metric/metric-data.type';
6-
import {ChartBuilderQueryLanguageEnum} from "../../enums/chart-builder-query-language.enum";
77

88
export class VisualizationType {
99
chartConfig?: any;

frontend/src/app/shared/components/code-editor/code-editor.component.ts

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export class CodeEditorComponent implements OnInit, OnDestroy, ControlValueAcces
5454
@Input() consoleOptions?: ConsoleOptions;
5555
@Output() execute = new EventEmitter<string>();
5656
@Output() clearData = new EventEmitter<void>();
57+
@Output() indexPatternChange = new EventEmitter<string>();
5758
@Input() queryError: string | null = null;
5859
@Input() customKeywords: string[] = [];
5960

@@ -88,13 +89,6 @@ export class CodeEditorComponent implements OnInit, OnDestroy, ControlValueAcces
8889
this.consoleOptions = { ...this.defaultOptions, ...this.consoleOptions };
8990
}
9091

91-
ngOnDestroy(): void {
92-
if (this.completionProvider) {
93-
this.completionProvider.dispose();
94-
this.completionProvider = undefined;
95-
}
96-
}
97-
9892
onEditorInit(editorInstance: monaco.editor.IStandaloneCodeEditor) {
9993
this.completionProvider = monaco.languages.registerCompletionItemProvider('sql', {
10094
provideCompletionItems: () => {
@@ -165,6 +159,7 @@ export class CodeEditorComponent implements OnInit, OnDestroy, ControlValueAcces
165159
}
166160

167161
clearMessages(): void {
162+
console.log('Query:', this.extractIndexPattern(this.sqlQuery));
168163
this.errorMessage = '';
169164
this.successMessage = '';
170165
}
@@ -184,4 +179,44 @@ export class CodeEditorComponent implements OnInit, OnDestroy, ControlValueAcces
184179
setDisabledState?(isDisabled: boolean): void {
185180
// Optional: handle disabled state
186181
}
182+
183+
extractIndexPattern(sql: string): string | null {
184+
const normalized = sql
185+
.replace(/\s+/g, ' ')
186+
.toLowerCase();
187+
188+
const fromIndex = normalized.indexOf(' from ');
189+
if (fromIndex === -1) { return null; }
190+
191+
const start = fromIndex + 6;
192+
193+
const keywords = [' where ', ' group by ', ' order by ', ' limit ', ' having '];
194+
195+
let end = normalized.length;
196+
for (const kw of keywords) {
197+
const idx = normalized.indexOf(kw, start);
198+
if (idx !== -1 && idx < end) {
199+
end = idx;
200+
}
201+
}
202+
203+
const originalFragment = sql.substring(start, end).trim();
204+
205+
if (originalFragment.length > 0) {
206+
const indexPatternSelected = this.customKeywords.find(keyword => keyword === originalFragment);
207+
208+
if (indexPatternSelected) {
209+
console.log(indexPatternSelected);
210+
this.indexPatternChange.emit(indexPatternSelected);
211+
}
212+
}
213+
}
214+
215+
ngOnDestroy(): void {
216+
if (this.completionProvider) {
217+
this.completionProvider.dispose();
218+
this.completionProvider = undefined;
219+
}
220+
}
221+
187222
}

0 commit comments

Comments
 (0)