This is an automated email from the ASF dual-hosted git repository.
dominikriemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git
The following commit(s) were added to refs/heads/dev by this push:
new 5753e7a417 feat: Support filtering of datasets in chart view (#4556)
5753e7a417 is described below
commit 5753e7a417ba0072acb58eb4f04af31f0afaa024
Author: Dominik Riemer <[email protected]>
AuthorDate: Sat Jun 13 14:30:01 2026 +0200
feat: Support filtering of datasets in chart view (#4556)
---
ui/cypress/support/utils/chart/ChartUtils.ts | 13 +-
.../userManagement/testUserRoleDataset.spec.ts | 3 +-
ui/deployment/i18n/de.json | 2 +
ui/deployment/i18n/en.json | 2 +
ui/deployment/i18n/pl.json | 2 +
.../chart-data-settings.component.html | 56 +++++--
.../data-settings/chart-data-settings.component.ts | 164 +++++++++++++++++----
7 files changed, 195 insertions(+), 47 deletions(-)
diff --git a/ui/cypress/support/utils/chart/ChartUtils.ts
b/ui/cypress/support/utils/chart/ChartUtils.ts
index 0f7788377a..12a38e78f1 100644
--- a/ui/cypress/support/utils/chart/ChartUtils.ts
+++ b/ui/cypress/support/utils/chart/ChartUtils.ts
@@ -493,19 +493,16 @@ export class ChartUtils {
$body.find('[data-cy="data-explorer-select-data-set"]').length
) {
cy.dataCy('data-explorer-select-data-set')
- .click()
- .get('mat-option')
- .contains(dataSet)
- .click();
+ .clear()
+ .type(dataSet);
+ cy.get('mat-option').contains(dataSet).click();
}
});
}
public static assertSelectDataSet(dataSet: string) {
- cy.dataCy('data-explorer-select-data-set')
- .click()
- .get('mat-option')
- .should('contain.text', dataSet);
+ cy.dataCy('data-explorer-select-data-set').clear().type(dataSet);
+ cy.get('mat-option').should('contain.text', dataSet);
}
/**
diff --git a/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts
b/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts
index ab6217fa6f..25819aa64f 100644
--- a/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts
+++ b/ui/cypress/tests/userManagement/testUserRoleDataset.spec.ts
@@ -172,8 +172,7 @@ describe('Test Dataset Permissions', () => {
cy.get('sp-alert-banner').should('be.visible');
ChartBtns.discardDataExplorerWidgetBtn().click();
} else {
- cy.dataCy('data-explorer-select-data-set').click();
- cy.get('mat-option').contains(datasetName).click();
+ ChartUtils.selectDataSet(datasetName);
ChartBtns.discardDataExplorerWidgetBtn().click();
ChartUtils.addDataViewAndTableWidget(datasetName, true);
ChartUtils.saveDataViewConfiguration(false, false, 'test');
diff --git a/ui/deployment/i18n/de.json b/ui/deployment/i18n/de.json
index 27fccbbc08..2eea78a5da 100644
--- a/ui/deployment/i18n/de.json
+++ b/ui/deployment/i18n/de.json
@@ -705,6 +705,7 @@
"No charts match your search.": "Keine Diagramme entsprechen deiner Suche.",
"No data available.": "Keine Daten verfügbar",
"No datasets available": "Keine Datensätze verfügbar",
+ "No datasets match your search.": "Keine Datensätze entsprechen deiner
Suche.",
"No entries available.": "Keine Einträge vorhanden.",
"No export providers found": "Keine Exportanbieter gefunden",
"No fields selected.": "Keine Felder ausgewählt.",
@@ -898,6 +899,7 @@
"Schema fields": "Schemafelder",
"Script": "Skript",
"Search": "Suche",
+ "Search datasets": "Datensätze suchen",
"Search charts": "Diagramme suchen",
"Searching for available extensions, please wait...": "Nach verfügbaren
Extensions suchen, bitte warten...",
"Second": "Sekunde",
diff --git a/ui/deployment/i18n/en.json b/ui/deployment/i18n/en.json
index 86d7ca4e3f..0c4193aaa4 100644
--- a/ui/deployment/i18n/en.json
+++ b/ui/deployment/i18n/en.json
@@ -705,6 +705,7 @@
"No charts match your search.": null,
"No data available.": null,
"No datasets available": null,
+ "No datasets match your search.": null,
"No entries available.": null,
"No export providers found": null,
"No fields selected.": null,
@@ -898,6 +899,7 @@
"Schema fields": null,
"Script": null,
"Search": null,
+ "Search datasets": null,
"Search charts": null,
"Searching for available extensions, please wait...": null,
"Second": null,
diff --git a/ui/deployment/i18n/pl.json b/ui/deployment/i18n/pl.json
index d17c3bcc58..a53abc89a1 100644
--- a/ui/deployment/i18n/pl.json
+++ b/ui/deployment/i18n/pl.json
@@ -705,6 +705,7 @@
"No charts match your search.": "Żadne wykresy nie pasują do wyszukiwania.",
"No data available.": "Brak dostępnych danych.",
"No datasets available": "Brak dostępnych zbiorów danych",
+ "No datasets match your search.": "Żadne zbiory danych nie pasują do
wyszukiwania.",
"No entries available.": "Brak dostępnych wpisów.",
"No export providers found": "Nie znaleziono exportera",
"No fields selected.": "Nie wybrano żadnych pól.",
@@ -898,6 +899,7 @@
"Schema fields": "Pola schematu",
"Script": "Skrypt",
"Search": "Szukaj",
+ "Search datasets": "Szukaj zbiorów danych",
"Search charts": "Szukaj wykresów",
"Searching for available extensions, please wait...": "Wyszukiwanie
dostępnych rozszerzeń, proszę czekać...",
"Second": "Sekunda",
diff --git
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
index b4a7dcd331..dc988a4145 100644
---
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
+++
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.html
@@ -85,24 +85,51 @@
}
@if (availableMeasurements.length !== 0) {
<mat-form-field
- color="accent"
fxFlex="100"
appearance="outline"
subscriptSizing="dynamic"
>
- <mat-select
+ <mat-icon matPrefix>search</mat-icon>
+ <input
+ matInput
+ [placeholder]="
+ 'Search datasets' | translate
+ "
+ [value]="measurementInputValue"
+ (input)="
+ onMeasurementSearchChange(
+ $any($event.target).value
+ )
+ "
+ [matAutocomplete]="datasetAutocomplete"
data-cy="data-explorer-select-data-set"
- [(value)]="sourceConfig.measureName"
- (selectionChange)="
- updateMeasure(
+ />
+ @if (hasActiveMeasurementSearch()) {
+ <button
+ mat-icon-button
+ matSuffix
+ type="button"
+ [attr.aria-label]="
+ 'Clear search' | translate
+ "
+ (click)="clearMeasurementSearch()"
+ >
+ <mat-icon>close</mat-icon>
+ </button>
+ }
+ <mat-autocomplete
+ #datasetAutocomplete="matAutocomplete"
+ autoActiveFirstOption
+ (optionSelected)="
+ onMeasurementSelected(
sourceConfig,
- $event.value
+ $event
)
"
>
@for (
- measurement of availableMeasurements;
- track measurement
+ measurement of filteredMeasurements;
+ track measurement.elementId
) {
<mat-option
[value]="measurement.measureName"
@@ -112,7 +139,18 @@
}}</span>
</mat-option>
}
- </mat-select>
+ @if (
+ filteredMeasurements.length === 0 &&
+ hasActiveMeasurementSearch()
+ ) {
+ <mat-option disabled>
+ {{
+ 'No datasets match your
search.'
+ | translate
+ }}
+ </mat-option>
+ }
+ </mat-autocomplete>
</mat-form-field>
}
</sp-split-section>
diff --git
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
index 478d92561b..71477ca0ac 100644
---
a/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
+++
b/ui/src/app/chart/components/chart-view/designer-panel/data-settings/chart-data-settings.component.ts
@@ -29,6 +29,7 @@ import {
DataExplorerDataConfig,
DataExplorerWidgetModel,
DataLakeMeasure,
+ DatasetSummaryDto,
DatalakeRestService,
SourceConfig,
} from '@streampipes/platform-services';
@@ -59,8 +60,12 @@ import {
SpAlertBannerComponent,
SplitSectionComponent,
} from '@streampipes/shared-ui';
-import { MatFormField } from '@angular/material/form-field';
-import { MatOption, MatSelect } from '@angular/material/select';
+import {
+ MatFormField,
+ MatPrefix,
+ MatSuffix,
+} from '@angular/material/form-field';
+import { MatOption } from '@angular/material/core';
import { MatIcon } from '@angular/material/icon';
import { MatRadioButton, MatRadioGroup } from '@angular/material/radio';
import { FormsModule } from '@angular/forms';
@@ -72,6 +77,11 @@ import { AggregateConfigurationComponent } from
'./aggregate-configuration/aggre
import { FilterSelectionPanelComponent } from
'./filter-selection-panel/filter-selection-panel.component';
import { OrderSelectionPanelComponent } from
'./order-selection-panel/order-selection-panel.component';
import { TranslatePipe } from '@ngx-translate/core';
+import {
+ MatAutocomplete,
+ MatAutocompleteSelectedEvent,
+ MatAutocompleteTrigger,
+} from '@angular/material/autocomplete';
@Component({
selector: 'sp-chart-data-settings',
@@ -91,7 +101,8 @@ import { TranslatePipe } from '@ngx-translate/core';
LayoutGapDirective,
MatButton,
MatFormField,
- MatSelect,
+ MatPrefix,
+ MatSuffix,
MatOption,
MatIcon,
MatRadioGroup,
@@ -102,6 +113,8 @@ import { TranslatePipe } from '@ngx-translate/core';
FormFieldComponent,
MatInput,
MatCheckbox,
+ MatAutocomplete,
+ MatAutocompleteTrigger,
AggregateConfigurationComponent,
FieldSelectionPanelComponent,
FilterSelectionPanelComponent,
@@ -137,7 +150,9 @@ export class ChartDataSettingsComponent implements OnInit {
@ViewChild('groupSelectionPanel')
groupSelectionPanel: GroupSelectionPanelComponent;
- availableMeasurements: DataLakeMeasure[] = [];
+ availableMeasurements: DatasetSummaryDto[] = [];
+ filteredMeasurements: DatasetSummaryDto[] = [];
+ measurementInputValue = '';
step = 0;
@@ -149,33 +164,42 @@ export class ChartDataSettingsComponent implements OnInit
{
}
ngOnInit(): void {
+ this.syncCurrentMeasure();
this.loadPipelinesAndMeasurements();
}
loadPipelinesAndMeasurements() {
- this.datalakeRestService
- .getAllMeasurementSeries()
- .subscribe(response => {
- this.availableMeasurements = response;
- this.availableMeasurements.sort((a, b) =>
- a.measureName.localeCompare(b.measureName),
- );
+ this.datalakeRestService.getMeasurementSummary().subscribe(response =>
{
+ this.availableMeasurements = response.resources.sort((a, b) =>
+ a.measureName.localeCompare(b.measureName),
+ );
+ this.applyMeasurementSearch();
- if (!this.sourceConfig) {
- const defaultConfigs = this.findDefaultConfig();
- this.initializeSourceConfig(defaultConfigs.measureName);
- if (defaultConfigs.measureName !== undefined) {
- this.updateMeasure(
- this.sourceConfig,
- defaultConfigs.measureName,
- );
- }
+ if (!this.sourceConfig) {
+ const defaultConfigs = this.findDefaultConfig();
+ this.initializeSourceConfig(defaultConfigs.measureName);
+ if (defaultConfigs.measureName !== undefined) {
+ this.loadMeasurement(
+ defaultConfigs.measureName,
+ true,
+ true,
+ );
}
- });
+ } else if (
+ !this.sourceConfig.measure &&
+ this.sourceConfig.measureName
+ ) {
+ this.loadMeasurement(
+ this.sourceConfig.measureName,
+ false,
+ false,
+ );
+ }
+ });
}
findDefaultConfig(): {
- measureName: string;
+ measureName: string | undefined;
} {
if (this.availableMeasurements.length > 0) {
return {
@@ -187,7 +211,84 @@ export class ChartDataSettingsComponent implements OnInit {
}
updateMeasure(sourceConfig: SourceConfig, measureName: string) {
- sourceConfig.measure = this.findMeasure(measureName);
+ sourceConfig.measureName = measureName;
+ this.measurementInputValue = measureName;
+ this.loadMeasurement(measureName, true, true);
+ }
+
+ onMeasurementSearchChange(value: string): void {
+ this.measurementInputValue = value;
+ this.applyMeasurementSearch();
+ }
+
+ clearMeasurementSearch(): void {
+ this.measurementInputValue = '';
+ this.applyMeasurementSearch();
+ }
+
+ hasActiveMeasurementSearch(): boolean {
+ return this.measurementInputValue.trim().length > 0;
+ }
+
+ onMeasurementSelected(
+ sourceConfig: SourceConfig,
+ event: MatAutocompleteSelectedEvent,
+ ): void {
+ this.updateMeasure(sourceConfig, event.option.value);
+ }
+
+ private applyMeasurementSearch(): void {
+ const query = this.measurementInputValue.trim().toLowerCase();
+ if (!query) {
+ this.filteredMeasurements = this.availableMeasurements;
+ return;
+ }
+
+ this.filteredMeasurements = this.availableMeasurements.filter(
+ measurement =>
+ measurement.measureName.toLowerCase().includes(query) ||
+ measurement.pipelines.some(pipeline =>
+ pipeline.toLowerCase().includes(query),
+ ),
+ );
+ }
+
+ private loadMeasurement(
+ measureName: string,
+ resetQueryConfig: boolean,
+ refreshData: boolean,
+ ): void {
+ this.datalakeRestService
+ .getMeasurementByName(measureName)
+ .subscribe(measure =>
+ this.applySelectedMeasurement(
+ measure,
+ resetQueryConfig,
+ refreshData,
+ ),
+ );
+ }
+
+ private applySelectedMeasurement(
+ measure: DataLakeMeasure,
+ resetQueryConfig: boolean,
+ refreshData: boolean,
+ ): void {
+ const sourceConfig = this.sourceConfig;
+ if (!sourceConfig) {
+ return;
+ }
+
+ this.dataLakeMeasure = measure;
+ this.dataLakeMeasureChange.emit(measure);
+ sourceConfig.measureName = measure.measureName;
+ sourceConfig.measure = measure;
+ this.measurementInputValue = measure.measureName;
+
+ if (!resetQueryConfig) {
+ return;
+ }
+
sourceConfig.queryConfig.fields = [];
if (this.fieldSelectionPanel) {
this.fieldSelectionPanel.applyDefaultFields();
@@ -197,13 +298,20 @@ export class ChartDataSettingsComponent implements OnInit
{
if (this.groupSelectionPanel) {
this.groupSelectionPanel.applyDefaultFields();
}
- this.triggerDataRefresh();
+
+ if (refreshData) {
+ this.triggerDataRefresh();
+ }
}
- findMeasure(measureName: string) {
- return this.availableMeasurements.find(
- m => m.measureName === measureName,
- );
+ private syncCurrentMeasure(): void {
+ if (this.sourceConfig?.measure) {
+ this.dataLakeMeasure = this.sourceConfig.measure;
+ this.dataLakeMeasureChange.emit(this.sourceConfig.measure);
+ this.measurementInputValue = this.sourceConfig.measure.measureName;
+ } else if (this.sourceConfig?.measureName) {
+ this.measurementInputValue = this.sourceConfig.measureName;
+ }
}
changeDataAggregation() {